数组与指针
- 指针与地址
- 指针与函数参数
- 指针与数组
- 地址算数运算
- 字符指针与函数
- 指针数组以及指向指针的指针
- 多维数组
- 命令行参数
- 指向函数的指针
- 复杂声明
指针是一种保存变量地址的变量。C语言中,指针的使用非常广泛,原因之一是,指针常常是表达某个计算的惟一途径,另一个原因是,同其它方法比较起来,使用指针通常可以生成更高效、更紧凑的代码。
指针与地址
- 指针是能够存放一个地址的一组存储单元(通常是两个或4个字节)。如图c的类型是char,并且p是指向c的指针。
- 取地址符&
地址运算符&只能应用于内存中的对象,即变量与数组元素。它不能作用于表达式、常量或register类型的变量。
p = &c; //将把c的地址赋值给变量p,我们称p为“指向”c的指针
- 间接寻址或间接引用运算符*
当它作用于指针时,将访问指针所指向的对象。
int x = 1, y = 2, z[10];
int *ip; //ip是指向int类型的指针
ip = &x; //ip现在指向x
y = *ip; //y值现在为1
*ip = 0; //x值现在为0
ip = &z[0]; //ip现在指向z[0]
(*ip)++,中的圆括号是必需的,否则,该表达式将对ip进行加1 运算,而不是对ip指向的对象进行加1运算。
指针也是变量,所以在程序中可以直接使用,而不必通过间接引用的方法使用。
指针与函数参数
指针参数使得被调用函数能够访问和修改主调函数中对象的值。(针对变量名称,其为指针还是类型?)
void swap(int *px, int *py)
{
int temp;
temp = *px;
*px = *py;
*py = temp;
}
指针与数组
在 C 语言中,指针和数组之间的关系十分密切,因此,在接下来的部分中,我们将同时讨论指针与数组。通过数组下标所能完成的任何操作都可以通过指针来实现。一般来说,用指针编写的程序比用数组下标编写的程序执行速度快,但另一方面,用指针实现的程序理解起来稍微困难一些。
数组声明:
int a[10];
存储形式:
int *pa;
pa = &a[0]; //pa指向数组a的第0个元素,pa的值为a[0]的地址
数组名代表该数组最开始的一个元素的地址。
pa = &a[0];
pa = a;
a[i]等同于*(a+i),在计算数组元素a[i]的值时,C 语言实际上先将其转换为*(a+i)的形式,然后再进行求值。简而言之,一个通过数组和下标实现的表达式可等价地通过指针和偏移量实现。
数组名和指针之间有一个不同之处,指针是一个变量,因此,在C语言中,语句pa=a和pa++都是合法的。但数组名不是变量,因此,类似于a=pa和a++形式的语句是非法的。但是在函数参数中,相当于将数组的第一个地址赋予了函数的形参,并非是数组名的自增,所以是可以传递数组名的。
- 函数定义中,形式参数char a[]和char *s是等价的。
地址算数运算
- c语言中的地址算术运算方法是一致且有规律的,将指针、数组和地址的算术运算集成在
一起是该语言的一大优点。 - 指针与整数之间不能相互转换,但0是惟一的例外:常量0可以赋值给指针,指针也可以和常量0进行比较。程序中经常用符号常量NULL代替常量0,这样便于更清晰地说明常量0是指针的一个特殊值。。符号常量NULL定义在标准头文件<stddef.h>中。
- 指针p和q指向同一个数组的成员,那么它们之间就可以进行类似于==、!=、<、>==的关系比较运算。
- 地址计算时是以字节为基本单元来运算的,比如char数组计算时,下一个字符提取可以直接地址加1.但是如果是int型,则需要地址+4才能转换到下一个int整数。前提是定义该指针为char型。
- 指针的算术运算具有一致性:如果处理的数据类型是比字符型占据更多存储空间的浮点类型,并且p是一个指向浮点类型的指针,那么在执行p++后,p将指向下一个浮点数的地址。
有效的指针运算包括相同类型指针之间的赋值运算;指针同整数之间的加法或减法运算;
指向相同数组中元素的两个指针间的减法或比较运算;将指针赋值为0或指针与0之间的比较运算
字符指针与函数
字符串常量是一个字符数组。
”I am a string“
字符串常量占据的存储单元数也因此比双引号内的字符数大1。
- 字符串常量使用
printf("Hello, world\n");
char *pmessage;
pmessage = "now is the time"; //都是指针使用
- 字符数组与字符指针
char amessage[] = "nw is the time"; /* 定义一个数组*/
char *pmessage = "now is the time"; /* 定义一个指针*/
- 复制字符串程序
void strcpy(char *s, char *t) //将t拷贝到s中
{
while (*s++ = *t++)
;
}
- 进栈和出栈操作
*p++ = val; //将val压入栈
val = *--p; //将栈顶元素弹出到val中
指针数组以及指向指针的指针
由于指针本身也是变量,所以它们也可以像其它变量一样存储在数组中。
char *lineptr[MAXLINES];
- 初始化
指针数组的初始化语法和前面所讲的其它类型对象的初始化语法类似。
static char *name[] = {
"Illegal month",
"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December"
};
多维数组
C语言提供了类似于矩阵的多维数组,但实际上它们并不像指针数组使用得那样广泛。
static char daytab[2][13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
- 区别
f(int (*daytab)[13]); //二维数组,表明参数是一个指针,它指向具有13个整型元素的一维数组
int *daytab[13]; //一维指针数组
- 指针与多维数组
int a[10][20];
int *b[10];
指针数组的一个重要优点在于,数组的每一行长度可以不同。而多维数组可以理解为每行固定长度的指针。
命令行参数
在支持C 语言的环境中,可以在程序开始执行时将命令行参数传递给程序。调用主函数main 时,它带有两个参数。第一个参数(习惯上称为argc,用于参数计数)的值表示运行程序时命令行中参数的数目;第二个参数(称为argv,用于参数向量)是一个指向字符串数组的指针,其中每个字符串对应一个参数。我们通常用多级指针处理这些字符串。
C语言程序有一个公共的约定:以负号开头的参数表示一个可选标志或参数。
在系统中调用运行的程序:
find -x -n
find -xn // 组合使用
指向函数的指针
在 C 语言中,函数本身不是变量,但可以定义指向函数的指针。
int (*comp)(void *, void *); //函数指针
int *comp(void *, void *); //返回指针的函数
复杂声明
int *f(); //f是一个函数,它返回一个指向int类型的指针
int (*pf)(); //pf:是一个指向函数的指针,该函数返回一个int类型的对象
如何创建复杂声明?
- 使用typedef通过简单的步骤合成;
- 采用函数将正确的C语言声明转换为文字描述。