指针是c语言中最重要的同时也是最难掌握的,因为数组跟指针息息相关,所以放在一起讲解。
//声明数组
int arr[3];
int arr[3] = {1,2,3};
#非字符串
char str_arr = {'f','r','e','e','c','l','s'};
#字符串
char str = {'f','r','e','e','c','l','s',' '};
//由编译器自动指定数组大小
int arr[] = {1,2,3}
//多维数组
int arr[2][2] = {{1,2}, {3,4}}
数组变量名本身就是数组的地址,数组跟指针的关系很密切
int arr[] = {1,2,3}
//数组的地址等于数组第一个元素的地址
//这两者都是常量,不能改变,如果想改变可以赋值给一个指针,然后改变该指针的值
arr == &arr[0]
//数组名可以表示指针名,反之亦然
int *ptr = arr
ptr[0] == arr[0]
//当作为函数形参时,int [], int *是一样的
//相同的地址
arr + 2 == &arr[2]
//相同的值
*(arr + 2) == arr[2]
//第1个元素值加2,因为*的优先级高于+
*arr + 2
指针的加减会根据指针类型的存储单元倍数移动
下面的例子中:
int型指针+1实际上指针位置移动了4(我的centos7 64位int占4个字节)
long型指针+1实际上指针位置移动了8(我的centos7 64位long占8个字节)
#include <stdio.h>
int main(){
int arr[3] = {1,2,3};
long arr_l[3] = {1,2,3};
int *ptr = arr;
long *ptr_l = arr_l;
//0x7ffd89358850 0x7ffd89358854
printf("%p %p
", ptr, ptr+1);
//0x7ffea17892d0 0x7ffea17892d8
printf("%p %p
", ptr_l, ptr_l+1);
}
指针求差,比较:
2个指针必须在同一个数组里,差值就是他们之间相差几个存储单元
#include <stdio.h>
int main(){
int arr[6] = {1,2,3,4,5,6};
int *ptr_1 = &arr[2];
int *ptr_2 = &arr[4];
//2 -2
printf("%d %d
", ptr_2 - ptr_1, ptr_1 - ptr_2);
//1 0
printf("%d %d
", ptr_2 > ptr_1, ptr_2 < ptr_1);
//相加直接报错
//printf("%d %d
", ptr_2 + ptr_1, ptr_1 + ptr_2);
}
解引用未初始化的指针
int *pt;
*pt = 5; //严重错误
因为创建一个指针时系统只分配了储存指针本身的内存,并未分配存储数据的内存。
进阶例子
#include <stdio.h>
int main(){
int arr[6] = {1,2,3,4,5,6};
int *ptr = arr;
int total = 0;
//结合律优先级相同的从右往左
//所以右边的ptr指针先++再求值,所以ptr已经指向数组第二个值
//而total是先加上ptr指向的值,然后ptr++
total += *ptr++;
/*等同于
total = total + *ptr
ptr++;
*/
printf("%d %d", total, *ptr); //1 2
total += *++ptr;
/*等同于
total = total + *(ptr+1)
ptr++;
*/
printf("%d %d", total, *ptr); //4 3
}
备注:有关优先级的操作尽量使用括号,免得影响代码阅读
指针和数组
int arr[2][2] = {{1,2}, {3,4}};
1.arr是数组首元素的地址,二维数组的首元素是arr[0](是arr[0][0]的地址),所以*(arr[0])就是arr[0][0]。
2.*arr代表数组首元素(arr[0])的值,而arr[0]是一个数组,数组本身是地址,所以*arr == &arr[0][0]。
3.**arr与*&arr[0][0]等价
简而言之,arr是地址的地址,要解引用2次才能得到原始值,地址的地址就是指针的指针,数组维度越大,就越复杂,这就是为啥指针那么难理解的原因。
#include <stdio.h>
int main(){
int arr[3][3] = {{1,2,3}, {3,4,5}};
//0x7ffd6688d660 0x7ffd6688d660 0x7ffd6688d660 0x7ffd6688d660
printf("%p %p %p %p
", arr, arr[0], &arr[0], &arr[0][0]);
//0x7ffd6688d668 0x7ffd6688d664 0x7ffd6688d668 0x7ffd6688d664
printf("%p %p %p %p
", arr+1, arr[0]+1, &arr[0]+1, &arr[0][0]+1);
}
arr 一个指向第一个数组的指针(数组指针,也就是指针的指针)
arr+1 由于arr是数组指针存储单元为数组大小,所以会往后移动一个数组,即&arr[1]
*(arr+1) 第二个数组的值即&arr[1][0]
*(arr+1)+1 即&arr[1][1]
*(*(arr+1) + 1) 即arr[1][1]
#include <stdio.h>
int main(){
int arr[3][3] = {{1,2,3}, {3,4,5}};
//0x7ffe4c7472b0 0x7ffe4c7472bc 0x7ffe4c7472bc 0x7ffe4c7472c0 4
printf("%p %p %p %p %d
", arr, arr+1, *(arr+1), *(arr+1) + 1, *(*(arr+1) +1 ) );
//0x7ffe4c7472b0 0x7ffe4c7472bc 0x7ffe4c7472bc 0x7ffe4c7472c0 4
printf("%p %p %p %p %d
", arr, &arr[1], &arr[1][0], &arr[1][1], arr[1][1] );
}
再来看下面2个声明的区别,它们都是指针的指针
一个是指针(可以改变自己,比如说自增)
一个是指针数组(不能改变自己)
int (* ptr)[2];
声明了1个指针ptr,它指向了一个数组,该数组包含2个int值。
int * pax[2];
声明了一个指针数组pax,它里面包含2个int型指针。
例子
#include <stdio.h>
int main(){
int (*ptr)[2];
int arr[2][2] = {{1,2}, {3,4}};
ptr = &arr[0];
//1 2
printf("%d %d
", ptr[0][0], ptr[0][1]);
printf("%d
", **(++ptr)); //3
int * pax[2];
pax[0] = arr[0];
pax[1] = arr[1];
//1 2
printf("%d %d
", pax[0][0], pax[0][1]);
//报错因为pax为数组变量,不能改变自己
printf("%d %d
", **(++pax));
}