部分内容参考自《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])