经典指针问题

部分内容参考自《C专家编程》

int a; // 一个整型数
int *a; // 一个指向整型数的指针
int **a; // 一个指向指针的的指针,它指向的指针是指向一个整型数
int a[10]; // 一个有10个整型数的数组
int *a[10]; // 一个有10个指针的数组,该指针是指向一个整型数的
int (*a)[10]; // 一个指向有10个整型数数组的指针
int (*a) (int) // 一个指向函数的指针,该函数有一个整型参数并返回一个整型数
int (* a[10]) (int); // 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数

int a

可以写成int (a),a是非指针类型

首先需要注意的是“地址y”和“地址y的内容”之间的区别。这是一个相当微妙之处,因为在大多数编程语言中我们用同一个符号来表示这两样东西,由编译器根据上下文环境判断它的具体含义。

原文:

The first destinction we must note is between adress y and contents of address y. This is actually quite a subtle point, because in most programming languages we use the same symbol to represent both, and the compiler figures out which is meant from the context. Take a simple assignment.

x = y;

The symbol x, in this context, means the address that x represents. This is an l-value. An l-value is known at compiletime. An l-value says where to store the result.

The symbol y, in this context, means the contents of the address that y represents. This is an r-value. An r-value is not known until runtime. "The value of y" means the r-value unless otherwise stated.

int a = 3; // 编译过后,内存地址1000中的内容是3(“地址a”是1000,“地址a的内容”是3)

int b = 7; // 编译过后,内存地址1004中的内容是7(“地址b”是1004,“地址b的内容”是7)

a = b;      // 符号a出现在赋值号左边,含义是a所代表的地址,被称为左值; 符号b出现在赋值号右边,含义是b所代表的地址的内容,被称为右值。

               // 左值在编译时可知,左值表示存储结果的地方; 右值直到运行时才知,如无特别说明,右值表示“b的内容”

左值用来确定对象在内存中的位置,右值用来表示对象的内容

a = b 表示将“地址b的内容存放至地址a处”,执行后,“地址a”还是1000,“地址b”还是1004,“地址a的内容”变为7

(编译器为每个变量分配一个地址(左值)。这个地址在编译时可知,而且该变量在运行时一直保存这个地址。)

如果作为右值: b表示“地址b的内容”,为7; &b表示“地址b”,为1004

int *a

可以写成 int *(a) 或 int (*a),a是指针类型,它指向的类型为int

int b = 7;         // 编译过后,内存地址2000中的内容是7(“地址b”是2000,“地址b的内容”是7)

int *a = NULL;  // 编译过后,内存地址2004中的内容是2000(“地址a”是2004,“地址a的内容”是0)

a = &b;            // 该语句表示将“地址b存放至地址a处”,即将2000存放至2004处,现在“地址a”还是2004,“地址a的内容”变为2000

下面的代码定义了“指向char类型的指针”和“指向int类型的指针”,它们都是指针,但类型不同:

char x = 'a';      // “地址x”是1000,“地址x的内容”是97“ 地址x的内容”是'a',它的整型表示是97

int y = 7;          // “地址y”是1500,“地址y的内容”是7

char *pX = &x;  // “地址pX”是2000,“地址pX的内容”是1000

int *pY = &y;    // “地址pY”是2004,“地址pY的内容”是1500

“char类型”与“int类型”本质上是相同的,只是编译器解释它们的方式不同

当x或y表示左值时,“地址x”被解释为将右值存放至内存位置为1000的单个字节位置,“地址y”被解释为将右值存放至内存位置为1500的四个字节位置

当x或y表示右值时,“地址x的内容”是内存位置为1000的单个字节数据,“地址y的内容”是内存位置为1500的四个字节数据

“指向char类型的指针”与“指向int类型的指针”本质上也是相同的,只是编译器解释它们的方式不同,它们甚至可以互相转换:

pX = (char *)pY; // 将“地址pY的内容存放至地址pX处”,执行前“地址pY的内容”是1500,“地址pX”是2000,执行后“地址pX”还是2000,“地址pX的内容”变为1500

这时,pX的内容与pY的内容相同,也就是,指针pX与指针pY都指向了内存中1500处

但是它们表示的意义不同,pX表示内存1500处的单个字节数据,而pY表示内存1500处的4个字节数据

int **a

可以写成 int **(a) 或 int *(*a) 或 int (**a),a是指针类型,它指向的类型为int *

int c = 11;        // “地址c”是3000,“地址c的内容”是11

int *b = &c;      // “地址b”是3004,“地址b的内容”是3000

int **a = &b;    // “地址a”是3008,“地址a的内容”是3004

int a[10]

a为数组类型,数组中存储的类型为int

数组和指针看似相同,其实它们大不相同:

int a[2] = {3, 5}; // “地址a”是1000,“地址a的内容”占两个int型大小,是3、5

int *p = a;           // “地址p”是2004,“地址p的内容”是1000

对于数组中第二个元素的访问,或用a[1]或p[1]表示

其实这里的a和p的本质也是相同的,它们都是编译器生成的一个地址,并且它们在运行时一直保存这个地址(“地址a”是1000,“地址p”是1004)

那为什么1000[1]和1004[1]都可以表示数组中的第二个元素呢?

前面说过,虽然它们的本质相同,但是编译器对它们的解释方式不同

对于a[1],编译器认为它是一个int型的数组,会直接以“a的地址”加上一个int型的步长,得到1004; 当它作为左值时,编译器会把4个字节长度的右值存放至地址1004处; 当它作为右值时,编译器会提取地址1004处的内容,即5

对于p[1],编译器认为它是一个指向int型的指针,会将“地址p的内容”1000作为基地址,再加上一个int型的步长,得到1004;

int *a[10]

可以写成 int *(a)[10] 或 int *(a[10]),a是数组类型,数组中存储的类型为int *

int *a[10] 与 int a[10] 类似,都是长度为10的数组,只不过后者存储的类型为int,而前者存储的类型为int *

int (*a)[10]

a是指针类型,它指向的类型为int (*)[10]

a是指针类型,它指向的类型为int [10]

a是一个数组的指针,它指向的数组是“int b[10]”类型的

int arr[2] = {3, 5};  // arr是一个有2个整形数的数组,“地址arr”是1000,“地址arr的内容”占两个int型大小,是3、5

int (*a)[2] = &arr;   // “地址a”是2000,“地址a的内容”是1000

int *b = arr;            // “地址b”是2004,“地址b的内容”是1000

这里有一个问题,&arr与arr都表示1000?

是的!

但是它们的类型不同:

a的类型为int (*)[2]

b的类型为int *

可以有如下转化:

int (*a)[2] = (int (*)[2])arr;

int *b = (int *)&arr;

虽然a和b存储的内容在数值上相同,但是他们却不是同一种类型

sizeof(*a) 为 8,它指向 int [2] 类型,int [2] 类型的大小为8

sizeof(*b) 为 4,它指向 int 类型,int 类型的大小为4

int (*a)(int)

函数指针

int (*a[10])(int)

变量名后加[],数组

Conclusion

将变量声明/定义成数组,需在变量名后加[]

将变量声明/定义成指针,需在变量前加*

“[]”的“结合性”比“*”高,所以 int *a[10] 被解释为 int *(a[10])

转载于:https://www.cnblogs.com/wotent/archive/2013/02/28/2937137.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值