掌握指针和数组:经典笔试题攻略(万字详解)(上)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 掌握指针和数组:经典笔试题攻略(万字详解)(上)


前言



  • 当涉及到计算机编程的核心概念时,指针和数组无疑是最重要和基础的话题之一。无论你是初学者还是有经验的开发者,深入理解和掌握这两个概念都能让你的编程技能得到质的提升。
    在本篇博客中,我们将带你进入指针和数组的精彩世界,探索其在编程中的作用和应用。无论你是为了笔试准备,还是为了提升自己的技能,这里都将为你提供一个深入学习的机会。

在本篇文章中如有遇到不懂的知识点,可以参考以下文章《掌握指针进阶:探索字符指针、数组指针和指针数组的妙用》《掌握指针进阶:一篇带你玩转函数指针、函数指针数组及指向函数指针数组的指针!!》希望对你有所帮助


注意: 本次讲解是在vs2022 x86的环境下进行的,即电脑的32位环境,所以默认为指针大小占4个字节


📍指针和数组笔试题



🚀一维数组


//一维数组
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));


以上语句输出的结果是什么?结果如下:

e16dc3d8db3544dcb722784d5bb68dd6.png


【解析】:

int main()
{
  //一维数组
  int a[] = { 1,2,3,4 };
  //数组中有4个元素,每个元素的类型为int类型,所以数组所占字节大小为4*4=16
  printf("%d\n", sizeof(a));
  //sizeof里面的a表示的是整个数组,所以这里求的是整个数组的大小,即4*4=16
  printf("%d\n", sizeof(a + 0));
  //a+0 其实是下标为0的元素的地址(即数组第一个元素的地址),是地址就是4/8字节,这里是32位环境,所以大小是4个字节
  printf("%d\n", sizeof(*a));
  //*a是数组首元素,计算的是数组首元素的大小,单位是字节,a为int类型,所以大小为4个字节
  printf("%d\n", sizeof(a + 1));
  //a为首元素地址,+1表示下表为1的元素地址(即第二个元素的地址),地址大小为4个字节
  printf("%d\n", sizeof(a[1]));
  //a[1]表示第二个元素,计算的是第二个元素的大小,大小为4个字节
  printf("%d\n", sizeof(&a));
  //&a是整个数组的地址,整个数组的地址也是地址,地址的大小为4个字节
  //&a---> 类型:int(*)[4] (数组指针类型)
  printf("%d\n", sizeof(*&a));
  //&a是数组的地址,*&a就是拿到了数组,*&a --> a,a就是数组名,sizeof(*&a)-->sizeof(a)
  //计算的是整个数组的大小,单位是字节,即大小是4*4 = 16个字节
  printf("%d\n", sizeof(&a + 1));
  //&a是整个数组的地址,&a+1,跳过整个数组,指向数组后边的空间,是一个地址,大小是4个字节
  printf("%d\n", sizeof(&a[0]));
  //&a[0]是首元素的地址,计算的是首元素地址的大小,为4个字节
  printf("%d\n", sizeof(&a[0] + 1));
  //&a[0]表示第一个元素的地址,+1就是第二个元素的地址,地址的大小为4个字节
  return 0;
}
//sizeof 类型:----> size_t ----> unsigned int


这里我们需要知道:

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

2. &数组名,数组名表示整个数组。取出的是整个数组的地址,+1跳过的是整个数组。

  • 除此之外,所有的数组名都是数组首元素的地址


🚀字符指针


🍁sizeof()的计算


//字符数组 ---> sizeof()
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));


以上语句输出的结果是什么?结果如下:

8f5c68c23de142298aa80d8f60383d4d.png


【解析】:

int main()
{
  char arr[] = { 'a','b','c','d','e','f' };
  printf("%d\n", sizeof(arr));
  //arr单独放在sizeof内部,计算的是整个数组的大小,单位是字节,所以大小为 6*1 = 6个字节
  printf("%d\n", sizeof(arr + 0));
  //arr表示首元素地址,+0表示下标为0的元素地址(即数组首元素的地址),大小为4个字节
  printf("%d\n", sizeof(*arr));
  //arr是数组的首元素的地址,*arr表示对arr解引用,得到字符'a',大小为1字节
  printf("%d\n", sizeof(arr[1]));
  //arr[1]是第二个元素,因为数组元素为char类型,所以大小为1字节
  printf("%d\n", sizeof(&arr));
  //&arr表示取出整个数组的地址,数组的地址也是地址,大小为4个字节
  printf("%d\n", sizeof(&arr + 1));
  //&arr表示取出整个数组的地址,+1表示跳过整个数组,指向数组后边空间(即'f'后面)的地址,大小为4个字节
  printf("%d\n", sizeof(&arr[0] + 1));
  //&arr[0] + 1表示数组第二个元素的地址,大小为4个字节
  return 0;
}


注意: 如果 char arr[] = "abcdef"; 这里的arr数组有7个元素(加上一个’\0’),编译器会默认为arr数组末尾添加结束标志‘\0’,所以计算数组大小时结果不再是6而是7。

212b04bf9bae4199a3a7e46539d8ac62.png

当使用指针存放字符串又会怎样?

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));


以上语句输出的结果是什么?结果如下:

81256dda0cf041e7a5c18722cf0d6ef5.png


【解析】:

int main()
{
  char* p = "abcdef";
  printf("%d\n", sizeof(p));
  //p是存放字符串"abcdef"的指针变量,是指针,所以大小为4个字节
  printf("%d\n", sizeof(p + 1));
  //p表示首元素的地址,+1表示下表为1的元素的地址(即b的地址),大小为4个字节
  printf("%d\n", sizeof(*p));
  //p表示首元素的地址,对p解引用,*p就是字符'a',sizeof(*p)计算的是字符的大小,是1个字节
  printf("%d\n", sizeof(p[0]));
  //p[0]-->*(p+0) --> *p  表示第一个元素'a',大小是1个字节
  printf("%d\n", sizeof(&p));
  //&p是二级指针,是指针大小为4个字节
  printf("%d\n", sizeof(&p + 1)); 
  //&p是二级指针,+1跳过的是p变量后(即指针大小为4个字节)的地址,因为还是指针,所以大小为4个字节
  printf("%d\n", sizeof(&p[0] + 1));
  //p[0]就是‘a’,&p[0]就是a的地址,+1,就是b的地址,是地址大小为4个字节
  return 0;
}


🍁strlen的计算


//size_t strlen(const char* str)
//strlen返回类型为size_t ----> unsigned int
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));


输出结果如下:

14d0e24453e2420ca3a1b17945733e37.png


【解析】:

int main()
{
  //size_t strlen(const char* str)
  //strlen返回类型为size_t ----> unsigned int
  char arr[] = { 'a','b','c','d','e','f' };
  printf("%d\n", strlen(arr));
  //strlen统计字符串长度时直到遇到 '\0'才停止,但是在初始化arr时我们没有加上'\0',
  //即在arr数组中我们不知道'\0'的位置,所以这里计算的结果是随机值
  printf("%d\n", strlen(arr + 0));
  //这里计算的是从下标为0的元素位置开始计算字符串长度,由于无法得知结束标志'\0'的位置,所以结果同上,为随机值
  //printf("%d\n", strlen(*arr)); ---> //strlen需要接收的是一个地址,strlen('a') -> strlen(97),非法访问 - error
  //printf("%d\n", strlen(arr[1])); ---> //'b'->98,和上面的代码类似,是非法访问 - error
  printf("%d\n", strlen(&arr));
  //&arr虽然是整个数组的地址,但是也是从数组起始位置开始的,计算的结果同第一个一样还是随机值
  //&arr类型 ---> char(*)[6](数组指针类型)
  printf("%d\n", strlen(&arr + 1));
  //&arr是整个数组的地址,&arr+1是跳过整个数组的地址即'f'后面的地址,计算的是'f'后面的字符串长度,
  //结果也是随即值,但是这个随机值比第一个少6,少的是字符串"abcdef"
  printf("%d\n", strlen(&arr[0] + 1));
  //&arr[0]表示的是第一个元素的地址, + 1表示第二个元素的地址,是'b'的地址,计算的是从'b'开始的字符串的长度,
  //结果也是随机值,但是这个随机值比第一个少1,少的是字符'a'
  return 0;
}


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));
return 0;


输出结果如下:

de66b9c2c8924ca6800bb38185d7c9d6.png

【解析】:

int main()
{
  char arr[] = "abcdef";//数组是7个元素
  //[a b c d e f \0]
  printf("%d\n", strlen(arr));
  //arr是数组首元素的地址,strlen从首元素的地址开始统计'\0'之前出现的字符个数,结果是6
  printf("%d\n", strlen(arr + 0));
  //arr + 0是数组首元素的地址,同第一个,结果是6
  //printf("%d\n", strlen(*arr));//*arr是'a',ASC II码值是97,传给strlen是一个非法的地址,造成非法访问
  //printf("%d\n", strlen(arr[1]));//'b' --> 98 ,同上
  printf("%d\n", strlen(&arr));
  //&arr虽然是整个数组的地址,但是也是从数组起始位置开始的,计算结果同第一个是6
  printf("%d\n", strlen(&arr + 1));
  //&arr + 1是跳过整个数组后的地址,统计字符串的长度是随机值
  printf("%d\n", strlen(&arr[0] + 1));
  //&arr[0]+1是b的地址,从第二个字符往后统计字符串的长度,大小是5
  return 0;
}


char* p = "abcdef";
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));

输出结果如下:

fcffadca9e2e4fa2a6bfaf6db0256426.png

【解析】:

int main()
{
  char* p = "abcdef";
  printf("%d\n", strlen(p));
  //6- 求字符串长度
  printf("%d\n", strlen(p + 1));
  //p + 1是b的地址,求字符串长度就是5
  //printf("%d\n", strlen(*p));//error,*p是'a'
  //printf("%d\n", strlen(p[0]));//error --> 同上一个
  printf("%d\n", strlen(&p));
  //&p拿到的是p这个指针变量的起始地址,从这里开始求字符串长度完全是随机值
  printf("%d\n", strlen(&p + 1));
  //&p+1是跳过p变量的地址,从这里开始求字符串长度也是随机值
  printf("%d\n", strlen(&p[0] + 1));
  //&p[0]表示'a'的地址, + 1是b的地址,从b的地址向后数字符串的长度是5
  return 0;
}


🍁总结


  • sizeof只关注占用内存空间的大小,单位是字节,不关心内存中存放的是什么
    sizeof 操作符
  • strlen是求字符串长度的,统计的是\0之前出现的字符个数,一定要找到 ‘\0’ 才算结束,所以可能存在越界访问的
    strlen库函数


🚀二维数组


//二维数组
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]));


以上语句输出的结果是什么?结果如下:

b53961d92ddf495fa2714b442e9ed05e.png


【解析】:

int main()
{
  //二维数组
  int a[3][4] = { 0 };
  printf("%d\n", sizeof(a));
  //数组名单独放在sizeof内部,a表示整个数组的地址,计算的是整个数组的大小,整个数组有3*4=12个元素,
  //每个元素为int类型,所以计算的大小为 3*4*4 = 12*4 = 48个字节
  printf("%d\n", sizeof(a[0][0]));
  //a[0][0]表示第一行第一列的元素(即数组首元素),大小为4个字节
  printf("%d\n", sizeof(a[0]));
  //a[0]是二维数组第一行的数组名,数组名单独放在sizeof内部,计算的就是数组(第一行)的大小,为4*4 = 16个字节
  printf("%d\n", sizeof(a[0] + 1));
  //a[0]作为第一行的数组名,没有单独放在sizeof内部,没有取地址,表示的就是数组首元素的地址
  //那就是a[0][0]的地址,a[0]+1就是第一行第二个元素的地址,是地址,计算的大小就是4个字节
  printf("%d\n", sizeof(*(a[0] + 1)));
//a[0] + 1表示的是第一行第二个元素的地址,对其解引用,*(a[0] + 1)就是是第一行第二个元素,计算的是元素的大小,为 4个字节
  printf("%d\n", sizeof(a + 1));
  //a是二维数组的数组名,数组名表示首元素的地址,就是第一行的地址,a+1就是第二行的地址
  //第二行的地址也是地址,是地址,计算的大小就是4个字节   
  //a 类型: --> int (*)[4](数组指针类型)
  //a+1 类型: --> int(*)[4](数组指针类型)
  printf("%d\n", sizeof(*(a + 1)));
  //a+1是第二行的地址,对其解引用,*(a+1)表示的就是第二行,*(a+1) --> a[1]
  //因为第二行有4个元素,每个元素为int类型,所以计算大小为 4*4 =16个字节
  printf("%d\n", sizeof(&a[0] + 1));
  //&a[0]是第一行的地址,&a[0]+1就是第二行的地址,是地址,大小就是4个字节
  printf("%d\n", sizeof(*(&a[0] + 1)));
  //*(&a[0] + 1) 是对第二行的地址解引用,得到的就是第二行,计算的就是第二行的大小,为4*4 = 16个字节
  printf("%d\n", sizeof(*a));
  //a表示首元素的地址,就是第一行的地址,*a就是第一行,计算的就是第一行的大小,为 4*4 = 16个字节
  //*a --> *(a+0) --> a[0]
  printf("%d\n", sizeof(a[3]));
  //结果是16个字节 <-- int[4]
  //如果数组存在第四行,a[3]就是第四行的数组名,数组名单独放在sizeof内部,计算的是第四行的大小,为 4*4 =16个字节
  return 0;
}


目录
相关文章
|
20天前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
30 3
|
19天前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
30 2
|
28天前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
32 1
|
1月前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。
|
1月前
|
存储
如何通过指针数组来实现二维数组?
介绍了二维数组和指针数组的概念及其区别,详细讲解了如何使用指针数组模拟二维数组,包括定义与分配内存、访问和赋值元素、以及正确释放内存的步骤,适用于需要动态处理二维数据的场景。
|
1月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
1月前
魔法指针 之 二级指针 指针数组
魔法指针 之 二级指针 指针数组
19 1
|
1月前
|
存储
一篇文章了解区分指针数组,数组指针,函数指针,链表。
一篇文章了解区分指针数组,数组指针,函数指针,链表。
19 0
|
1月前
|
编译器 C语言
【C语言】指针篇-深入探索数组名和指针数组- 必读指南(2/5)
【C语言】指针篇-深入探索数组名和指针数组- 必读指南(2/5)
|
3月前
|
搜索推荐 C语言
指针与数组
指针与数组
59 9
下一篇
无影云桌面