数组、指针练习题及解析(含笔试题目讲解)其一(下)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 数组、指针练习题及解析(含笔试题目讲解)其一(下)

字符串

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7

这次我们的数组在初始化时使用的是字符串。有什么不同呢?字符串初始会自动在字符串末尾加上\0作为结束的标志。

这时我们的数组arr里就是{a,b,c,d,e,f,\0},这时使用sizeof计算arr的大小,结果就是7。

sizeof在计算时不管数组的元素是什么,它只管计算数组的大小,arr默认最后添加了一个\0,于是数组arr就变成了7个元素。

printf("%d\n", sizeof(arr+0))//4/8

arr+0,时数组首元素地址,是地址就是占4/8个字节。

printf("%d\n", sizeof(*arr));//1

*arr,表示的是数组arr首元素,是一个字符,占1个字节

printf("%d\n", sizeof(arr[1]));//1

arr[1]是数组第二个元素,是字符b,占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

&arr取的是数组的地址,是地址就占4/8个字节

&arr+1跳过一个数组arr的空间,实质还是一个地址,是地址就占4/8个字节

&arr[0]+1,指的是数组第二个元素的地址,占4/8个字节

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

这里的arr和arr+0都表示的是数组首元素地址,那么使用strlen函数计算的是\0之前的字符个数,那它们的输出结果都是6.

printf("%d\n", strlen(*arr));//error
printf("%d\n", strlen(arr[1]))//error

*arr和arr[1]都表示的是数组arr里的元素,字符当作地址传递给strlen函数会使程序运行时出现错误

printf("%d\n", strlen(&arr));//6
printf("%d\n", strlen(&arr+1));//随机值
printf("%d\n", strlen(&arr[0]+1));//5

这里&arr取的是数组的地址,但数组的地址和数组首元素的地址,值是一样的,那么传递给strlen函数后,依然是从数组的第一个元素的位置开始往后统计,所以输出结果是6。

&arr+1,跳过了整个数组arr,从arr数组最后的\0后边开始向后统计字符串长度,所以输出的结果将会是一个随机值。

&arr[0]+1,是数组第二个元素的地址,从第二个元素开始向后统计,所以输出结果是5

字符指针

char *p = "abcdef";
printf("%d\n", sizeof(p));//4/8
printf("%d\n", sizeof(p+1));//4/8

这里的指针变量p里边存的是字符a的地址(字符串在内存中存储是连续的),我们前边总结过:

sizeof在计算时,它只在乎数据的类型。

p它是char*类型的指针,是指针那它就占4/8个字节

那p+1,表示的就是字符b的地址,是指针那它就占4/8个字节

printf("%d\n", sizeof(*p));//1
printf("%d\n", sizeof(p[0]));//1

*p就是就是字符a,是char类型,占1个字节。

p[0],就等价于*(p+0),p+0指向的还是字符a,所以p[0]也是字符a,char类型,占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

p是char*类型,那&p就是char**是一个二级指针,是指针那它就占4/8个字节。

&p+1,也是一个二级指针,也是占4/8个字节,那我们思考一下,&p+1指向的是哪里呢?

如上图,&p+1指向的是&p的后边,且相邻。

&p[0]+1,这个很好理解,&p[0]是首元素a的地址,+1指向的就是字符b的地址,所占空间也是4/8个字节。

printf("%d\n", strlen(p));//6
printf("%d\n", strlen(p+1));//5

这次我们换用为strlen,我们知道,字符串在内存中存储时是连续的,且会在字符串的末尾加上\0,作为结束的标志。

p指向的是字符串首元素a的地址,向后统计字符个数,所以strlen(p)的结果是6.

p+1指向的是b的地址,从字符串第二字符的位置开始向后统计字符个数,输出结果是5.

printf("%d\n", strlen(*p));//error
printf("%d\n", strlen(p[0]));//error

*p和p[0]都是字符a,我们前边了解到,将字符作为地址传给strlen函数在程序运行时会出现错误。

printf("%d\n", strlen(&p));//随机值
printf("%d\n", strlen(&p+1));//随机值
printf("%d\n", strlen(&p[0]+1));//5

&p我们知道是一个二级指针,它存放的只是指针p的地址,并不是字符串abcdef,所以在从&p开始向后统计字符个数时,无法得知字符个数,因此为随机值

&p+1与&p同理,所以输出也是一个随机值

&p[0]+1,p[0]是字符串首元素,&p[0]等价于p,所以&p[0]+1就是指向字符串第二个字符b,从第二个字符开始向后统计字符个数,输出结果就是5

二维数组

int a[3][4] = {0};
printf("%d\n",sizeof(a));//48

二维数组也是数组,将数组名单独放在sizeof内部,结果是什么呢?

siaeof(a)表示中a表示整个数组,3行4列,12个元素,一个元素占4个字节,所以数组a的大小就是48个字节

printf("%d\n",sizeof(a[0][0]));//48
printf("%d\n",sizeof(a[0]));//16
printf("%d\n",sizeof(a[0]+1));//4/8

a[0][0],表示的就是1行1列的元素,是一个整形数字,占4个字节

a[0],在对二维数组的理解中,我们可以将二维数组理解为一个存放一维数组的数组。

a[0]代表的是第一行一维数组的数组名,a[1]代表第二行一维数组的数组名,a[2]代表第三行一维数组的数组名。

而每个数组又有4个整形的空间,所以a[0]所占的内存大小就是占16个字节。

我们再来看a[0]+1,首先a[0]作为第一行的数组名没有单独放在sizeof内部,没有&,所以a[0]表示的是第一行数组的首元素地址,那么a[0]+1,指向的就是第一行第二列元素(a[0][1])的地址。

它是地址,那就占4/8个字节

printf("%d\n",sizeof(*(a[0]+1)));//4

理解了a[0]+1指向的是第一行第二列元素(a[0][1])的地址,这里就很简单了,解引用就是第一行第二列的元素(a[0][1]),占4个字节

printf("%d\n",sizeof(a+1));//4/8
printf("%d\n",sizeof(*(a+1)));//16

a+1,我们前边提到可以将a[3][4]理解为一个存放数组的数组,即a[3][4]={ a[0] , a[1] , a[2] };

并且没有单独将a放在sizeof内部,所以a表示的就是第一行的数组的地址(数组首元素的地址),a+1跳到相邻元素,所以a+1就是第二行的地址,占4/8个字节

*(a+1)

两种理解思路

1. *(a+1)等价于a[1],也表示的是第二行的数组,所以占16个字节

2. a+1表示第二行数组的地址,类型为int (*)[4],解引用就是一个4个整形空间大小的数组,占16个字节。

printf("%d\n",sizeof(&a[0]+1));//4/8
printf("%d\n",sizeof(*(&a[0]+1)));//16

a[0]是第一行的数组,&a[0]就是第一行的地址,&a[0]+1也等价于a+1,表示的就是第二行的地址,占4/8个字节

将&a[0]+1解引用就是第二行的数组,占16个字节

printf("%d\n",sizeof(*a));//16
printf("%d\n",sizeof(a[3]));//16

sizeof(*a),并没有将a单独放在sizeof内部,所以这里的a表示的就是数组首元素地址,二维数组首元素地址就是第一行数组的地址,再解引用就是第一行的大小,所以占16个字节

sizeof(a[3]),这里我们可以发现数组越界了,但这并不影响,sizeof计算只关注数据类型。

a[3]也就相当于是二维数组中的第四行,不管第四行是否催在,所能存储的类型都只能是int [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));//2,5
  return 0;
}
//程序的结果是什么?

题目分析:

ptr是一个指针,赋值为(int*)(&a+1),&a我们知道取出的是整个数组的地址,&a+1指向的位置如图所示:

在将int (*)[5]强制类型转换为int*类型。

输出:

*(a+1),a表示数组首元素地址,a+1就是数组第二个元素的地址,*(a+1)就是数组第二个元素,也就是2.

*(ptr - 1),我们已经知道ptr所指向的位置,并且强制转换为int*类型,那ptr+1或-1就只会跳过一个整形的空间。那ptr-1就是数组第五个元素的地址,所以*(ptr - 1)就是数组第五个元素,也就是5.

笔试题2:

struct Test
{
int Num;
char *pcName;
short sDate;
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;
}

由于未向大家详细具体的介绍结构体,这里先告知大家结构体大小是20个字节(32位操作系统)

假设p 的值为0x100000,即p=(struct Test*)0x100000

这道题考察的就是指针+1跳过的空间大小。

输出:

p + 0x1,p是结构体指针,地址+1根据数据类型不同跳过的空间大小不同,我们已知结构体大小是20个字节。所以输出为:0x100014,14转化为10进制就是20。

(unsigned long)p + 0x1,将p转化为无符号长整形,那这里就是正常的整数相加,输出也就是0x100001

(unsigned int*)p + 0x1,将结构体类型的指针转换为无符号整形的指针,+1就变成了一次跳过4个字节,所以输出也就是0x100004


总结

考虑到文章长度问题,本期博客到此结束,预知后事如何,见下期博客,最后感谢阅读!

相关文章
|
1月前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
38 3
|
29天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
1月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
1月前
|
容器
在使用指针数组进行动态内存分配时,如何避免内存泄漏
在使用指针数组进行动态内存分配时,避免内存泄漏的关键在于确保每个分配的内存块都能被正确释放。具体做法包括:1. 分配后立即检查是否成功;2. 使用完成后及时释放内存;3. 避免重复释放同一内存地址;4. 尽量使用智能指针或容器类管理内存。
|
1月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
11天前
|
存储 程序员 C++
深入解析C++中的函数指针与`typedef`的妙用
本文深入解析了C++中的函数指针及其与`typedef`的结合使用。通过图示和代码示例,详细介绍了函数指针的基本概念、声明和使用方法,并展示了如何利用`typedef`简化复杂的函数指针声明,提升代码的可读性和可维护性。
40 0
|
1月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
60 4
|
1月前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
50 2
|
1月前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
39 1
|
2月前
|
人工智能 前端开发 JavaScript
拿下奇怪的前端报错(一):报错信息是一个看不懂的数字数组Buffer(475) [Uint8Array],让AI大模型帮忙解析
本文介绍了前端开发中遇到的奇怪报错问题,特别是当错误信息不明确时的处理方法。作者分享了自己通过还原代码、试错等方式解决问题的经验,并以一个Vue3+TypeScript项目的构建失败为例,详细解析了如何从错误信息中定位问题,最终通过解读错误信息中的ASCII码找到了具体的错误文件。文章强调了基础知识的重要性,并鼓励读者遇到类似问题时不要慌张,耐心分析。

推荐镜像

更多