前言:
本篇会列出全部有关数组和指针含义的内容、实例及分析,并引入八道笔试真题进行实操练习,干货满满。
贯穿本篇的核心知识就是数组名的意义:
1、sizeof(数组名),这里的数组名表示整个数组,sizeof计算的是整个数组的大小,注意括号中必须为数组名才满足这一意义,比如sizeof(arr+1)就不是整个数组的大小了,&数组名同理。
2、&数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3、除此之外所有的数组名都表示首元素的地址。
一、一维数组
int main() { //一维数组 int a[] = { 1,2,3,4 }; printf("%d\n", sizeof(a)); //16 //a为数组名,sizeof(数组名)计算的是整个数组的大小,即4*4=16 printf("%d\n", sizeof(a + 0)); //4/8 //a为数组名,代表首元素地址,a+0还是首元素地址,只要是地址,大小即为4/8 printf("%d\n", sizeof(*a)); //4 //*a为数组中的元素1,为int类型,sizeof(int)为4 printf("%d\n", sizeof(a + 1)); //4/8 //a+1指向数组中的元素2,为int*,只要是指针类型,大小即为4/8 printf("%d\n", sizeof(a[1])); //4 printf("%d\n", sizeof(&a)); //4/8 //&数组名取出的是整个数组的地址,但是仍为地址,所以大小为4/8 printf("%d\n", sizeof(*&a)); //16 //*&a相当于a,sizeof(数组名)计算的是整个数组的大小,为16 printf("%d\n", sizeof(&a + 1)); //4/8 //&a+1指向4后面一个元素,但是&a+1是指针,所以大小为4/8 printf("%d\n", sizeof(&a[0])); //4/8 //首元素地址,大小为4/8 printf("%d\n", sizeof(&a[0] + 1));//4/8 //&a[0]+1指向元素2,但他也是指针,所以大小为4/8 return 0; }
经过对一维数组的分析, 我们可以知道:
1、在判断时,只要是指针,那么他的大小即为4/8,32位为4,64位为8,可以加快我们做题判断的速度。
2、指针的步长取决于指针的类型,指针的类型为int,那么指针加+1就是跳过一个int类型,指针类型为数组,那么指针+1就是跳过整个数组。
二、字符数组
字符数组的分析除了sizeof我们再引入strlen库函数进行更深入的分析。
strlen函数的参数为const char*,功能为统计'\0'前的元素个数。
int main() { //字符数组 char arr[] = { 'a','b','c','d','e','f' }; printf("%d\n", sizeof(arr)); //6 printf("%d\n", sizeof(arr + 0)); //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)); //4/8 printf("%d\n", sizeof(&arr[0] + 1));//4/8 printf("%d\n", strlen(arr)); //随机 //该字符数组不是字符串,'\0'的位置不确定,所以为随机值 printf("%d\n", strlen(arr + 0)); //随机 printf("%d\n", strlen(*arr)); //err //strlen需要的参数是指针,而*arr是字符'a'。ASCII值为97 //字符'a'的ASCII值为97,strlen会从97这个地址开始计算字符串长度 //会造成非法访问 printf("%d\n", strlen(arr[1])); //err printf("%d\n", strlen(&arr)); //随机 printf("%d\n", strlen(&arr + 1)); //随机 printf("%d\n", strlen(&arr[0] + 1));//随机 char arr[] = "abcdef"; printf("%d\n", sizeof(arr)); //7 printf("%d\n", sizeof(arr + 0)); //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)); //4/8 printf("%d\n", sizeof(&arr[0] + 1));//4/8 printf("%d\n", strlen(arr)); //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)); //6 printf("%d\n", strlen(&arr + 1)); //随机 //该指针步长为该字符串,+1后指针指向'\0'的后一个位置,所以值为随机 printf("%d\n", strlen(&arr[0] + 1));//5 //该指针步长为一个字符,+1后指针指向字符'b',所以计算值为5 char* p = "abcdef"; //经过前面的学习我们知道p存放的是该字符串首个字符的地址 printf("%d\n", sizeof(p)); //4/8 printf("%d\n", sizeof(p + 1)); //4/8 printf("%d\n", sizeof(*p)); //1 printf("%d\n", sizeof(p[0])); //1 printf("%d\n", sizeof(&p)); //4/8 printf("%d\n", sizeof(&p + 1)); //4/8 //思考:这里指针指向了哪里?? printf("%d\n", sizeof(&p[0] + 1)); //4/8 printf("%d\n", strlen(p)); //6 printf("%d\n", strlen(p + 1)); //5 printf("%d\n", strlen(*p)); //err printf("%d\n", strlen(p[0])); //err printf("%d\n", strlen(&p)); //随机 printf("%d\n", strlen(&p + 1)); //随机 printf("%d\n", strlen(&p[0] + 1)); //5 return 0; }
char* p="hello world";
字符指针p存放的是该字符串首个字符的地址。
我想单独讲解一下这段代码:
int main { char* p = "abcdef"; printf("%d\n", sizeof(&p)); //4/8 printf("%d\n", sizeof(&p + 1)); //4/8 //思考:这里指针指向了哪里?? return 0; }
首先我们知道p指向的是字符'a',即p的类型为char*,在&p后,&p的类型就是char**,此时给&p+1,&p的步长应为char*,也就是指针&p+1相对于&p跳过了一个char* ,比如p的地址是0x0012ff40,如图:
也就是&p是指针的指针,所以他的步长应为一个指针的长度。
其中相似原理的我并未重复注释,大家有问题的可以评论或者私信问我🌹🌹🌹
三、二维数组
首先我们需要知道:
假定一个二维数组int a[3][4],对于二维数组a来说,a[0]就是该二维数组第一行的一维数组的数组名,同理a[1]就是该二维数组第二行的一维数组的数组名,又因为数组名就是数组首元素的地址,所以a[0]就是该二维数组第一行的一维数组的首元素地址,同理a[1]就是该二维数组第二行的一维数组的首元素地址。
在了解了上面的概念后,接下来的代码就迎刃而解了。
int main() { //二维数组 int a[3][4] = { 0 }; printf("%d\n", sizeof(a)); //48 printf("%d\n", sizeof(a[0][0])); //4 printf("%d\n", sizeof(a[0])); //16 printf("%d\n", sizeof(a[0] + 1)); //4/8 //即a[0]其实就是a[0][0]的地址,+1后变为a[0][1]的地址,只要是地址,大小就是4/8 printf("%d\n", sizeof(*(a[0] + 1))); //4 printf("%d\n", sizeof(a + 1)); //4/8 printf("%d\n", sizeof(*(a + 1))); //16 printf("%d\n", sizeof(&a[0] + 1)); //4/8 printf("%d\n", sizeof(*(&a[0] + 1)));//16 //a[0]是第一行数组的数组名,所以&a[0]取出的是该一维数组的地址 //+1后指向了第二行的一维数组,解引用后是第二行一维数组,大小为4*4=16 printf("%d\n", sizeof(*a)); //16 printf("%d\n", sizeof(a[3])); //16 //越界会影响sizeof的计算么?? return 0; }
sizeof是如何计算的?
我们发现最后一个题目中数组越界了,但并没有影响程序的运行,也就是说,sizeof的计算只考虑类型,不会真的访问计算,a[3]的类型是int [4],所以大小为4*4=16。
四、指针笔试题
笔试题(1)
int main() { int a[5] = { 1, 2, 3, 4, 5 }; int *ptr = (int *)(&a + 1); printf( "%d,%d", *(a + 1), *(ptr - 1)); return 0; } //程序的结果是什么?
答案:2,5
分析:
a代表首元素地址,指针类型为int*,步长为int,+1后指向元素'2',解引用后为元素'2',所以打印值为2。
&a拿到的是整个数组的地址,指针类型为int(*)[5],步长为int [5],+1后ptr指向元素'5'后面的位置,并将指针强制转换为int*类型,ptr指针类型为int*,步长为int,-1后ptr指向元素'5',解引用得到元素'5',所以打印值为5。