目录
本篇文章会以题目形式,辅助理解指针和数组(内容有点多)。
一、sizeof大小的判断以及strlen长度的判断
知识点:
*数组名是数组首元素的地址,但是有两个例外:
1.sizeof(数组名),这里的数组名是表示整个数组的,计算的是整个数组的大小,单位是字节。
2.&数组名,这里的的数组名也表示整个数组,取出的是数组的地址。
除上面两种特殊情况外,所有的数组名都是数组首元素的地址。
注意:以下代码均在32位平台下运行(x86),地址大小均为4个字节;若在64位平台下(x64),地址大小都会变成8个字节。
1. 一维数组
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a)); //16
printf("%d\n", sizeof(a + 0)); //4
printf("%d\n", sizeof(*a)); //4
printf("%d\n", sizeof(a + 1)); //4
printf("%d\n", sizeof(a[1])); //4
printf("%d\n", sizeof(&a)); //4
printf("%d\n", sizeof(*&a)); //16
printf("%d\n", sizeof(&a + 1)); //4
printf("%d\n", sizeof(&a[0])); //4
printf("%d\n", sizeof(&a[0] + 1));//4
解析(见图):
一维数组的情况比较简单,只要抓住开头那条规则,基本上不会出错。
2. 字符数组
补充:关于strlen的知识点:
sizeof(操作符/关键字):不关注类型,只关注占用空间的大小。
strlen(库函数):只针对字符串类型,关注的是字符串中\0的位置,计算的是\0之前出现了多少个字符。
注意:strlen需要的是一个地址,从这个地址开始向后找字符。该函数的返回值为size_t,是无符号的。
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr)); //6
printf("%d\n", sizeof(arr + 0)); //4
printf("%d\n", sizeof(*arr)); //1
printf("%d\n", sizeof(arr[1])); //1
printf("%d\n", sizeof(&arr)); //4
printf("%d\n", sizeof(&arr + 1)); //4
printf("%d\n", sizeof(&arr[0] + 1)); //4
printf("%d\n", strlen(arr)); //随机值
printf("%d\n", strlen(arr + 0)); //随机值
printf("%d\n", strlen(*arr)); //报错
printf("%d\n", strlen(arr[1])); //报错
printf("%d\n", strlen(&arr)); //随机值
printf("%d\n", strlen(&arr + 1)); //随机值
printf("%d\n", strlen(&arr[0] + 1)); //随机值
解析(见图):
char brr[] = "abcdef";
printf("%d\n", sizeof(brr)); //7
printf("%d\n", sizeof(brr + 0)); //4
printf("%d\n", sizeof(*brr)); //1
printf("%d\n", sizeof(brr[1])); //1
printf("%d\n", sizeof(&brr)); //4
printf("%d\n", sizeof(&brr + 1)); //4
printf("%d\n", sizeof(&brr[0] + 1)); //4
printf("%d\n", strlen(brr)); //6
printf("%d\n", strlen(brr + 0)); //6
printf("%d\n", strlen(*brr)); //报错
printf("%d\n", strlen(brr[1])); //报错
printf("%d\n", strlen(&brr)); //6
printf("%d\n", strlen(&brr + 1)); //随机值
printf("%d\n", strlen(&brr[0] + 1)); //5
解析(见图):
数组中存字符串相比上面的数组中存字符,在字符串最后会自动有'\0',所以在计算数组长度的时候,能准确计算长度,如果想要在字符数组中不返回随机值,需手动在数组末尾加上'\0'。
char* p = "abcdef";
printf("%d\n", sizeof(p)); //4
printf("%d\n", sizeof(p + 1)); //4
printf("%d\n", sizeof(*p)); //1
printf("%d\n", sizeof(p[0])); //1
printf("%d\n", sizeof(&p)); //4
printf("%d\n", sizeof(&p + 1)); //4
printf("%d\n", sizeof(&p[0] + 1));//4
printf("%d\n", strlen(p)); //6
printf("%d\n", strlen(p + 1)); //5
printf("%d\n", strlen(*p)); //报错
printf("%d\n", strlen(p[0])); //报错
printf("%d\n", strlen(&p)); //随机值
printf("%d\n", strlen(&p + 1)); //随机值
printf("%d\n", strlen(&p[0] + 1));//5
解析(见图):
字符数组总的来说不难,细心一点就不会判断错。
3. 二维数组
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
printf("%d\n", sizeof(*(a[0] + 1))); //4
printf("%d\n", sizeof(a + 1)); //4
printf("%d\n", sizeof(*(a + 1))); //16
printf("%d\n", sizeof(&a[0] + 1)); //4
printf("%d\n", sizeof(*(&a[0] + 1)));//16
printf("%d\n", sizeof(*a)); //16
printf("%d\n", sizeof(a[3])); //16
解析(见图):
二维数组相较于一维数组会难判断一点,但只要记住:二维数组解引用一次就是某一行的地址,再解引用一次才某一行元素
二、具体题目判断
题目1:
#include <stdio.h>
int main()
{
int a[5] = { 1,2,3,4,5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1)); //2,5
return 0;
}
解析(见图):
题目2:
#include <stdio.h>
struct Test
{
int Num;
char* pcName;
char cha[2];
short sBa[4];
}*p;
//假设p的值为0x100000
int main()
{
printf("%p\n", p + 0x1); //0x100014
printf("%p\n", (unsigned long)p + 0x1);//0x100001
printf("%p\n", (unsigned int*)p + 0x1);//0x100004
return 0;
}
解析(见图):
这道题需要计算结构体大小,后面文章会再讲到计算结构体的大小要用到结构体内存对齐规则,这里是解释指针就不对结构体部分做过多描述。
题目3:
#include <stdio.h>
int main()
{
int a[4] = { 1,2,3,4 };
int* ptr1 = (int*)(&a + 1);
int* ptr2 = (int*)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2); //4,2000000
return 0;
}
解析(见图):
题目4:
#include <stdio.h>
int main()
{
int a[3][2] = { (0,1),(2,3),(4,5) };
int* p;
p = a[0];
printf("%d", p[0]); //1
return 0;
}
解析(见图):
这里要注意的是,初始化数组中只有3个数,因为是用()括起来的,()里相当于逗号表达式,值为最后一个表达式的值。如果要表示成初始化三行两列的形式,应该把()换成{ }。
题目5:
#include <stdio.h>
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); //FFFFFFFC,-4
return 0;
}
解析(见图):
注意:要知道%p打印的是什么。
题目6:
#include <stdio.h>
int main()
{
int aa[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
int* ptr1 = (int*)(&aa + 1);
int* ptr2 = (int*)(*(aa + 1));
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); //10,5
return 0;
}
解析(见图):
这道题是二维数组的,可以和前面题目3的一维数组做类比,他们的同样的题型。
题目7:
#include <stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa); //at
return 0;
}
解析(见图):
题目8:
#include <stdio.h>
int main()
{
char* c[] = { "ENTER","NEW","POINI","FIRST" };
char** cp[] = { c + 3,c + 2,c + 1,c };
char*** cpp = cp;
printf("%s\n", **++cpp); //POINI
printf("%s\n", *-- * ++cpp + 3); //ER
printf("%s\n", *cpp[-2] + 3); //ST
printf("%s\n", cpp[-1][-1] + 1); //EW
return 0;
}
解析(见图):
这道题算是比较复杂的一种题型,但是画图还是能够很好地理解并做出来的。
以上就是指针和数组的一些题目,理解透这些题目,基本上就能够比较好地运用指针和数组。再次强调:指针和数组对于C语言来说是非常重要的,要很好地理解好并运用出来。