指针总结

指针的本质

可执行程序是由指令、数据和地址组成的。当CPU访问内存单元时,必须把内存单元的地址加载到地址总线上,同时将内存电路的“读写控制”设置为有效,然后内存单元中的数据就通过数据总线流向了接受寄存器中,或者结果寄存器中的值流向目标内存单元中,这就是内存读写周期,而内存单元地址就是指针的值

定义指针类型的注意事项

虽然类型名和*的组合是一种新的类型,但是编译器解释的时候会将*和后面的变量名结合

1
int* a, b, c;

这样定义的话a为int类型指针,b和c还是int类型的变量
全局指针变量的默认初始值是NULL,而non-static局部指针必须显示指定初始值;所以任何指针应在声明的同时初始化它,要么赋有效地址,要么置空

指针有实际意义的运算

(1) 自增运算,表示指向下一个元素
(2) 自减运算,表示指向上一个元素
(3) 加整数i,表示向后递进i个元素
(4) 减整数i,表示向前递进i个元素
(5) 同类型指针相减,表示计算它们之间的元素个数
(6) 指针赋值,把一个指针值赋给另一个指针
(7) 指针比较(> < == != >= <=)标准允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针进行比较,但不允许与指向数组第一个元素之前的那个内存位置的指针进行比较
(8) 取地址和反引用

1
2
3
4
5
int arr[10] = { 0 };
printf("%p\n", arr); // arr是首元素的地址
printf("%p\n", arr + 1); // 编译器改写成为arr+1*sizeof(int)
printf("%p\n", &arr); // &arr是数组的地址,虽然值和arr一样但是表示的对象不同
printf("%p\n", &arr + 1);// 编译器改写成为&arr+1*sizeof(arr)

注意void*类型指针不能参与算术运算、不能解引用,只能赋值、比较、和sizeof()操作

字符指针的误区

C/C++中默认char*表示字符串:

1
2
3
4
char ch = 'a';
char* pChar = &ch;
cout << pChar << endl; // 编译器会错把字符指针当作字符串
cout << *pChar << endl; // 正确用法:输出一个字符

在初始化字符数组的时候可以这样:

1
2
char message[] = { "Hello" };
char message2[] = "Hello";

尽管看上去第二种好像是一个char*类型的字符串常量指针,实际上并不是,它和第一种写法是等价的
也就是说当用于初始化一个字符数组时,它就是一个初始化列表,在其他任何地方,它都表示一个字符串常量
比如这样:

1
2
3
char* p = "Hello";
const char* pArr[3] = { "Hello", "C", "C++" };// 指针数组
char arr[][6] = { "Hello", "C", "C++" }; // 这里是值拷贝

数组指针

数组指针它是一个指针,但它是指向数组的指针

1
2
3
4
5
int(*ptr1)[10] = NULL;
int(*ptr2)[10] = ptr1 + 1;
printf("%d\n", ptr1); // 0
printf("%d\n", ptr2); // 40
printf("%d\n", ptr2 - ptr1);// 1

数组指针加减整数,加减的基本单位是整个数组,如上述例子,ptr1为0,ptr2=ptr1+1,但是ptr2不为1,而是40,跳过整个含有10个int类型的数组;但是ptr2-ptr1还是1,并不是40,这是因为基本单位是整个数组(40个字节的数组)每40个字节,对应1个单位,所以结果为1

指针数组

指针数组它是一个数组,但它的每一个元素是指针

1
2
3
4
int a = 0;
int b = 0;
int c = 0;
int* arr[3] = { &a, &b, &c };

arr[3]中的三个元素都是int*类型的,其中数组名arr的值是一个二级指针