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个字节

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


相关文章
|
4天前
|
C语言
爱上C语言:分支与循环(分支篇)多个if与if — else if区别
爱上C语言:分支与循环(分支篇)多个if与if — else if区别
|
4天前
|
设计模式 算法 Java
后端面试题:接口和抽象类的区别?抽象类可以多继承吗?
字节后端面试题:接口和抽象类的区别?抽象类可以多继承吗?
29 0
|
4天前
|
存储 缓存 安全
面试题-HashMap底层原理与HashTable的区别
字节跳动面试题-HashMap底层原理与HashTable的区别
33 0
|
4天前
|
存储 C语言
【我爱C语言】详解字符函数isdigit和字符串转换函数(atoi和snprintf实现互相转换字符串)&&三种strlen模拟实现2
【我爱C语言】详解字符函数isdigit和字符串转换函数(atoi和snprintf实现互相转换字符串)&&三种strlen模拟实现
|
1天前
|
前端开发
vue2与vue3双向数据绑定的区别,前端面试自我介绍
vue2与vue3双向数据绑定的区别,前端面试自我介绍
|
2天前
|
开发工具 Python
Python中return和yield的区别,面试官不讲武德
Python中return和yield的区别,面试官不讲武德
|
2天前
|
消息中间件 前端开发 Java
java面试刷题软件kafka和mq的区别面试
java面试刷题软件kafka和mq的区别面试
|
4天前
|
设计模式 API 数据格式
腾讯面试官问我适配器和桥接器的区别?
腾讯面试官问我适配器和桥接器的区别?
7 0
|
4天前
|
C语言
C语言:字符函数和字符串函数(strlen strcat strcmp strncmp等函数和模拟实现)
C语言:字符函数和字符串函数(strlen strcat strcmp strncmp等函数和模拟实现)
|
4天前
|
Java 调度
一张图搞清楚wait、sleep、join、yield四者区别,面试官直接被征服!
一张图搞清楚wait、sleep、join、yield四者区别,面试官直接被征服!
31 2