指针和数组笔试题深度解析(上)一

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

前言


本文带大家详细了解 sizeof 与 strlen 在指针和数组方面的应用,让大家不再傻傻分不清楚,提高大家对其的理解。(注:以下代码均在x86 (32位) 环境下运行)


核心要点


数组名的意义:


1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。

2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。

3. 除此之外所有的数组名都表示首元素的地址。


sizeof 与 strlen 的作用:


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

sizeof 是操作符,不是函数, sizeof 不关注 \0

strlen 求字符串长度的,从给定的地址出发计算字符串中 \0 之前出现的字符的个数,统计到 \0 为止,如果没有看到 \0,会继续往后找

strlen 是库函数,使用需要包含头文件


指针加一跳多长?


char *p;        //一级指针   *p里的*说明p是指针,指向的类型是char型
char* *pp=&p;   //二级指针   *pp里的*说明pp是指针,指向的类型是char*型


p+1跳过的是一个char型对象

pp+1跳过的是一个char*型对象


指针加一跳多长,取决于指针指向对象的类型


一维数组


下面一段代码大家可以先试着做一做,想一想


#include<stdio.h>
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;
}


输出:


101c57f0c35f453d8963a937b2d2ef31.png


下面我们来逐个分析:


int a[] = {1,2,3,4};       //数组a有4个元素,每个元素占4个字节,
  printf("%d\n",sizeof(a));      // 16
    //a作为数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节
  printf("%d\n",sizeof(a+0));   // 4/8
  //a并非单独放在sizeof内部,也没有&,所以数组名a就是数组首元素的地址
  //a+0还是数组首元素的地址,是地址其大小就是 4/8 个字节(x86是4字节,x64是8字节)
  printf("%d\n",sizeof(*a));    // 4
  //a是首元素的地址,*a就是首元素,sizeof(*a)算的就是首元素的大小 
  printf("%d\n",sizeof(a+1));   // 4/8
  //a是首元素的地址,a+1是第二个元素的地址,sizeof(a+1)计算的是指针也就是地址大小
  printf("%d\n",sizeof(a[1]));  // 4
  //a[1]就是数组的第二个元素,sizeof(a[1])的大小 - 4个字节
  printf("%d\n",sizeof(&a));   // 4/8
  //&a取出的整个数组的地址,数组的地址,也是地址呀,sizeof(&a)就是 4/8 个字节
  printf("%d\n",sizeof(*&a));  // 16
  //&a是数组的地址,是数组指针类型,*&a是数组指针解引用,访问一个数组的大小
  //sizeof(*&a) ==> sizeof(a)  =16
  printf("%d\n",sizeof(&a+1));  // 4/8
  //&a数组的地址,&a+1跳过整个数组,&a+1还是地址,是 4/8 个字节
  printf("%d\n",sizeof(&a[0]));  // 4/8
  //a[0]是数组的第一个元素,&a[0]是第一个元素的地址,是 4/8 个字节
  printf("%d\n",sizeof(&a[0]+1));  // 4/8
  //&a[0]是第一个元素的地址,&a[0]+1就是第二个元素的地址,是 4/8 个字节


字符数组


类型一


有了以上讲解相信大家对下面代码应该会得心应手


#include<stdio.h>
int main()
{
  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));
  return 0;
}


输出:

928daa8738074d03b574333989189f49.png

分析:


char arr[] = { 'a','b','c','d','e','f' };  //每个 char 型字符占一个字节,整个数组占6个字节
  printf("%d\n", sizeof(arr));      // 6
  //arr是数组名,并且是单独放在sizeof内部,计算的是数组总大小,单位是字节 - 6
  printf("%d\n", sizeof(arr + 0));  // 4/8
  //arr是数组名,并非单独放在sizeof内部,arr表示首元素的地址,arr+0还是首元素的地址
  printf("%d\n", sizeof(*arr));     // 1
  //arr是首元素的地址,*arr就是首元素,sizeof计算的是首元素的大小,是1字节
  printf("%d\n", sizeof(arr[1]));   // 1
  printf("%d\n", sizeof(&arr));     // 4/8   
  //&arr-取出的是数组的地址,sizeof(&arr)计算的是数组的地址的大小,是地址就是4/8字节
  printf("%d\n", sizeof(&arr + 1));  // 4/8
  //&arr是数组的地址,&arr+1跳过整个数组,指向'f'的后边,&arr+1的本质还是地址
  printf("%d\n", sizeof(&arr[0] + 1)); // 4/8
  //&arr[0]是‘a’的地址,&arr[0]+1是'b'的地址,是地址就是4/8字节


我们再来看看 strlen 函数的使用:


#include<stdio.h>
#include<string.h>
int main()
{
  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));
  return 0;
}


输出:


18c009d843ad44e3aa20423568c4f0d4.png


为什么会出现这种情况呢?strlen(*arr) 有什么问题呢?


arr是数组首元素的地址,*arr 是首元素 - ‘a’ - 97

strlen就把 ‘a’ 的ASCII码值 97 当成了地址

导致非法访问内存,也就是 err(注:像这种地址都是不能访问的)


它下面的一段代码是 arr[1] ,也就是 ‘b’ - 98,也是不能访问的。

我们将它俩屏蔽再来看看结果:


aeb798904236474d905028c183e3c3aa.png


不知道大家有没有发现好像两次输出的结果有点小差异,第一次输出是:23 23,这与19 19不同,为什么呢?

我们来分析一下:


char arr[] = { 'a','b','c','d','e','f' };
  printf("%d\n", strlen(arr));    //随机值
  //arr是数组名,但是没有放在sizeof内部,也没&,arr就是首元素的地址
  //strlen得到arr后,从arr数组首元素的地方开始计算字符串的长度,直到直到\0,但是arr数组中没有\0,arr内存的后边是否有\0,在什么位置是不确定的,所以\0之前出现了多少个字符是随机的。
  printf("%d\n", strlen(arr + 0));  //随机值
  //arr是数组首元素的地址,arr+0还是首元素的地址
   //printf("%d\n", strlen(*arr));
   //printf("%d\n", strlen(arr[1]));
  printf("%d\n", strlen(&arr));  //随机值
  //&arr是数组的地址,数组的地址也是指向数组起始位置,和第一个案例一样
  printf("%d\n", strlen(&arr + 1));   //随机值-6
  //跳过整个数组,整个数组大小为6个字节,所以在随机值的基础上-6
  printf("%d\n", strlen(&arr[0] + 1));  //随机值-1
  //&arr[0] + 1是第二个元素地址,所以是随机值-1

相关文章
|
22天前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
31 3
|
21天前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
31 2
|
29天前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
32 1
|
2月前
|
人工智能 前端开发 JavaScript
拿下奇怪的前端报错(一):报错信息是一个看不懂的数字数组Buffer(475) [Uint8Array],让AI大模型帮忙解析
本文介绍了前端开发中遇到的奇怪报错问题,特别是当错误信息不明确时的处理方法。作者分享了自己通过还原代码、试错等方式解决问题的经验,并以一个Vue3+TypeScript项目的构建失败为例,详细解析了如何从错误信息中定位问题,最终通过解读错误信息中的ASCII码找到了具体的错误文件。文章强调了基础知识的重要性,并鼓励读者遇到类似问题时不要慌张,耐心分析。
|
2月前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。
|
2月前
|
存储
如何通过指针数组来实现二维数组?
介绍了二维数组和指针数组的概念及其区别,详细讲解了如何使用指针数组模拟二维数组,包括定义与分配内存、访问和赋值元素、以及正确释放内存的步骤,适用于需要动态处理二维数据的场景。
|
2月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
2月前
魔法指针 之 二级指针 指针数组
魔法指针 之 二级指针 指针数组
19 1
|
2月前
|
存储
一篇文章了解区分指针数组,数组指针,函数指针,链表。
一篇文章了解区分指针数组,数组指针,函数指针,链表。
19 0

推荐镜像

更多
下一篇
无影云桌面