《C语言深度解剖》第四章 数组与指针 总结

1.指针的内存布局

一个基本的数据类型(包括结构体等自定义类型)加上“*”号就构成了一个指针类型的模子。这个模子的大小是一定的,与“*”号前面的数据类型无关。“*”号前面的数据类型只是说明指针所指向的内存里存储的数据类型。所以,在32 位系统下,不管什么样的指针类型,其大小都为4byte。

2 指针的定义和初始化

int *p = NULL;这句代码的意思是:定义一个指针变量p,其指向的内存里面保存的是int 类型的数据;在定义变量p 的同时把p 的值设置为0x00000000,而不是把*p 的值设置为0x00000000。这个过程叫做初始化,是在编译的时候进行的。

3 数组的内存布局

int a[5];,当我们定义一个数组a 时,编译器根据指定的元素个数和元素的类型分配确定大小(元素类型大小*元素个数)的一块内存,并把这块内存的名字命名为a。名字a 一旦与这块内存匹配就不能被改。a[0],a[1]等为a 的元素,但并非元素的名字。数组的每一个元素都是没有名字的。

4数组首地址和数组首元素首地址(&a和&a[0])的区别

这里&a[0]和&a 到底有什么区别呢?a[0]是一个元素,a 是整个组,虽然&a[0]和&a的值一样,但其意义不一样。前者是数组首元素的首地址,而后者是数组的首地址。举个例子:湖南的省政府在长沙,而长沙的市政府也在长沙。两个政府都在长沙,但其代表的意义完全不同。这里也是同一个意思。
main()
{
    int a[5]={1,2,3,4,5};
    int *ptr=(int *)(&a+1);
    printf("%d,%d",*(a+1),*(ptr-1));
}
打印出来的值为多少呢? 这里主要是考查关于指针加减操作的理解。对指针进行加1 操作,得到的是下一个元素的地址,而不是原有地址值直接加1。所以,一个类型为T 的指针的移动,以sizeof(T) 为移动单位。因此,对上题来说,a 是一个一维数组,数组中有5 个元素; ptr 是一个int 型的指针。

&a + 1: 取数组a 的首地址,该地址的值加上sizeof(a) 的值,即&a + 5*sizeof(int),也就是下一个数组的首地址,显然当前指针已经越过了数组的界限。

(int *)(&a+1): 则是把上一步计算出来的地址,强制转换为int * 类型,赋值给ptr。

*(a+1): a,&a 的值是一样的,但意思不一样,a 是数组首元素的首地址,也就是a[0]的首地址,&a 是数组的首地址,a+1 是数组下一元素的首地址,即a[1]的首地址,&a+1 是下一个数组的首地址。所以输出2。

*(ptr-1): 因为ptr 是指向a[5],并且ptr 是int * 类型,所以*(ptr-1) 是指向a[4] ,输出5。

5数组名a作为左值和右值的区别

简单而言,出现在赋值符“=”右边的就是右值,出现在赋值符“=”左边的就是左值。比如,x=y。

左值:在这个上下文环境中,编译器认为x 的含义是x 所代表的地址。这个地址只有编译器知道,在编译的时候确定,编译器在一个特定的区域保存这个地址,我们完全不必考虑这个地址保存在哪里。

右值:在这个上下文环境中,编译器认为y 的含义是y 所代表的地址里面的内容。这个内容是什么,只有到运行时才知道。

C 语言引入一个术语-----“可修改的左值”。意思就是,出现在赋值符左边的符号所代表的地址上的内容一定是可以被修改的。换句话说,就是我们只能给非只读变量赋值。

当a 作为右值的时候代表的是什么意思呢?很多书认为是数组的首地址,其实这是非常错误的。a 作为右值时其意义与&a[0]是一样,代表的是数组首元素的首地址,而不是数组的首地址。这是两码事。但是注意,这仅仅是代表,并没有一个地方(这只是简单的这么认为,其具体实现细节不作过多讨论)来存储这个地址,也就是说编译器并没有为数组a分配一块内存来存其地址,这一点就与指针有很大的差别。

a 不能作为左值!这个错误几乎每一个学生都犯过。编译器会认为数组名作为左值代表的意思是a 的首元素的首地址,但是这个地址开始的一块内存是一个总体,我们只能访问数组的某个元素而无法把数组当一个总体进行访问。所以我们可以把a[i]当左值,而无法把a当左值。其实我们完全可以把a 当一个普通的变量来看,只不过这个变量内部分为很多小块,我们只能通过分别访问这些小块来达到访问整个变量a 的目的。

6指针和数组的定义声明

首先强调定义和声明之间的区别:
(1)定义分配内存,而声明没有;
(2)定义只能出现一次,而声明可以出现多次;

以下两种情况都是错误的:    
定义为数组,声明为指针
定义为指针,声明为数组 

7指针和数组的对比
这里写图片描述
这里写图片描述

8指针数组和数组指针

初学者总是分不出指针数组与数组指针的区别。其实很好理解:

指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。

数组指针:首先它是一个指针,它指向一个数组。在 32 位系统下永远是占 4 个字节,至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称
下面到底哪个是数组指针,哪个是指针数组呢:
A), int *p1[10];
B), int (*p2)[10];

这里需要明白一个符号之间的优先级问题。“[]”的优先级比“*”要高。 
p1 先与“[]”结合,构成一个数组的定义,数组名为 p1, int *修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含 10 个指向 int 类型数据的指针,即指针数组。至于 p2 就更好理解了,在这里“()”的优先级比“[]”高,“*”号和 p2 构成一个指针的定义,指针变量名为 p2, int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚 p2 是一个指针,它指向一个包含 10 个 int 类型数据的数组,即数组指针。我们可以借助下面的图加深理解:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/vonvingon/article/details/68928019