指针是C语言的精髓,正是指针使C威力无穷,下面是我学习过程中总结的一些指针相关内容,持续更新中;
指针的本质
可执行程序是由指令、数据和地址组成的。当CPU访问内存单元时,必须把内存单元的地址加载到地址总线上,同时将内存电路的“读写控制”设置为有效,然后内存单元中的数据就通过数据总线流向了接受寄存器中,或者结果寄存器中的值流向目标内存单元中,这就是内存读写周期,而内存单元地址就是指针的值。
定义指针类型的注意事项
虽然类型名和*的组合是一种新的类型,但是编译器解释的时候会将*和后面的变量名结合
int* a,b,c;
如果像这样定义编译器就会理解成:
int *a,b,c;
a为int类型指针,b和c还是int类型的变量。
全局指针变量的默认初始值是NULL,而non-static局部指针必须显示指定初始值;所以任何指针应在声明的同时初始化它,要么赋有效地址,要么置空;
指针有实际意义的运算
(1) 自增运算;表示指向下一个元素;
(2) 自减运算;表示指向上一个元素;
(3) 加整数i;表示向后递进i个元素;
(4) 减整数i;表示向前递进i个元素;
(5) 同类型指针相减;表示计算它们之间的元素个数;
(6) 指针赋值,把一个指针值赋给另一个指针
(7) 指针比较(> < == != >= <=)
(8) 取地址和反引用
上述3、4条中递进i个元素其含义是:指针所指对象字节数信息
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()操作
&的操作数必须为一个对象,不能对void*类型指针使用*取其所指向的变量
int a = 0;
//int **p = &&a; 这样是非法的
int *pInt = &a;
int **ppInt = &pInt;
字符指针的误区
C/C++中默认char *表示字符串:
char ch = 'a';
char *pChar = &ch;
cout << pChar << endl; // 编译器会错把字符指针当作字符串
cout << *pChar << endl; // 正确用法:输出一个字符
在初始化字符数组的时候可以这样:
char message[] = { "Hello" };
char message2[] = "Hello";
尽管看上去第二种好像是一个char*类型的字符串常量指针,实际上并不是,它和第一种写法是等价的。
也就是说当用于初始化一个字符数组时,它就是一个初始化列表,在其他任何地方,它都表示一个字符串常量。
比如这样:
char *p = "Hello";
const char *pArr[3] = { "Hello", "C","C++" }; // 指针数组,C++中必须加上const,c语言由于是弱类型,可以不加,但是最好加上,良好的编程习惯。
char arr[][6] = { "Hello", "C", "C++" }; // 这里是值拷贝,字符数组,不需const
字符串拷贝和比较
字符串拷贝时需要用strcpy/strncpy,不要用=,因为那是字符指针的赋值。
同理 ==、 >=、 != 符号比较的时字符指针,如果想要比较字符串是否相等需要用strcmp/strncmp
数组指针
它是一个指针,但它是指向数组的指针
int(*ptr1)[10] = 0;
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。
指针数组
它是一个数组,但它的每一个元素是指针(指针的数组)
int a = 0;
int b = 0;
int c = 0;
int *arr[3] = { &a,&b,&c };
arr[3]中的三个元素都是int*类型的,其中数组名arr的值是一个二级指针