1、指针与地址
一元运算符&用于取一个对象的地址,因而语句
p = &c;
用于将c的地址赋给变量p,并且说p是指向c的指针。
一元运算符* 是间接寻址或间接引用运算符,当它应用于指针时,它将访问指针所指向的对象。
由于指针也是变量,所以在程序中不必通过间接引用的方法就可以直接使用它们。
例如,如果i q是另一个指向整数的指针,那么语句
iq = ip
就将i p的值赋给i q,因此该语句使得指针i q指向i p所指向的对象。
2、指针与函数变元
由于C语言以按值调用的方式将变元传递给函数,因而被调用函数不能直接更改调用函数中
变量的值。
定义void swap(int * px, int * py);
调用swap(&a, &b);
3、指针与数组
int *pa;
赋值语句
pa = &a[0]
用于将指针pa指向数组a的第0个元素,也即pa的值为数组元素a [ 0 ]的地址
则赋值语句
x = *pa;
把数组元素a[0]的内容赋给变量x。
下标和指针运算有着很密切的对应关系。按照定义,一个类型为数组的变量或表达式的值是该数组第0个元素的地址。因此在赋值语句
pa = &a[0];
执行后, pa和a具有相同的值。由于一个数组的名字即该数组第0个元素的位置,所以赋值语句
pa = &a[0]也可以写成如下形式:
pa = a;
一个用数组和下标实现的表达式可等价地用指针和和偏移量来实现。然而,必须注意到,数组名字和指针之间仍然存在着一点区别。指针是变量,因而在C语言中,语句pa = a和p a + +都是合法的。但数组名字不是变量,因而诸如a = pa和a + +这样的语句是非法的。
当把一个数组名字传递给一个函数时,实际上传递的是该数组第一个元素的位置。由于一个数组名字参数就是一个指针,所以在被调用函数中,与数组名字参数对应的变元是一个包含地址值的局部变量。
在函数定义中,将
char s[ ]; 和 char *s;
作为函数的形式参数所表示的含义是等价的,我们更喜欢使用后一种形式,因为它比前者更明白地表示了该参数是一个指针。当数组名字被传递给函数时,函数就根据操作的方便来判定传递给它的是数组还是指针,然后按相应的方式操纵该参数。为了清楚而恰当地描述函数,在函数中甚至可同时使用数组和指针这两种表示法。
也可以通过传递指向子数组的指针的方法把数组的一部分作为参数传递给函数。
4、地址算术运算
指针的算术运算具有一致性:如果处理的是比字符类型占据更多存储空间的浮点类型,并且p是一个指向浮点类型的指针,那么执行p + +后p就指向下一个浮点数的地址。因此只需将a l l o c和a f r e e函数中所有的c h a r类型替换为f l o a t类型,就可以得到一个针对浮点类型而不是字符类型的内存分配函数版本。所有的指针运算都会自动考虑它所指对象的大小。
有效的指针运算包括:相同类型指针之间的赋值运算;指针值加或减一个整数值的运算;指向相同数组中的元素的指针之间的减或比较运算;将指针赋0或指针与0之间的比较运算。所有其他形式的指针运算均非法。诸如下列形式的运算就是非法的指针运算:指针间的加法、乘法、除法、移位或屏蔽运算;指针值加单双精度浮点数的运算;除两者之一是v o i d *类型指针外,不经强制类型转换就将指向一种类型对象的指针赋给指向另一种类型对象的指针的运算。
5、字符指针与函数
C语言没有提供将一个完整的字符串作为一个整体处理的运算符。
如下两个定义的差别很大:
char amessage[ ] = "now is the time";
char *pmessage = "now is the time";
上述说明中,amessage是一个足以存放字符串初值和空字符'/0' 的一维数组。可以更改数组中的单个字符,但amessage是一个不可改变的常量,它总指向同一存储区。另一方面, pmessage是一个指针,其初值指向一个字符串常量,之后它可以被修改指向其他地址,但如果试图修改字符串的内容,结果将不确定。
定义函数使用指针参数时可以有两个版本的写法,一是利用数组下标,一时使用指针移动。
头文件< string.h >中包含本节中所提到的函数的说明,其中还包括标准库中的许多其他字符串处理函数的说明。
6、指针数组与指向指针的指针
由于指针本身也是变量,所以它们也可以像其他变量一样存储在数组中。
char *lineptr[MAXLINES]
它表示lineptr是一个具有MAXLINES个元素的一维数组,其中数组的每个元素是一个指向字符型对象的指针。即,lineptr[i]是一个字符指针,而* lineptr[i]是该指针所指向的第i个文本行的首字符。lineptr本身是一个数组名字。
7、多维数组
在C语言中,二维数组实际上是一种特殊的一维数组:其元素也是一维数组。
因此数组下标应写成如下形式:
daytab[i][j]
如果要将二维数组作为变元传递给函数,那么函数的参数说明中应该指明相应数组的列数。数组的行数不必指定,因为正如前所述,函数调用时传递给它的是一个指向由行向量构成的一维数组的指针,其中每个行向量是具有n个整数元素的一维数组。
一般而言,除第一维可以不指定大小外,其余各维都必须明确指定大小。
8、指针数组的初始化
month_name函数中包含一个私有的字符串数组,且当它被调用时返回一个指向正确的字符串位置的指针。
指针数组的初始化语法和早先见过的初始化相似:
char * month_name(int n)
{
static char *name[ ] = {
"Illegal month",
"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December"
};
return ( n < 1 | | n > 12 ) ? name[0] : name[n];
}
9、指针与多维数组
int a[10][20];
int *b[10];
那么a [3][4]与b[3][4]在语法上都是对一个整数的合法引用。但a是一个真正的二维数组:它分配了200个整数大小的存储空间,并且用常规的矩阵下标计算公式20×row+col来计算元素a[row][col]的位置。但对b而言,该定义仅仅分配了10个指针而且没有对它们进行初始化,它们的初始化必须以静态的方式或用代码显式地进行。
指针数组的重要优点在
于数组的每一行可以有不同的长度,即并不是b的每个元素都指向一个具有20个元素的向量,有些元素可以指向具有两个元素的向量,有些可以指向具有50个元素的向量,而有些根本就不指向任何向量。
10、命令行变元
在支持C语言的环境中,可以在程序开始执行时将命令行变元或参数传递给程序。调用主函数时,它可以带有两个变元。第一个变元(习惯上称做argc,用于变元计数)的值为程序执行时命令行中变元的数目,第二个变元(称为argv,用于变元向量)是一个指向字符串数组的指针,其中每个字符串对应一个变元。我们通常用多级指针处理这些字符串。
11、指向函数的指针
在C语言中,函数本身不是变量,但可以定义指向函数的指针,这种指针可以被赋值、存放于数组之中、传递给函数及作为函数返回值等等
一元运算符&用于取一个对象的地址,因而语句
p = &c;
用于将c的地址赋给变量p,并且说p是指向c的指针。
一元运算符* 是间接寻址或间接引用运算符,当它应用于指针时,它将访问指针所指向的对象。
由于指针也是变量,所以在程序中不必通过间接引用的方法就可以直接使用它们。
例如,如果i q是另一个指向整数的指针,那么语句
iq = ip
就将i p的值赋给i q,因此该语句使得指针i q指向i p所指向的对象。
2、指针与函数变元
由于C语言以按值调用的方式将变元传递给函数,因而被调用函数不能直接更改调用函数中
变量的值。
定义void swap(int * px, int * py);
调用swap(&a, &b);
3、指针与数组
int *pa;
赋值语句
pa = &a[0]
用于将指针pa指向数组a的第0个元素,也即pa的值为数组元素a [ 0 ]的地址
则赋值语句
x = *pa;
把数组元素a[0]的内容赋给变量x。
下标和指针运算有着很密切的对应关系。按照定义,一个类型为数组的变量或表达式的值是该数组第0个元素的地址。因此在赋值语句
pa = &a[0];
执行后, pa和a具有相同的值。由于一个数组的名字即该数组第0个元素的位置,所以赋值语句
pa = &a[0]也可以写成如下形式:
pa = a;
一个用数组和下标实现的表达式可等价地用指针和和偏移量来实现。然而,必须注意到,数组名字和指针之间仍然存在着一点区别。指针是变量,因而在C语言中,语句pa = a和p a + +都是合法的。但数组名字不是变量,因而诸如a = pa和a + +这样的语句是非法的。
当把一个数组名字传递给一个函数时,实际上传递的是该数组第一个元素的位置。由于一个数组名字参数就是一个指针,所以在被调用函数中,与数组名字参数对应的变元是一个包含地址值的局部变量。
在函数定义中,将
char s[ ]; 和 char *s;
作为函数的形式参数所表示的含义是等价的,我们更喜欢使用后一种形式,因为它比前者更明白地表示了该参数是一个指针。当数组名字被传递给函数时,函数就根据操作的方便来判定传递给它的是数组还是指针,然后按相应的方式操纵该参数。为了清楚而恰当地描述函数,在函数中甚至可同时使用数组和指针这两种表示法。
也可以通过传递指向子数组的指针的方法把数组的一部分作为参数传递给函数。
4、地址算术运算
指针的算术运算具有一致性:如果处理的是比字符类型占据更多存储空间的浮点类型,并且p是一个指向浮点类型的指针,那么执行p + +后p就指向下一个浮点数的地址。因此只需将a l l o c和a f r e e函数中所有的c h a r类型替换为f l o a t类型,就可以得到一个针对浮点类型而不是字符类型的内存分配函数版本。所有的指针运算都会自动考虑它所指对象的大小。
有效的指针运算包括:相同类型指针之间的赋值运算;指针值加或减一个整数值的运算;指向相同数组中的元素的指针之间的减或比较运算;将指针赋0或指针与0之间的比较运算。所有其他形式的指针运算均非法。诸如下列形式的运算就是非法的指针运算:指针间的加法、乘法、除法、移位或屏蔽运算;指针值加单双精度浮点数的运算;除两者之一是v o i d *类型指针外,不经强制类型转换就将指向一种类型对象的指针赋给指向另一种类型对象的指针的运算。
5、字符指针与函数
C语言没有提供将一个完整的字符串作为一个整体处理的运算符。
如下两个定义的差别很大:
char amessage[ ] = "now is the time";
char *pmessage = "now is the time";
上述说明中,amessage是一个足以存放字符串初值和空字符'/0' 的一维数组。可以更改数组中的单个字符,但amessage是一个不可改变的常量,它总指向同一存储区。另一方面, pmessage是一个指针,其初值指向一个字符串常量,之后它可以被修改指向其他地址,但如果试图修改字符串的内容,结果将不确定。
定义函数使用指针参数时可以有两个版本的写法,一是利用数组下标,一时使用指针移动。
头文件< string.h >中包含本节中所提到的函数的说明,其中还包括标准库中的许多其他字符串处理函数的说明。
6、指针数组与指向指针的指针
由于指针本身也是变量,所以它们也可以像其他变量一样存储在数组中。
char *lineptr[MAXLINES]
它表示lineptr是一个具有MAXLINES个元素的一维数组,其中数组的每个元素是一个指向字符型对象的指针。即,lineptr[i]是一个字符指针,而* lineptr[i]是该指针所指向的第i个文本行的首字符。lineptr本身是一个数组名字。
7、多维数组
在C语言中,二维数组实际上是一种特殊的一维数组:其元素也是一维数组。
因此数组下标应写成如下形式:
daytab[i][j]
如果要将二维数组作为变元传递给函数,那么函数的参数说明中应该指明相应数组的列数。数组的行数不必指定,因为正如前所述,函数调用时传递给它的是一个指向由行向量构成的一维数组的指针,其中每个行向量是具有n个整数元素的一维数组。
一般而言,除第一维可以不指定大小外,其余各维都必须明确指定大小。
8、指针数组的初始化
month_name函数中包含一个私有的字符串数组,且当它被调用时返回一个指向正确的字符串位置的指针。
指针数组的初始化语法和早先见过的初始化相似:
char * month_name(int n)
{
static char *name[ ] = {
"Illegal month",
"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December"
};
return ( n < 1 | | n > 12 ) ? name[0] : name[n];
}
9、指针与多维数组
int a[10][20];
int *b[10];
那么a [3][4]与b[3][4]在语法上都是对一个整数的合法引用。但a是一个真正的二维数组:它分配了200个整数大小的存储空间,并且用常规的矩阵下标计算公式20×row+col来计算元素a[row][col]的位置。但对b而言,该定义仅仅分配了10个指针而且没有对它们进行初始化,它们的初始化必须以静态的方式或用代码显式地进行。
指针数组的重要优点在
于数组的每一行可以有不同的长度,即并不是b的每个元素都指向一个具有20个元素的向量,有些元素可以指向具有两个元素的向量,有些可以指向具有50个元素的向量,而有些根本就不指向任何向量。
10、命令行变元
在支持C语言的环境中,可以在程序开始执行时将命令行变元或参数传递给程序。调用主函数时,它可以带有两个变元。第一个变元(习惯上称做argc,用于变元计数)的值为程序执行时命令行中变元的数目,第二个变元(称为argv,用于变元向量)是一个指向字符串数组的指针,其中每个字符串对应一个变元。我们通常用多级指针处理这些字符串。
11、指向函数的指针
在C语言中,函数本身不是变量,但可以定义指向函数的指针,这种指针可以被赋值、存放于数组之中、传递给函数及作为函数返回值等等