【C语言】指针总结(完结篇)

2024-09-25 11:50:15 浏览数 (3)

前言 这篇博客终于迎来了指针博客的大结局,本篇主要分析习题来回顾之前的指针总结的知识点,这篇博客的题有点绕,哈哈算是经典了 个人主页:小张同学zkf 若有问题 评论区见 感兴趣就关注一下吧

1. sizeof和strlen的对比

1.1 sizeof

我们在做指针习题前,再次认识下sizeof()和strlen,很多人分不清,其实这两区别特别大,sizeof 计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。

sizeof 只关注占用内存空间的大小,不在乎内存中存放什么数据。

1.2 strlen

strlen 是C语言库函数,功能是求字符串长度。函数原型如下:

size_t strlen ( const char * str );

统计的是从 strlen 函数的参数 str 中这个地址开始向后, 之前字符串中字符的个数。 strlen 函数会一直向后找 字符,直到找到为止,所以可能存在越界查找。

1.3 sizeof和strlen的对比

2. 数组和指针笔试题解析

以下所有题牢记两个结论: sizeof(数组名)这里的数组名代表着整个数组的大小(sizeof里只有一个数组名!!!) &数组名 这里的数组名表示取整个数组的地址 其他数组名出现都代表首元素地址!!!!!!

2.1 一维数组

int a[] = {1,2,3,4}; printf("%dn",sizeof(a)); printf("%dn",sizeof(a 0)); printf("%dn",sizeof(*a)); printf("%dn",sizeof(a 1)); printf("%dn",sizeof(a[1])); printf("%dn",sizeof(&a)); printf("%dn",sizeof(*&a)); printf("%dn",sizeof(&a 1)); printf("%dn",sizeof(&a[0])); printf("%dn",sizeof(&a[0] 1));

这里面元素都是int类型,一个元素大小4个字节

我们先看第一个,sizeof(a)代表什么意思那,a是数组名在sizeof里只有一个数组名,此时a代表整个元素,一个元素大小是4,则整个元素的大小就是16个字节

sizeof(a 0)此时不仅仅是数组名一个单独在sizeof里,所以这里的a代表数组首元素的地址,那首元素地址加0还是首元素地址,所以此时sizeof里是个地址,大小为4或8个字节

sizeof(*a)此时不仅仅是数组名一个单独在sizeof里,所以这里的a代表数组首元素的地址,那首元素地址解引用,所以此时sizeof里是第一个元素的大小,大小为4个字节

sizeof(a 1)此时不仅仅是数组名一个单独在sizeof里,所以这里的a代表数组首元素的地址,那首元素地址加1,所以此时sizeof里是第2个元素的地址,大小为4或8个字节

sizeof(a[1])此时不仅仅是数组名一个单独在sizeof里,所以这里的a代表数组首元素的地址,那首元素地址后面跟个数组括号,里面有个1,所以此时sizeof里是第2个元素的大小,大小为4个字节

sizeof(&a)此时不仅仅是数组名一个单独在sizeof里,但是别忘了还有一个结论,&后跟数组名代表取整个数组的地址,那整个数组的地址也是地址呀,所以此时sizeof里是地址大小,大小为4或8个字节

sizeof(*&a)&后跟数组名代表取整个数组的地址,但前面那个*解引用号把地址又解引用了,相当于此时此刻只有一个数组名在sizeof里,那此刻数组名就代表整个数组的大小,所以大小为16个字节

sizeof(&a 1)&后跟数组名代表取整个数组的地址,所以这里取整个数组的地址,那&a 1相当于跳了一个数组大小的地址,所以此时sizeof里是地址的大小,大小为4或8个字节

sizeof(&a[0])此时&后跟数组名,但是这里[]优先级比&高,所以a先与后面的【0】成为数组里第一个元素再对它取地址,那&a【0】相当于首元素地址,所以此时sizeof里是地址的大小,大小为4或8个字节

sizeof(&a[0] 1)根据以上推测第一个地址加一相当于第2个地址,既然是地址,那大小为4或8个字节

2.2 字符数组

char arr[] = {'a','b','c','d','e','f'}; printf("%dn", sizeof(arr)); printf("%dn", sizeof(arr 0)); printf("%dn", sizeof(*arr)); printf("%dn", sizeof(arr[1])); printf("%dn", sizeof(&arr)); printf("%dn", sizeof(&arr 1)); printf("%dn", sizeof(&arr[0] 1));

这里是char类型,一个元素大小1个字节

我们先看第一个,sizeof(arr),arr是数组名在sizeof里只有一个数组名,此时arr代表整个元素,一个元素大小是1,则整个元素的大小就是6个字节

sizeof(arr 0),此时不仅仅是数组名一个单独在sizeof里,所以这里的arr代表数组首元素的地址,那首元素地址加0还是首元素地址,所以此时sizeof里是个地址,大小为4或8个字节

sizeof(*arr),此时不仅仅是数组名一个单独在sizeof里,所以这里的arr代表数组首元素的地址,那解引用就是首元素的大小,所以此时sizeof里是个元素大小,大小为1

sizeof(arr[1]),此时不仅仅是数组名一个单独在sizeof里,所以这里的arr代表数组首元素的地址,那首元素地址后面跟个数组括号,里面有个1,所以此时sizeof里是第2个元素的大小,大小为1个字节

sizeof(&arr),&后跟数组名代表取整个数组的地址,所以此时sizeof里是个地址大小,大小为4或8个字节

sizeof(&arr 1),此&后跟数组名代表取整个数组的地址,加一就是跳过一个数组的大小的地址,所以此时sizeof里是个地址大小,大小为4或8个字节

sizeof(&arr【0】 1),&后跟首元素的地址,加一是第2个元素的地址,所以此时sizeof里是个地址大小,大小为4或8个字节


char arr[] = {'a','b','c','d','e','f'}; printf("%dn", strlen(arr)); printf("%dn", strlen(arr 0)); printf("%dn", strlen(*arr)); printf("%dn", strlen(arr[1])); printf("%dn", strlen(&arr)); printf("%dn", strlen(&arr 1)); printf("%dn", strlen(&arr[0] 1));

那如果所有的sizeof换成strlen会是什么样子

strlen(arr)这里的arr代表首元素地址,此时从首元素读取到不知道什么时候读取到,所以此时为随机值

strlen(arr 0)这里的arr代表首元素地址,加0依旧是首元素的地址,此时从首元素读取到不知道什么时候读取到,所以此时为随机值

strlen(*arr)这里的arr代表首元素地址,解引用是一个字符,字符对应的ASC码值强制转换成地址,是个野指针,所以这里读取错误

strlen(arr【1】)这里的arr代表首元素地址,后面【1】代表第2元素大小,字符对应的ASC码值强制转换成地址,是个野指针,所以读取错误。

strlen(&arr)&后跟数组名代表取整个数组的地址,整个数组的地址也是从首元素开始读取,找到结束,所以是随机值。

strlen(&arr 1)跳过一个数组的地址,也是个地址,从这个地址开始读取,到结束,所以是个随机值

strlen(&arr【0】 1)跳过一个元素的地址,从这个地址开始读取,到结束,所以是个随机值


char arr[] = "abcdef"; printf("%dn", sizeof(arr)); printf("%dn", sizeof(arr 0)); printf("%dn", sizeof(*arr)); printf("%dn", sizeof(arr[1])); printf("%dn", sizeof(&arr)); printf("%dn", sizeof(&arr 1)); printf("%dn", sizeof(&arr[0] 1));

来看看字符串

sizeof(arr),arr是数组名在sizeof里只有一个数组名,此时arr代表整个元素,一个元素大小是1,但别忘了字符串结束标志还有一个,则整个元素的大小就是7个字节

sizeof(arr 0),此时不仅仅是数组名一个单独在sizeof里,所以这里的arr代表数组首元素的地址,那首元素地址加0还是首元素地址,所以此时sizeof里是个地址,大小为4或8个字节

sizeof(*arr),此时不仅仅是数组名一个单独在sizeof里,所以这里的arr代表数组首元素的地址,那解引用就是首元素大小,所以此时sizeof大小为1个字节

sizeof(arr【1】),此时不仅仅是数组名一个单独在sizeof里,所以这里的arr代表数组首元素的地址,那首元素地址后面跟个数组括号,里面有个1,所以此时sizeof里是第2个元素的大小,所以此时sizeof大小为1个字节

sizeof(&arr),此&后跟数组名代表取整个数组的地址,所以此时sizeof里是个地址大小,大小为4或8个字节

sizeof(&arr 1),此&后跟数组名代表取整个数组的地址,加一就是跳过一个数组的大小的地址,所以此时sizeof里是个地址大小,大小为4或8个字节

sizeof(&arr【0】 1),&后跟首元素的地址,加一是第2个元素的地址,所以此时sizeof里是个地址大小,大小为4或8个字节


char arr[] = "abcdef"; printf("%dn", strlen(arr)); printf("%dn", strlen(arr 0)); printf("%dn", strlen(*arr)); printf("%dn", strlen(arr[1])); printf("%dn", strlen(&arr)); printf("%dn", strlen(&arr 1)); printf("%dn", strlen(&arr[0] 1));

字符串数组换成strlen是什么情况那

strlen(arr)这里的arr代表首元素地址,此时从首元素读取到就停止,所以此时为6

strlen(arr 0)这里的arr代表首元素地址,加0还是首元素地址,此时从首元素读取到就停止,所以此时为6

strlen(*arr)这里的arr代表首元素地址,解引用是一个字符,字符对应的ASC码值强制转换成地址,是个野指针,所以这里读取错误

strlen(arr【1】)这里的arr代表首元素地址,后面【1】代表第2元素大小,字符对应的ASC码值强制转换成地址,是个野指针,所以读取错误。

strlen(&arr)&后跟数组名代表取整个数组的地址,整个数组的地址也是从首元素开始读取,找到结束,所以是6。

strlen(&arr 1)跳过一个数组的地址,刚好跳过了字符串的,也是个地址,从这个地址开始读取,到结束,所以是个随机值

strlen(&arr【0】 1)跳过一个元素的地址,相当于从第二个元素开始读取,到结束,所以是5


char *p = "abcdef"; printf("%dn", sizeof(p)); printf("%dn", sizeof(p 1)); printf("%dn", sizeof(*p)); printf("%dn", sizeof(p[0])); printf("%dn", sizeof(&p)); printf("%dn", sizeof(&p 1)); printf("%dn", sizeof(&p[0] 1));

当换成指针变量存放字符会是什么结果那

sizeof(p)p是指针变量,指针变量的大小就是4或8个字节

sizeof(p 1)p指针指向的是第一元素指针, 1指向的是第二元素的地址,此刻还是个地址,大小就是4或8个字节

sizeof(*p)p指针指向的是第一元素指针,解引用的是第一个元素的大小,是char类型,大小为1个字节

sizeof(p[0])此时这里面可以把它看成一个数组,因为p指针是首元素的地址,相当于数组名嘛,此刻就是第一个元素的大小,大小为1个字节

sizeof(&p)此时取地址取的是p指针变量这个空间的地址,不是p指向的首元素的地址,相当于char* *p2=&p二级指针,但此刻存放地址的地址也是地址,大小为4或8个字节

sizeof(&p 1)p指针变量的地址加1还是地址,相当于跳了一个char*指针变量的地址,大小为4或8个字节

sizeof(&p[0] 1)取首元素的地址,再加1,相当于第二个元素的地址,大小为4或8


char *p = "abcdef"; printf("%dn", strlen(p)); printf("%dn", strlen(p 1)); printf("%dn", strlen(*p)); printf("%dn", strlen(p[0])); printf("%dn", strlen(&p)); printf("%dn", strlen(&p 1)); printf("%dn", strlen(&p[0] 1));

我们再看看换成strlen

strlen(p),p是首元素的地址,从首元素的地址往后读取到结束,长度为6

strlen(p 1),p是首元素的地址,加1是第二个元素地址,从第2个元素的地址往后读取到结束,长度为5

strlen(*p),p是首元素的地址,*p是第一个元素的大小,相当于字符对应的ASC码值强制转换为地址,是个野指针,则读取错误

strlen(p【0】),把它看成数组,相当于首元素大小,相当于字符对应的ASC码值强制转换为地址,是个野指针,则读取错误

strlen(&p),p指针变量的地址,相当于把p存放的地址读取到结束,但我不知道什么时候结束,所以是随机值

strlen(&p 1),p指针变量的地址,再加一,相当于是从p跳过一个p指针变量的地址,从这个地方把存储的东西读取到结束,但我不知道什么时候结束,所以是随机值

strlen(&p【0】 1),相当于首元素地址加一,是从第二个元素地址开始读取,到结束,大小为5


2.3 二维数组

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

哈哈哈接下来继续上强度,二维数组

sizeof(a)a是二维数组的数组名,当数组名单独存在sizeof()里代表的是整个数组的大小,大小为48个字节

sizeof(a【0】【0】)a不是单独存在那就代表是首元素的地址,后面跟一个数组括号里面是0代表第一行数组数组名,再跟一个数组括号里面是个0就代表第一行数组的首元素大小,大小为4个字节

sizeof(a【0】)a不是单独存在那就代表是首元素的地址,后面跟一个数组括号里面是0代表第一行数组数组名,数组名单独存在就代表这是第一行数组大小,大小为16个字节

sizeof(a【0】 1)a不是单独存在那就代表是首元素的地址,后面跟一个数组括号里面是0代表第一行数组数组名,数组名不是单独存在就代表是第一行首元素地址,加1就代表是第二个元素地址,大小为4或8个字节

sizeof(*(a【0】 1))a不是单独存在那就代表是首元素的地址,后面跟一个数组括号里面是0代表第一行数组数组名,数组名不是单独存在就代表是第一行首元素地址,加1就代表是第二个元素地址,再解引用就是第二个元素大小,大小为4字节

sizeof(a 1)a不是单独存在那就代表是首元素的地址,二维数组的首元素地址就是第一行数组的地址,第一行数组的地址加1就是第二行数组的地址,地址大小就为4或8个字节

sizeof(*(a 1))a不是单独存在那就代表是首元素的地址,二维数组的首元素地址就是第一行数组的地址,第一行数组的地址加1就是第二行数组的地址,再解引用,就是第二行数组名,数组名单独存在就代表第二行数组的大小,是16个字节

sizeof(&a[0] 1)a[0]是第一行数组名,取地址就是第一行的地址,再加一就是第二行地址,地址大小为4或8个字节

sizeof(*(&a[0] 1))a[0]是第一行数组名,取地址就是第一行的地址,再加一就是第二行地址,再解引用是第二行数组大小,大小为16个字节

sizeof(*a)a不是单独存在那就代表是首元素的地址,二维数组的首元素地址就是第一行数组的地址,再解引用就是第一行数组名,又因为第一行数组名单独存在,就代表第一行大小,大小为16个字节

sizeof(a【3】)这里肯定有人疑惑,这不是越界了吗,确实越界了,但sizeof里表达式不运算,所以根本不访问指针指向的对象,依旧可以运行,这里就相当于第四行数组名,数组名单独存在,就代表第四行大小,注意,我并不需要去访问它,只需要知道它的类型是int [4],就可以知道它的大小是16个字节


3. 指针运算笔试题解析

3.1 题目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)); return 0; } //程序的结果是什么?

我们来分析一下,a是首元素地址,加1就是第二个元素的地址,再解引用就是第二个元素,所以第一个答案就是2,ptr是int型指针,&后跟数组名取的是整个数组的地址,加1就是跳了一个数组的地址,注意,此时被强制转换为int*指针,所以再减一,减的是整形大小,相当于现在指向的是第5个元素,再解引用就是第五个元素,所以第二个答案为5


3.2 题目2

//在X86环境下 //假设结构体的⼤⼩是20个字节 //程序输出的结果是啥? struct Test { int Num; char *pcName; short sDate; char cha[2]; short sBa[4]; }*p = (struct Test*)0x100000; int main() { printf("%pn", p 0x1); printf("%pn", (unsigned long)p 0x1); printf("%pn", (unsigned int*)p 0x1); return 0; }

分析第一个,p是结构体类型,加1就是跳了一个结构体类型,一个结构体20个字节,跳了一个就是加20,又因为是16进制数,所以大小为0x100014,但让以地址打印所以去掉符号补零,就是100014前面位置全补零,第二个,此时p强制转换为unsigned long类型,此刻注意它不是指针了,注意,它不是指针了!!是个整型,那加一个整型1就是直接加一,结果就是100001高位补零就行,第三个是整型指针类型,加1就是跳过4个字节,那结果就是100004高位补零就行。


3.3 题目3

#include <stdio.h> int main() { int a[3][2] = { (0, 1), (2, 3), (4, 5) }; int *p; p = a[0]; printf( "%d", p[0]); return 0; }

此刻首先一定要注意里面是小括号(),不是大括号{},是逗号表达式,返回值是最右边的值,那数组里就三个数,1,3,5,a[0]是第一行数组名,代表第一行首元素地址,用指针p表示,那p[0]就是第一行首元素,结果就是1

OK,指针这块就完全结束了,喜欢的各位可以点赞,对指针还是不懂的朋友可以从我的第一篇指针博客再回顾一下。

0 人点赞