c语言中strlen与sizeof的区别(指针面试题详解帮你深度区分!)

简介: c语言中strlen与sizeof的区别(指针面试题详解帮你深度区分!)

什么是strlen?什么是sizeof?

1. strlen:通俗的来讲是一个用来计算字符串长度的库函数。

例如:

int main()
{
  char arr[] = "abcdes";
  int len = strlen(arr);
  printf("字符串的长度是:%d\n", len);
  return 0;
}

运行结果:

由图可知strlen是计算一个不包括’\0’在内的字符串的长度的总合。

2. sizeof:sizeof操作符以字节形式给出了其操作数的存储大小。但sizeof我们要提到特殊的一点"当数组名单独放在sizeof中的时候,该数组名表示的是整个数组"例如:

int main()
{
  int arr[] = { 1,3,5,6,7 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  printf("数组的长度是:%d\n", sz);
  return 0;
}

运行结果:

数组的特殊情况

前面我们提到了sizeof与strlen的用法,下面我们还要学习一点关于数组的一些特殊情况。

①:当数组名单独放在sizeof里面的时候,该数组名表示整个数组

②:当&arr(数组名),此时也表示的是整个数组

③:在其除上面俩种特殊情况以外,数组名都表示的首元素地址

④:特别强调,二维数组的数组名表示第一行数组

来看看一些经典笔试题来帮助我们理解指针在strlen与sizeof上不同的含义。

指针和数组笔试题解析

sizeof与一维数组

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

题目分析:

  1. printf("%d\n", sizeof(a));

前面我们提到过数组名单独放在sizeof中表示的是整个数组

因此我们可以知道,这个sizeof计算的是整个数组大小,因此为16个字节

  1. printf("%d\n", sizeof(a + 0));

数组名没有单独放在sizeof内部因此,该数组名表示的是首元素地址

首元素地址加0还是首元素的地址,是地址就是4或8个字节

  1. printf("%d\n", sizeof(*a));

同理,数组名没有单独放在sizeof内部因此,该数组名表示的是首元素地址,对数组名进行解引用表示的仍然是首元素,因此是4个字节

  1. printf("%d\n", sizeof(a + 1));

同理,数组名没有单独放在sizeof内部因此,该数组名表示的是首元素地址,首元素地址加一跳过四个字节,表示第二个元素的地址,因此仍然是4个字节

  1. printf("%d\n", sizeof(a[1]));

a[1]就是数组的第二个元素,这里计算的就是第二个元素的大小,故为4个字节

  1. printf("%d\n", sizeof(&a));

&a表示的是数组的地址,是地址就是4个或者8个字节(取决于x86还是x64)

  1. printf("%d\n", sizeof(*&a));

对数组指针解引用访问一个数组的大小

sizeof(*&a) – sizeof(a);

  1. printf("%d\n", sizeof(&a + 1));

&a表示数组的地址,加一跳过整个数组,但仍是地址,是地址就是4或8个字节

  1. printf("%d\n", sizeof(&a[0]));

&a[0]表示的是一个元素的地址,因此是4或8个字节

  1. printf("%d\n", sizeof(&a[0] + 1));

取出来首元素地址,首元素地址加一就是第二个元素的地址,是地址就是4或者8个字节

运行结果:

sizeof与字符数组

//字符数组
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;
}

题目分析:

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

数组名单独放在sizeof中表示整个数组,因此该sizeof计算的是整个数组的大小,字节为6(char类型为一个字节)

  1. printf("%d\n", sizeof(arr + 0));

数组名没有单独放在sizeof中因此数组名表示首元素地址,首元素地址加0还是首元素地址,是地址就是4/8个字节

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

数组名没有单独放在sizeof中,因此数组名表示首元素地址,对首元素地址进行解引用表示首元素,因此为1个字节

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

数组名没有单独放在sizeof中表示的首元素地址,因此arr[1]表示数组中第二个元素,故为1个字节

  1. printf("%d\n", sizeof(&arr));

&arr表示数组的地址,是地址就是4或8个字节

  1. printf("%d\n", sizeof(&arr + 1));

&arr表示整个数组的地址,&arr+1表示跳过整个数组,但仍然是地址,是地址就是4/8个字节

  1. printf("%d\n", sizeof(&arr[0] + 1));

&arr[0]表示取数组中第一个元素的地址,&arr[0] + 1表示第一个元素的地址加1,表示第二个元素的地址,是地址就是4/8个字节

运行结果:

strlen与字符数组

//strlen求字符数组
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));

题目分析:

  1. printf("%d\n", strlen(arr));

数组名表示首元素地址,因此计算的是该数组的长度,strlen是从所传的参数计算长度一直到’\0’为止,因为该字符数组没有’\0’故而该结果是一个随机值(随机值>=6)。

  1. printf("%d\n", strlen(arr+0));

随机值>=6(没有/0)arr就是首元素地址,arr+0还是首元素地址。

  1. printf("%d\n", strlen(*arr));

因*arr表示的是该数组首元素的值,因此传给strlen的就是首元素的ASCII值,故而造成了非法访问(strlen是用来结收地址),因此无法运行结果

  1. printf("%d\n", strlen(arr[1]));

同理,arr[1]也是该数组首元素的值,传给strlen的也是该值的ASCII值,因此非法访问,导致无法运行出结果

  1. printf("%d\n", strlen(&arr));

将数组的地址传给strlen,实际上把数组的地址转换成了该数组的首元素地址,又因为该数组没有’\0’,因此结果是一个随机值

  1. printf("%d\n", strlen(&arr+1));

将整个数组的地址+1,故而跳过了整个数组,因此不知道后面什么时候会碰到’\0’,因此也是随机值

  1. printf("%d\n", strlen(&arr[0]+1));

&arr[0]就是找到数组的首元素的地址,首元素的地址+1就是数组中第二个元素的地址,又因为数组中没有’\0’因此也是一个大于5的随机值

这个的运行结果要屏蔽掉第三个和第四个(无法访问):

sizeof与字符串

//字符串
int main()
{
  char arr[] = "abcdef";
  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;
}

题目分析:

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

数组名单独放在sizeof里面表示整个数组,因此计算的就是整个数组的大小,一个字符是一个字节,因此为7个字节。

  1. printf("%d\n", sizeof(arr + 0));

数组名没有单独放在sizeof内部,因此数组名表示首元素的地址,首元素地址+0还是首元素的地址,是地址就是4/8个字节

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

对数组名进行解引用,表示首元素,首元素是字符,因此为1个字节

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

arr[1]表示第二个元素,元素是字符因此是一个字节

  1. printf("%d\n", sizeof(&arr));

&arr表示整个字符串的地址,是地址就是4/8个字节

  1. printf("%d\n", sizeof(&arr + 1));

&arr表示整个数组的地址,是地址加1表示跳过整个字符串,但仍然是地址,是地址就是4/8个字节

  1. printf("%d\n", sizeof(&arr[0] + 1));

&arr[0]表示字符串首字符的地址,首字符的地址加1仍然是地址,是地址就是4/8个字节

运行结果:

strlen与字符串

//字符串
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;
}

题目分析:

  1. printf("%d\n", strlen(arr));

数组名表示首元素地址,在字符串的末尾有’\0’,一直找到’\0’,因此长度为6

  1. printf("%d\n", strlen(arr + 0));

数组名表示首元素地址,首元素地址加0还是首元素地址,因此长度仍然为6

  1. printf("%d\n", strlen(*arr));

对数组解引用,*arr表示首元素,在将首元素转换成了ASCII值传给了strlen,然而我们前面提到了,strlen只能接收地址,因此造成了非法访问,无法得到结果

  1. printf("%d\n", strlen(arr[1]));

arr[1]同样表示字符串中的第二个元素,将它转换成了ASCII值传给了strlen,故而非法访问,无法得到结果

  1. printf("%d\n", strlen(&arr));

&arr表示的是整个字符串的地址,然后&arr会被转换成首元素地址,因此从首元素一直找到’\0’一共有六个字符,因此长度为6

  1. printf("%d\n", strlen(&arr + 1));

&arr表示的地址,因此&arr + 1跳过了整个字符串,从这个地址一直找到’\0’停止下来,但因为不知道什么时候会找到’\0’,因此结果是一个随机值

  1. printf("%d\n", strlen(&arr[0] + 1));

找到数组的第一个字符的地址然后+1,即数组的第二个元素的地址往后找到’\0’为止,因此结果为5

运行结果:因第三个和第四个无法得到结果,这是省略了这俩个后的运行结果

sizeof与字符指针

int main()
{
  //字符指针与sizeof
  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));
  return 0;
}

题目分析:

  1. printf("%d\n", sizeof(p));

p表示指针变量,因此计算的是指针变量的大小,指针变量的大小只有4/8个字节

  1. printf("%d\n", sizeof(p + 1));

p + 1表示字符串中第二个字符的地址,是地址就还是4/8个字节

  1. printf("%d\n", sizeof(*p));

*p表示的是字符串中的首字符,字符的大小是1个字节,因此为1字节

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

p[0]等价于 *(p + 0)表示的是字符串中第一个字符,因此为1字节

  1. printf("%d\n", sizeof(&p));

取出来的是地址它也是4/8个字节

  1. printf("%d\n", sizeof(&p + 1));

&p是首字符的地址,&p +1是第二个字符的地址,是地址就是4/8个字节

  1. printf("%d\n", sizeof(&p[0] + 1));

&p[0]表示的是首字符的地址 ,+1是第二个字符的地址,是地址就是4/8个字节

运行结果:

strlen与字符指针

int main()
{
  //字符指针与strlen
  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;
}

题目分析:

  1. printf("%d\n", strlen(p));

p表示首字符的地址,从首字符一直往后找直到碰到’\0’为止,故为6

  1. printf("%d\n", strlen(p + 1));

p表示首字符的地址,p + 1表示第二个字符的地址,故而为5

  1. printf("%d\n", strlen(*p));

*p表示首字符的值,然后转换成ASCII的值传给strlen,我们前面提到过strlen只能接收指针和地址,因此会非法访问无法得到结果

  1. printf("%d\n", strlen(p[0]));

同理,p[0]表示首字符,然后转换成ASCII的值传给strlen,因此会非法访问无法得到结果

  1. printf("%d\n", strlen(&p));

&p相当与一个存放了一级指针p的地址,因此从这个地址往后找’\0’,直到找到为止,因此为随机值

  1. printf("%d\n", strlen(&p + 1));

同理&p相当与一个存放了一级指针p的地址,往后加一相当与&p往后的一个地址,因此从这个地址往后找’\0’,直到找到为止,因此为随机值

  1. printf("%d\n", strlen(&p[0] + 1));

&p[0]相当与首字符的地址,然后 + 1相当于第二个字符的地址,一直往后找,找到’\0’为止,因此为5。

运行结果:因第三个和第四个无法得到结果,这是省略了这俩个后的运行结果

sizeof与二维数组

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

题目分析:

  1. printf("%d\n", sizeof(a));

数组名单独放在sizeof中表示整个数组,因此计算的是整个数组的字节,数组中有12个整型因此为4个字节.

  1. printf("%d\n", sizeof(a[0][0]));

a[0][0]相当与数组中的第一个元素,计算第一个元素的大小,因此为4个字节

  1. printf("%d\n", sizeof(a[0]));

a[0]相当于第一行的数组名,第一行的数组名单独放在sizeof内部相当于计算这数组的第一行的大小,因此为16个字节

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

数组名并非单独放在sizeof内部,因次a[0]表示第一行第一个元素的地址,a[0] + 1 -->等价于 a[0][1]因此为4个字节

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

(a[0] + 1)表示的是数组第一行第一个元素的地址,*(a[0] + 1)表示第一行第一个元素,因此为4个字节

  1. printf("%d\n", sizeof(a + 1));

数组名并非单独放在sizeof内部,因此a表示第一行的数组地址,a + 1表示第二行数组的地址,因此计算的是整个第二行的数组的地址,是地址就是4/8个字节

  1. printf("%d\n", sizeof(*(a + 1)));

a + 1表示第二行数组的地址,因此*(a + 1)表示的是整个第二行,故是16个字节

  1. printf("%d\n", sizeof(&a[0] + 1));

&a[0]是数组第一行的地址,第一行的地址加一就是第二行的地址,是地址就是4/8个字节

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

&a[0]是数组第一行的地址,第一行的地址加一就是第二行的地址,*(&a[0] + 1)就是表示的是整个第二行,计算的就是第二行的大小,因此为16个字节

  1. printf("%d\n", sizeof(*a));

*a表示数组名,也就算的是整个第一行的,因此为16个字节

  1. printf("%d\n", sizeof(a[3]));

在我们看来a[3]是越界的情况,实际上sizeof并不会真的取运行这个,也就是说sizeof并不会真的取查看它是否越界,而是根据它的类型来直接访问它的值,也就是算的整个一行的,因此为16个字节

好了,今天的内容就讲到这里了,如果有讲的不好地方甚至错误的地方也 希望各位帮忙指出一下,作者都会虚心接受努力修改的!


相关文章
|
16天前
|
Java 程序员 调度
Java 高级面试技巧:yield() 与 sleep() 方法的使用场景和区别
本文详细解析了 Java 中 `Thread` 类的 `yield()` 和 `sleep()` 方法,解释了它们的作用、区别及为什么是静态方法。`yield()` 让当前线程释放 CPU 时间片,给其他同等优先级线程运行机会,但不保证暂停;`sleep()` 则让线程进入休眠状态,指定时间后继续执行。两者都是静态方法,因为它们影响线程调度机制而非单一线程行为。这些知识点在面试中常被提及,掌握它们有助于更好地应对多线程编程问题。
52 9
|
21天前
|
安全 Java 程序员
Java面试必问!run() 和 start() 方法到底有啥区别?
在多线程编程中,run和 start方法常常让开发者感到困惑。为什么调用 start 才能启动线程,而直接调用 run只是普通方法调用?这篇文章将通过一个简单的例子,详细解析这两者的区别,帮助你在面试中脱颖而出,理解多线程背后的机制和原理。
50 12
|
19天前
|
编译器 Android开发 开发者
Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
Lambda表达式和匿名函数都是Kotlin中强大的特性,帮助开发者编写简洁而高效的代码。理解它们的区别和适用场景,有助于选择最合适的方式来解决问题。希望本文的详细讲解和示例能够帮助你在Kotlin开发中更好地运用这些特性。
29 9
|
2月前
|
Java
Java社招面试题:& 和 && 的区别,HR的套路险些让我翻车!
今日分享的主题是如何区分&和&&的区别,提高自身面试的能力。主要分为以下四部分。 1、自我面试经历 2、&amp和&amp&amp的不同之处 3、&对&&的不同用回答逻辑解释 4、彩蛋
|
3月前
|
Java 程序员
Java社招面试题:& 和 && 的区别,HR的套路险些让我翻车!
小米,29岁程序员,分享了一次面试经历,详细解析了Java中&和&&的区别及应用场景,展示了扎实的基础知识和良好的应变能力,最终成功获得Offer。
104 14
|
2月前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
2月前
|
C语言
【C语言】sizeof 关键字详解
`sizeof` 关键字在C语言中用于计算数据类型或变量在内存中占用的字节数。它是一个编译时操作符,对性能没有影响。`sizeof` 可以用于基本数据类型、数组、结构体、指针等,了解和正确使用 `sizeof` 对于内存管理和调试程序非常重要。
100 2
|
3月前
|
存储 缓存 网络协议
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点,GET、POST的区别,Cookie与Session
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点、状态码、报文格式,GET、POST的区别,DNS的解析过程、数字证书、Cookie与Session,对称加密和非对称加密
|
4月前
|
编译器
经典面试题:变量的声明和定义有什么区别
在编程领域,变量的“声明”与“定义”是经典面试题之一。声明告诉编译器一个变量的存在,但不分配内存,通常包含变量类型和名称;而定义则为变量分配内存空间,一个变量必须至少被定义一次。简而言之,声明是告知变量形式,定义则是实际创建变量并准备使用。
|
4月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
307 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习

热门文章

最新文章