一、指针加减运算规则
1.运算规则
我们平常说的指针是指针变量,是用来存放内存地址的变量,通俗讲就是说将一个变量或者函数的地址的地址取出放入指针变量中,每一个内存上的字节都对应一个地址。
指针变量中存放的是一串数字,而这串数字再进行运算的时候,跟普通的算术运算大不相同,当对同一个指针进行运算时,计算机会根据指针指向类型进行计算,跳过一个该类型的大小。例:当一个指针变量指向整型,给指针变量+1,其在该内存中存储的数字就会+4。
即p+1在内存中存储的数字不会变成0x00121213,而是会变成0x00121216,从应用方面理解就是,给指针变量+1,指针变量会自动跳过其所指向的这个类型的变量的所有地址,指向其后方。
2.其他说明
再对题目进行分析之前还需要对一些小知识点说明一下,以防混淆
(1).数组名一般表示首元素地址但 &数组名 sizeof(数组名),求出的是整个数组的大小;
(2).sizeof()计算的是类型的大小,其知道括号内是什么类型就直接输出结果,不会对括号内的表达式进行计算,且注意数组名和数组名+0意思不同,一个是计算整个数组大小,一个是指针变量;
(3).strlen(),这个函数返回值是size_t,函数参数接收形式为const char *,遇到 '\0' 会停止计算。
(4).sizeof是计算'\0'的,strlen是不计算'\0'的
(5)对数组指针进行解引用时,取出来的是整个数组。
二、题目
1.整型数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a)); 此处求出的是整个数组的大小——16字节
printf("%d\n",sizeof(a+0)); 此处a不是自己单独出现的,a代表首元素地址——4/8字节
printf("%d\n",sizeof(*a)); 此处a代表首元素地址,*a代表首元素,int类型——4字节
printf("%d\n",sizeof(a+1)); a+1即代表a数组第二个元素的地址4/8字节
printf("%d\n",sizeof(a[1]));a[1]等同于 *(a+1) ,int类型——4字节
printf("%d\n",sizeof(&a)); &a取出整个数组的地址即为数组指针 int(*)[4]类型——4字节
printf("%d\n",sizeof(*&a)); *与&抵消掉,即a代表数组名——16字节
printf("%d\n",sizeof(&a+1));取地址操作符优先级高于+,此处指向a数组后面的一个数组指针(这个加1跳过了16个字节)——4/8字节
printf("%d\n",sizeof(&a[0]));此处取出的是首元素的地址——4/8字节
printf("%d\n",sizeof(&a[0]+1))此处是第二个元素的地址——4/8字节
2.字符数组
char arr[] = {'a','b','c','d','e','f'}; 这种字符串存储方式结尾没有'\0'
printf("%d\n", sizeof(arr)); sizeof(数组名)求的是整个数组的大小——6字节
printf("%d\n", sizeof(arr+0)); arr不是单独出现的,此处代表首元素地址——4/8字节
printf("%d\n", sizeof(*arr));*arr,代表首元素——1字节
printf("%d\n", sizeof(arr[1]));arr[1]=*(arr+1),此处代表第二个元素'b'——1字节
printf("%d\n", sizeof(&arr));此处取出的是整个字符串的地址——4字节
printf("%d\n", sizeof(&arr+1));此处取出地址后加以指向'f'后面的地址,(此处+1跳过6个字节)——4/8字节
printf("%d\n", sizeof(&arr[0]+1));此处为第二个元素的地址——4/8字节
printf("%d\n", strlen(arr)); 数组名代表首元素地址,字符串的存储最后没有'\0'——随机值
printf("%d\n", strlen(arr+0)); arr+0代表首元素地址,同上——随机值
printf("%d\n", strlen(*arr));*arr->'a',字符a的ASCII码值为97,strlen就会去找97地址处——ERR
printf("%d\n", strlen(arr[1]));意思同上——ERR
printf("%d\n", strlen(&arr));此处为数组指针,strlen需要的是字符指针,但是二者数值相同,strlen会使用这里的数组指针作为字符指针——随机值
printf("%d\n", strlen(&arr+1)); 此处跳过arr字符串,指向'f'后——比上面的随机值小6
printf("%d\n", strlen(&arr[0]+1));第二个字符的地址——比前面的随机值小1
char arr[] = "abcdef"; 这种命名方式使得字符串最后有'\0'
printf("%d\n", sizeof(arr));arr为数组名,此处计算整个数组的大小——7字节
printf("%d\n", sizeof(arr+0));arr不是单独出现,此处代表首元素地址——4/8字节
printf("%d\n", sizeof(*arr));此处代表第一个字符——1字节
printf("%d\n", sizeof(arr[1]));此处代表第二个字符——1字节
printf("%d\n", sizeof(&arr));此处代表数组指针——4/8字节
printf("%d\n", sizeof(&arr+1));此处跳过整个字符串,包括'\0',但还是指针变量——4/8字节
printf("%d\n", sizeof(&arr[0]+1));第二个字符的地址——4/8字节
printf("%d\n", strlen(arr));此处为首元素地址,且有'\0'——6字节
printf("%d\n", strlen(arr+0));此处也为首元素地址——6字节
printf("%d\n", strlen(*arr));此处为第一个元素——ERR
printf("%d\n", strlen(arr[1]));此处为第二个元素——ERR
printf("%d\n", strlen(&arr));strlen需要的是字符指针,但是二者数值相同,strlen会使用这里的数组指针作为字符指针——6字节
printf("%d\n", strlen(&arr+1));跳过整个字符串——随机值
printf("%d\n", strlen(&arr[0]+1));第二个元素开始算——5字节
char *p = "abcdef"; 将首元素的地址存入p,p就是一个字符指针
printf("%d\n", sizeof(p)); p就是一个字符指针——4/8字节
printf("%d\n", sizeof(p+1));指向第二个字符——4/8字节
printf("%d\n", sizeof(*p));第一个字符,char——1字节
printf("%d\n", sizeof(p[0]));p[0]=*(p+0),第一个字符——1字节
printf("%d\n", sizeof(&p));二级指针,指向p的指针 char**类型——4/8字节
printf("%d\n", sizeof(&p+1));跳过一个char*的对象,指向p的后面——4/8字节
printf("%d\n", sizeof(&p[0]+1));此处指向b,为字符指针——4/8字节
printf("%d\n", strlen(p));p为首元素地址——6字节
printf("%d\n", strlen(p+1));p+1从第二个字节开始算——5字节
printf("%d\n", strlen(*p));第一个字符——ERR
printf("%d\n", strlen(p[0]));p[0]=*(p+0)——ERR
printf("%d\n", strlen(&p));二级指针指向p——随机值
printf("%d\n", strlen(&p+1));二级指针指向p后——随机值
printf("%d\n", strlen(&p[0]+1));第二个元素地址开始算——5字节
3.二维数组
int a[3][4] = {0};
printf("%d\n",sizeof(a));sizeof(a)是整个数组的大小,共3*4——48字节
printf("%d\n",sizeof(a[0][0])); a[0][0]=*(*(a+0)+0),第一个元素——4字节
printf("%d\n",sizeof(a[0]));a[0]代表第一行的数组名,sizeof(数组名)代表整个数组的大小,这里也就是4*4——16字节
printf("%d\n",sizeof(a[0]+1));a[0]不是单独出现,这里代表第a[0][1]处的地址,int*类型——4/8字节
printf("%d\n",sizeof(*(a[0]+1)));上面的地址解引用了——4字节
printf("%d\n",sizeof(a+1));数组名不是单独出现a+1代表第二行地址(int*[4]类型)——4/8字节
☆printf("%d\n",sizeof(*(a+1)));a+1代表第二行地址,对第二行地址进行解引用,即对int*[4]进行解引用,取出了一个数组——16字节
printf("%d\n",sizeof(&a[0]+1));第二行的地址——4/8字节
printf("%d\n",sizeof(*(&a[0]+1))); 对第二行的地址进行解引用——16字节
printf("%d\n",sizeof(*a));对第一行元素进行解引用——16字节
printf("%d\n",sizeof(a[3]));第四行数组名,sizeof不会管内存中到底有没有这个a[3]只要知道类型就会计算——16字节