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

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


相关文章
|
1月前
|
程序员 C语言 开发者
pymalloc 和系统的 malloc 有什么区别
pymalloc 和系统的 malloc 有什么区别
|
16天前
|
C语言
【C语言】sizeof 关键字详解
`sizeof` 关键字在C语言中用于计算数据类型或变量在内存中占用的字节数。它是一个编译时操作符,对性能没有影响。`sizeof` 可以用于基本数据类型、数组、结构体、指针等,了解和正确使用 `sizeof` 对于内存管理和调试程序非常重要。
43 2
|
1月前
|
程序员 C语言 开发者
pymalloc 和系统的 malloc 有什么区别?
pymalloc 和系统的 malloc 有什么区别?
|
2月前
|
存储 C语言
C语言中a 和&a 有什么区别
在C语言中,"a" 是一个变量的名字,代表存储在内存中的某个值。而"&a" 则是获取该变量的内存地址,即变量a在计算机内存中的具体位置。这两者的主要区别在于:"a" 操作的是变量中的值,"&a" 操作的是变量的内存地址。
206 23
|
2月前
|
存储 C语言
C语言:普通局部变量、普通全局变量、静态局部变量、静态全局变量的区别
C语言中,普通局部变量在函数内部定义,作用域仅限于该函数;普通全局变量在所有函数外部定义,作用域为整个文件;静态局部变量在函数内部定义但生命周期为整个程序运行期;静态全局变量在所有函数外部定义,但仅在定义它的文件内可见。
105 10
|
2月前
|
程序员 编译器 C语言
C中的 malloc 和C++中的 new 有什么区别
在C语言中,`malloc`函数用于在运行时分配内存,返回指向所分配内存的指针,需显式包含头文件 `<stdlib.h>`。而在C++中,`new`不仅分配内存,还对其进行构造初始化,且直接使用类型声明即可,无需额外包含头文件。`new`还支持数组初始化,能更好地融入C++的面向对象特性,而`malloc`仅作为内存分配工具。使用完毕后,`free`和`delete`分别用于释放`malloc`和`new`分配的内存。
67 21
|
2月前
|
存储 C语言
C语言:结构体与共用体的区别
C语言中,结构体(struct)和共用体(union)都用于组合不同类型的数据,但使用方式不同。结构体为每个成员分配独立的内存空间,而共用体的所有成员共享同一段内存,节省空间但需谨慎使用。
|
2月前
|
存储 编译器 C语言
C语言函数的定义与函数的声明的区别
C语言中,函数的定义包含函数的实现,即具体执行的代码块;而函数的声明仅描述函数的名称、返回类型和参数列表,用于告知编译器函数的存在,但不包含实现细节。声明通常放在头文件中,定义则在源文件中。
|
2月前
|
存储 C语言
C语言指针与指针变量的区别指针
指针是C语言中的重要概念,用于存储内存地址。指针变量是一种特殊的变量,用于存放其他变量的内存地址,通过指针可以间接访问和修改该变量的值。指针与指针变量的主要区别在于:指针是一个泛指的概念,而指针变量是具体的实现形式。
|
2月前
|
存储 编译器 C语言
C语言:数组名作为类型、作为地址、对数组名取地址的区别
在C语言中,数组名可以作为类型、地址和取地址使用。数组名本身代表数组的首地址,作为地址时可以直接使用;作为类型时,用于声明指针或函数参数;取地址时,使用取地址符 (&),得到的是整个数组的地址,类型为指向该类型的指针。