第四章、指针和数组
- 当初始化指针时要注意:为防止指针地址是非法的要进行改写,使其指向合法内存。例如:int i=10;int*p=&i;*p=NULL;
- 注意NULL 就是NULL,它被宏定义为0:
#define NULL 0
很多系统下除了有NULL外,还有NUL(Visual C++ 6.0 上提示说不认识NUL)。NUL 是ASCII码表的第一个字符,表示的是空字符,其ASCII 码值为0。其值虽然都为0,但表示的意思完全不一样。同样,NULL 和0 表示的意思也完全不一样。一定不要混淆。 - 有系统也定义了null,其意思也与NULL 没有区别,但是千万不用使用null。
- 将数值存储到指定的内存空间:
int*p=(int*)0x12ff7c;//强转 *p=0x100; //可优化为: (int*)0x12ff7c=0x100;
- 当我们定义一个数组a 时,编译器根据指定的元素个数和元素的类型分配确定大小(元素类型大小*元素个数)的一块内存,并把这块内存的名字命名为a。名字a 一旦与这块内存匹配就不能被改变。a[0],a[1]等为a 的元素,但并非元素的名字。数组的每一个元素都是没有名字的。
- 有一个特例:sizeof(a[5])的值在32 位系统下为4。sizeof 是关键字不是函数。函数求值是在运行的时候,而关键字sizeof 求值是在编译的时候。它是根据参数的类型来确定其值的。
- &a[0]和&a的值一样,但其意义不一样。前者是数组首元素的首地址,而后者是数组的首地址。
int a[5];={0}; &a+1=&a+5*sizeof(int);//为整个数组之后的地址
- 出现在赋值符“=”右边的就是右值,出现在赋值符“=”左边的就是左值。
为左值时,代表地址。为右值时,代表的地址中的内容。
出现在赋值符左边的符号所代表的地址上的内容一定是可以被修改的。换句话说,就是我们只能给非只读变量赋值。 - 数组名a为右值时代表首元素的首地址,而非数组的首地址。
a不能作为左值。编译器会认为数组名作为左值代表的意思是a 的首元素的首地址,但是这个地址开始的一块内存是一个总体,我们只能访问数组的某个元素而无法把数组当一个总体进行访问。 - 指针与数组之间间没有任何关系!
- 以下标的形式访问在本质上与以指针的形式访问没有区别,只是写法上不同。
- 指针和数组根本就是两个完全不一样的东西。只是它们都可以“以指针形式”或“以下标形式”进行访问。一个是完全的匿名访问,一个是典型的具名+匿名访问。
- 指针和数组的对比
- 指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身
决定。它是“储存指针的数组”的简称。
数组指针:首先它是一个指针,它指向一个数组。在32 位系统下永远是占4 个字节,
至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称。
(这里“()”的运算等级比“【】”要高) - 地址的强制转换
指针变量与一个整数相加减并不是用指针变量里的地址直接加减这个整数。这个整数的单位不是byte 而是元素的个数。 - 无法向函数传递一个数组
C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元
素首地址的指针。 - 在C 语言中,所有非数组形式的数据实参均以传值形式(对实参做一份拷贝并传递给被调用的函数,函数不能修改作为实参的实际变量的值,而只能修改传递给它的那份拷贝)调用。然而,如果要拷贝整个数组,无论在空间上还是在时间上,其开销都是非常大的。
- 函数的返回值也不能是一个数组,而只能是指针。这里要明确的一个概念就是:函数本身是没有类型的,只有函数的返回值才有类型。
- 无法把指针变量本身传递给一个函数
对实参做一份拷贝并传递给被调用的函数。 - C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。这条规则并不是递归的,也就是说只有一维数组才是如此,当数组超过一维时,将第一维改写为指向数组首元素首地址的指针之后,后面的维再也不可改写。比如:a[3][4][5]作为参数时可以被改写为(*p)[4][5]。
- 函数指针
char * (*fun)(char * p1,char * p2);// 正确定义形式 char * (*)(char * p1,char * p2) fan1;//辅助理解形式 int (*) [10] p;//类比
- 函数指针数组
这些指针指向一些返回值类型为指向字符的指针、参数为一个指向字符的指针的函数。 - 函数指针数组的指针:指向一个有指向函数的指针的数组。