C语言指针易错题分析

发布于:2022-12-21 ⋅ 阅读:(383) ⋅ 点赞:(0)

最近在做题的时候,看见了一些很有趣的题目

来看看第一道题: 

int main()
{
   int a[] = {1,2,3,4};
   printf("%d\n",sizeof(a));
   printf("%d\n",sizeof(a+0));
   printf("%d\n",sizeof(*a));
   printf("%d\n",sizeof(a+1));
   printf("%d\n",sizeof(a[1]));
   printf("%d\n",sizeof(&a));
   printf("%d\n",sizeof(*&a));
   printf("%d\n",sizeof(&a+1));
   printf("%d\n",sizeof(&a[0]));
   printf("%d\n",sizeof(&a[0]+1));
return 0;
}

首先要说的是:

关于数组名

数组名是数组首元素的地址

但是有2个例外:

1. sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小,单位是字节

2. &数组名 - 数组名也表示整个数组,取出的是整个数组的地址

//除了这个2个例外,你见到的所有的数组名都表示首元素的地址。

 来分析一下每个数算出来的结果:

1、printf("%d\n", sizeof(a));//16,a作为数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节。

2、printf("%d\n", sizeof(a + 0));//4/8,a并非单独放在sizeof内部,也没有&,所以数组名a就是数组首元素的地址,a+0还是数组首元素的地址,是地址大小就是 4/8 个字节。

3、printf("%d\n", sizeof(*a));//a是首元素的地址,*a就是首元素,sizeof(*a)就算的就是首元素的大小 - 4

4、printf("%d\n", sizeof(a + 1));//a是首元素的地址,a+1是第二个元素的地址,sizeof(a+1)计算的是指针的大小 - 4/8

5、printf("%d\n", sizeof(a[1]));//a[1]就是数组的第二个元素,sizeof(a[1])的大小 - 4个字节

6、printf("%d\n", sizeof(&a));//&a取出的数组的地址,数组的地址,也是地址呀,sizeof(&a)就是 4/8 个字节。

7、printf("%d\n", sizeof(*&a));//&a是数组的地址,是数组指针类型,*&a是都数组指针解引用,访问一个数组的大小--16字节,sizeof(*&a) ==> sizeof(a) =16

8、printf("%d\n", sizeof(&a + 1));//&a数组的地址,&a+1跳过整个数组,&a+1还是地址,是 4/8 个字节。

9、printf("%d\n", sizeof(&a[0]));//a[0]是数组的第一个元素,&a[0]是第一个元素的地址,是 4/8 个字节。

10、printf("%d\n", sizeof(&a[0] + 1));//&a[0]是第一个元素的地址,&a[0]+1就是第二个元素的地址,是 4/8 个字节。&a[0]+1 == &a[1]

接下来看看第二道题:

//字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));

接着分析;

1、printf("%d\n", sizeof(arr)) //arr是数组名,并且是单独放在sizeof内部,计算的是数组总大小,单位是字节 - 6

2、printf("%d\n", sizeof(arr + 0));//arr是数组名,并非单独放在sizeof内部,arr表示首元素的地址,arr+0还是首元素的地址,是地址大小就是4/8。

3、printf("%d\n", sizeof(*arr));//arr是首元素的地址,*arr就是首元素,sizeof计算的是首元素的大小,是1字节。

4、printf("%d\n", sizeof(arr[1]));//arr[1]是数组的第二个元素,sizeof(arr[1])计算的是第二个元素的大小,1个字节。

5、printf("%d\n", sizeof(&arr));//&arr- 取出的是数组的地址,sizeof(&arr))计算的是数组的地址的大小,是地址就是4/8字节。

6、printf("%d\n", sizeof(&arr + 1));//&arr是数组的地址,&arr+1跳过整个数组,指向'f'的后边,&arr+1的本质还是地址,是地址就是4/8字节。

7、printf("%d\n", sizeof(&arr[0] + 1));//&arr[0]是‘a’的地址,&arr[0]+1是'b'的地址,是地址就是4/8字节。

再来看看第三道题:

char arr[] = {'a','b','c','d','e','f'};
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));

1、 printf("%d\n", strlen(arr));//随机值,arr是数组名,但是没有放在sizeof内部,也没&,arr就是首元素的地址,strlen得到arr后,从arr数组首元素的地方开始计算字符串的长度,直到直到\0,但是arr数组中没有\0,arr内存的后边是否有\0,在什么位置。‘\0’是不确定的,所以\0之前出现了多少个字符是随机的。

2、printf("%d\n", strlen(arr + 0));//arr是数组首元素的地址,arr+0还是首元素的地址,所以还是随机值。

3、printf("%d\n", strlen(*arr));//arr是数组首元素的地址,*arr 是首元素 - ‘a’ - 97

strlen就把‘a’的ASCII码值 97 当成了地址,所以为err 会非法访问内存。

4、printf("%d\n", strlen(arr[1]));//arr[1] - 'b' - 98 - err

5、printf("%d\n", strlen(&arr));//随机值,&arr是数组的地址,数组的地址也是指向数组起始位置,跟第一个案例一样,也是随机值。

6、printf("%d\n", strlen(&arr + 1));//&arr+1跳过整个数组,所以是第一个结果随机值-6,也还是随机值。

7、printf("%d\n", strlen(&arr[0] + 1));//&arr[0]+1是'b'的地址,所以地址是从b开始的,是第一个结果随机值-1,也还是随机值。

再总结一下sizeof与strlen的区别:

sizeof 是计算对象或者类型创建的对象所占内存空间的大小,单位是字节

sizeof 是操作符,不是函数。

strlen 求字符串长度的,计算的是字符串中\0之前出现的字符的个数

统计到\0为止,如果没有看到\0,会继续往后找,strlen 是库函数

接下来看看第四道题:

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));

1、printf("%d\n", sizeof(arr));//结果为7,sizeof(arr)是计算数组的总大小,而数组里面是字符串,包含了‘\0’字符,所以为7个。

2、printf("%d\n", sizeof(arr+0));//arr+0是首元素的地址,地址大小为4/8。 

3、printf("%d\n", sizeof(*arr));//arr表示的是首元素的地址,*arr是计算的是首元素的大小。所以首元素的大小为1。

4、printf("%d\n", sizeof(arr[1]));//arr[1]表示的是第二个元素的大小,所以结果为1。

5、printf("%d\n", sizeof(&arr));//&arr是数组的地址,其地址为4/8。

6、printf("%d\n", sizeof(&arr+1));//4/8

7、printf("%d\n", sizeof(&arr[0]+1));4/8

 

看看第5道题: 

char arr[] = "abcdef";
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));

1、printf("%d\n", strlen(arr));//strlen是求‘\0’之前字符串长度的,arr是首元素的地址,从当前开始计算,所以结果为6。

2、printf("%d\n", strlen(arr+0));//6

3、printf("%d\n", strlen(*arr));//err
4、printf("%d\n", strlen(arr[1]));//err

5、printf("%d\n", strlen(&arr));//&arr是数组的地址,数组的地址也是指向数组起始位置,所以结果为6。

6、printf("%d\n", strlen(&arr+1));//&arr+1跳过整个数组,包括字符串后面的‘\0’,所以为随机值

7、printf("%d\n", strlen(&arr[0]+1));//&arr[0]+1是从b开始的地址,所以结果为5。

 看看第六道题

char *p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p+1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p+1));
printf("%d\n", sizeof(&p[0]+1));

printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
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));

 

 

1、 printf("%d\n", sizeof(p));p是一个指针变量存放地址的,所以就为4/8。

2、printf("%d\n", sizeof(p+1));地址加一,由上图为0x0012ff41,所以为4/8。

3、printf("%d\n", sizeof(*p));//1,char*的指针,解引用只能访问一个字节
4、printf("%d\n", sizeof(p[0]));//1,p[0]==*(p+0),第一个元素,所以还是为1个字节。

5、printf("%d\n", sizeof(&p));//&p是取出p的地址,所以为4/8。
6、printf("%d\n", sizeof(&p+1));//4/8
7、printf("%d\n", sizeof(&p[0]+1));//4/8

8、printf("%d\n", strlen(p));//p是指针变量指向a,所以到‘\0’,长度为6。
9、printf("%d\n", strlen(p+1));//p+1指针变量指向b,长度为5。
10、printf("%d\n", strlen(*p));//err
11、printf("%d\n", strlen(p[0]));//err
12、printf("%d\n", strlen(&p));//随机值,假设p的地址为0x0012ff40,从p的地址0x0012ff40中取长度,遇到‘\0’就停止,‘\0’不知道在哪,所以为随机值。
13、printf("%d\n", strlen(&p+1));//随机值,&p+1,跳过p,取地址
14、printf("%d\n", strlen(&p[0]+1));//5
 

再看看第六道题:

int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));

1、 printf("%d\n", sizeof(a));//a是二维数组的数组名,数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节。为48

2、printf("%d\n", sizeof(a[0][0]));//a[0][0]是一个整型元素,大小是4个字节。

3、printf("%d\n", sizeof(a[0]));//把二维数组的每一行看做一维数组的时候,a[0]是第一行的数组名,第一行的数组名单独放在sizeof内部,计算的是第一行的总大小,单位是字节 - 16

4、printf("%d\n",sizeof(a[0]+1));//a[0]虽然是第一行的数组名,但是并非单独放在sizeof内部

a[0]作为第一行的数组名并非表示整个第一行这个数组,a[0]就是第一行首元素的地址,a[0]== &a[0][0] - int*,// //a[0]+1,跳过一个int型,是a[0][1]的地址 4/8字节。

5、printf("%d\n", sizeof(*(a[0] + 1)));//a[0]+1是第一行第二个元素的地址,所以*(a[0]+1)就是a[0][1],大小是4个字节。

6、printf("%d\n", sizeof(a + 1));//a是二维数组的数组名,没单独放在sizeof内部,也没有&,所以a就是数组首元素的地址,二维数组,我们把它想象成一维数组,它的第一个元素就是二维数组的第一行,a就是第一行的地址,a+1 是第二行的地址,是地址,大小就是 4/8 个字节

a - &a[0]

a+1 - &a[1]

a+2 - &a[2]

7、printf("%d\n", sizeof(*(a + 1)));//a+1是第二行的地址,*(a+1) 找到的就是第二行,sizeof(*(a + 1))计算的就是第二行的大小,为16。*(a+1) --> a[1],sizeof(*(a + 1)) --> sizeof(a[1])

8、printf("%d\n", sizeof(&a[0] + 1));//&a[0]是第一行的地址,&a[0]+1就是第二行的地址,sizeof(&a[0] + 1)计算的第二行地址大小,单位是字节 - 4/8。

9、printf("%d\n", sizeof(*(&a[0] + 1)));//&a[0] + 1是第二行的地址,*(&a[0] + 1)拿到的就是第二行,大小就是16个字节。

10、printf("%d\n", sizeof(*a));//a表示首元素的地址,就是第一行的地址 - &a[0],*a - 拿到的就是第一行 - 大小就是16个字节,*a -> *(a+0) -> a[0]

11、printf("%d\n", sizeof(a[3]));//代码没问题

a[3]是二维数组的第4行,虽然没有第四行,但是类型能够确定,大小就是确定的。大小就是一行的大小,单位是字节 - 16,能够分析出 a[3]的类型是:int[4]。

任何一个表达式有两个属性,一个是值属性,一个是类型属性。

任何一个表达式有2个属性

3+5

属性:8

类型属性:int

 

 

 

 

本文含有隐藏内容,请 开通VIP 后查看