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

简介: 指针和数组笔试题深度解析(上)

类型二


废话不多说上代码:


#include<stdio.h>
int main()
{
  char arr[] = "abcdef";        //看清楚是 sizeof  
  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;
}


输出:


d777f752083c42429c96b8b823924a65.png


相信大家应该挺疑惑的吧,为什么会是 7 不是 6 呢?


首先我们得知道,"abcdef"里面存了啥?

其实在这个字符串最后面有一个 \0 没有写出来

写成大括号的形式就是{‘a’,‘b’,‘c’,‘d’,‘e’,‘f’,‘\0’}

我们再看 sizeof 的用法:strlen 求字符串长度的,计算的是字符串中 \0 之前出现的字符的个数,统计到 \0 为止,如果没有看到 \0,会继续往后找

如此想必大家都明白了吧。


分析:


char arr[] = "abcdef";       
  printf("%d\n", sizeof(arr));      // 7
  printf("%d\n", sizeof(arr + 0));  // 4/8
  //首元素地址
  printf("%d\n", sizeof(*arr));    // 1
  //arr[0]的大小
  printf("%d\n", sizeof(arr[1]));  // 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


再来看关于 strlen 的:


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


看输出:


c91a8455c77747989256273cc4a65cf7.png


分析:


printf("%d\n", strlen(arr));      // 6        arr是整个数组地址
  printf("%d\n", strlen(arr + 0));  // 6        arr+0是首元素地址
  printf("%d\n", strlen(&arr));     // 6        &arr是整个数组地址
  printf("%d\n", strlen(&arr + 1));  //随机值   &arr+1是跳过整个数组后的地址
  printf("%d\n", strlen(&arr[0] + 1));  //5     &arr[0] + 1是第二个元素的地址


类型三


#include<stdio.h>
int main()
{
  const char* p = "abcdef";          //p存放的是字符串首元素 —‘a’的地址,可以通过该地址找到后面的元素
  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));
  return 0;
}


输出:


0fd506a806ea4621a0052c3a2ac4e4ae.png


分析:


printf("%d\n", sizeof(p));       // 4/8
  //p存放的是字符串首元素 —‘a’的地址,地址大小就是 4/8
  printf("%d\n", sizeof(p + 1));   // 4/8    ‘b’的地址
  printf("%d\n", sizeof(*p));      // 1      首元素‘a’的大小为一字节
  printf("%d\n", sizeof(p[0]));    // 1      首元素‘a’的大小为一字节
  printf("%d\n", sizeof(&p));      // 4/8    取出首元素地址
  printf("%d\n", sizeof(&p + 1));  // 4/8    ‘b’的地址
  printf("%d\n", sizeof(&p[0] + 1));  // 4/8   ‘b’的地址


再上strlen:


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


输出:


d4e3dd0af6094901bafbec96ae31cbe9.png


是不是没有想到呢,我们来分析一下:


const char* p = "abcdef";  
  printf("%d\n", strlen(p));    //6   首元素地址
  printf("%d\n", strlen(p + 1));   //5   第二个元素地址
  printf("%d\n", strlen(&p));     //随机值
  //p是首元素地址,那么&p就是把 p 的地址取出来,strlen从 p 的地址处开始找\0,见下图
  printf("%d\n", strlen(&p + 1));   //随机值
  printf("%d\n", strlen(&p[0] + 1));  //5  第二个元素地址


假设‘a’的地址是 0x0012ff40 ,那么p里存放的就是该地址,如下图:


b57c04b2f23c47ca80ca6b0b6d064af2.png


strlen(&p)就是把p的地址放进去,然后从p的地址处开始找 \0 ,如图


734214ad193d43c89b111186163a81b1.png


图中标出&p的位置,从该处往后找 \0 ,地址是占4个或8个字节,我们这里用x86,占4个字节,那里面的数是怎么样的呢?(假设我们是小端存储,如有不懂请看往期)


1d2337917b1641ed8f61d258e9578fdd.png


我们可以看到第四个字节位置是00,也就是 \0 ,strlen应该是读到这里为止,那么strlen(&p)应该为3,这是在‘a’的地址确定的情况下,问题是我们不知道‘a’的地址是多少,在每次测试时都有可能不同,所以就是随机值了。strlen(&p + 1)也是同样的道理。


二维数组


首先来补充一点:(假设有个二维数组 int arr[3][4])


arr 也可以看做另类的一维数组,我们可以把每行当成一个元素,这样第一行就可以用 a[0]来表示,第二行用 a[1]来表示,以此类推(a[0]、a[1]若是单独放在sizeof里面,或者前面加了个&,则a[0]、a[1]的类型才是int(*)[4],才能代表一行元素的地址,否则只能代表一行的首元素地址。如有疑惑看往期—指针进阶),那么每一行里的具体元素怎么表示呢?我们可以再加个括号即可,如:第一行第二个元素,我们可以写成:a[0][1]也可以写成 *(a[0]+1) 还可以写成 * ( *(a+0)+1)来表示,为什么呢?

我们知道 *(a+1)==a[1] ,那么二维数组第一行 a[0] 也可以写成 *(a+0) ,同样的 a[0][1]也就可以写成 *(a[0]+1)


下面我们来练练二维数组:


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


输出:


43561b534cab4d439011ee7562ad85ef.png


不知道大家准确率咋样,我们直接开始分析了:


int a[3][4] = { 0 };
  printf("%d\n", sizeof(a));   // 48
  //a是二维数组的数组名,数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节
  printf("%d\n", sizeof(a[0][0]));  // 4
  //a[0][0]是一个整型元素,大小是4个字节
  printf("%d\n", sizeof(a[0]));   // 16
  //a[0]是第一行的数组名,第一行的数组名单独放在sizeof内部,计算的是第一行的总大小,单位是字节 - 16
  printf("%d\n", sizeof(a[0] + 1));  // 4/8
  //a[0]虽然是第一行的数组名,但是并非单独放在sizeof内部
  //a[0]作为第一行的数组名并非表示整个第一行这个数组,a[0]就是第一行首元素的地址,a[0]--> &a[0][0] - int*
  //a[0]+1,跳过一个int,是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
  //a是二维数组的数组名,没单独放在sizeof内部,也没有&,所以a就是数组首元素的地址
  //二维数组,我们把它想象成一维数组,它的第一个元素就是二维数组的第一行
  //a就是第一行的地址,a+1 是第二行的地址,是地址,大小就是 4/8 个字节
  printf("%d\n", sizeof(*(a + 1)));   // 16
  //a+1是第二行的地址,*(a+1) 找到的就是第二行,sizeof(*(a + 1))也就是sizeof(a[1])计算的就是第二行的大小
  printf("%d\n", sizeof(&a[0] + 1));  // 4/8
  //&a[0]是第一行的地址,&a[0]+1就是第二行的地址,sizeof(&a[0] + 1)计算的第二行地址大小
  printf("%d\n", sizeof(*(&a[0] + 1)));  // 16
  //&a[0] + 1是第二行的地址,*(&a[0] + 1)拿到的就是第二行,大小就是16个字节
  printf("%d\n", sizeof(*a));  // 16
  //a表示首元素的地址,就是第一行的地址 - &a[0]
  printf("%d\n", sizeof(a[3]));  //16
  //a[3]是二维数组的第4行,虽然没有第四行,但是类型能够确定,大小就是确定的。大小就是一行的大小,单位是字节 - 16


总结


做这类题就是要抓住核心要点,明白 strlen、sizeof 的用法,再多加练习就能做到秒杀,相信大家看完本期一定收获满满吧,以后碰到就不怕了。


相关文章
|
11月前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
126 3
|
7月前
|
存储 监控 算法
关于员工上网监控系统中 PHP 关联数组算法的学术解析
在当代企业管理中,员工上网监控系统是维护信息安全和提升工作效率的关键工具。PHP 中的关联数组凭借其灵活的键值对存储方式,在记录员工网络活动、管理访问规则及分析上网行为等方面发挥重要作用。通过关联数组,系统能高效记录每位员工的上网历史,设定网站访问权限,并统计不同类型的网站访问频率,帮助企业洞察员工上网模式,发现潜在问题并采取相应管理措施,从而保障信息安全和提高工作效率。
92 7
|
10月前
|
存储 程序员 C++
深入解析C++中的函数指针与`typedef`的妙用
本文深入解析了C++中的函数指针及其与`typedef`的结合使用。通过图示和代码示例,详细介绍了函数指针的基本概念、声明和使用方法,并展示了如何利用`typedef`简化复杂的函数指针声明,提升代码的可读性和可维护性。
258 1
|
11月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
11月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
11月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
11月前
|
容器
在使用指针数组进行动态内存分配时,如何避免内存泄漏
在使用指针数组进行动态内存分配时,避免内存泄漏的关键在于确保每个分配的内存块都能被正确释放。具体做法包括:1. 分配后立即检查是否成功;2. 使用完成后及时释放内存;3. 避免重复释放同一内存地址;4. 尽量使用智能指针或容器类管理内存。
|
11月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
201 4
|
11月前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
119 2
|
11月前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
106 1

热门文章

最新文章

推荐镜像

更多
  • DNS