sizeof、strlen、字符串、数组,整到一块,你还清楚吗?

写在前面数组

sizeof、strlen、字符串、数组,提到这些概念,相信学过C语言的人都能耳熟能详,也能谈得头头是道,可是,在实际运用中,当这些内容交织在一块儿时,你们却不必定能搞地清清楚楚,本文的目的正是帮助你们将相关知识总结清楚。函数

 

正文编码

先看一段代码spa

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 void testchar(char str[])
 5 {
 6     printf("%d %d\n", sizeof(str), strlen(str));
 7 }
 8 
 9 void testint(int arr[])
10 {
11     printf("%d\n", sizeof(arr));
12 }
13 
14 int main()
15 {
16     char str[] = "abc";
17     printf("%d %d\n", sizeof(str), strlen(str)); //4 3
18 
19     char str1[10] = "abc";
20     printf("%d %d\n", sizeof(str1), strlen(str1)); //10 3
21 
22     char dog[] = "wangwang\0miao";
23     printf("%d %d\n", sizeof(dog), strlen(dog)); //14 8
24     testchar(dog); //4 8
25 
26     char *cat = "wangwang\0miaomiao";
27     printf("%d %d\n", sizeof(cat), strlen(cat)); //4 8
28     
29     int arr[10] = { 0 };
30     printf("%d %d\n", sizeof(arr), sizeof(arr[11])); //40 4
31     testint(arr); //4
32 
33     return 0;
34 }

 

 结果指针

 

在解释上面的例子以前,咱们先来讲一说sizeof和strlen。code

语法上的本质不一样:对象

sizeof是运算符,strlen是函数。blog

适用范围不同:内存

对sizeof(name)而言,name能够是变量名也能够是类型名,对strlen而言,参数必须是char*类型的,即strlen仅用于字符串。字符串

重中之重——从底层看本质

strlen(ptr)的执行机理是:从参数ptr所指向的内存开始向下计数,直到内存中的内容是全0(即’\0’)为止(不会对’\0’进行计数)。用strlen测量字符串的长度,其实就是基于这个原理。

sizeof(name)的执行机理是:若是name是一个类型名,获得的是该类型的大小(所谓类型的大小,指的是:若是存在一个该类型的变量,这个变量在内存中所占用的字节数),若是name是一个变量名,那么,sizeof(name)并不会真正访问该变量,而是先获知该变量的类型,而后再返回该类型的大小(即使是struct这样的复杂类型,编译器在编译时也会根据它的各个域记录其大小,因此,由类型获得类型大小,不是一件难事)。换句话说,本质上,sizeof的运算对象是类型。若是name是一个变量名,那么,sizeof如何“看待”name的类型,将是一个关键问题。(后面咱们会对这一点有深入的体会)

上面提到的这一点,是理解好sizeof和strlen的不二法门,是放之四海皆准的准则。下面,咱们就以这样的准则来分析上面的例子。

a.

char str[] = "abc";
printf("%d %d\n", sizeof(str), strlen(str)); //4 3

这里,是用数组的形式声明字符串,编译器会自动在字符串后面加上'\0',因此,数组的元素个数是4而不是3。对于sizeof(str)而言,sizeof将str视为char [4]l类型的变量,因此,sizeof(str)的结果就是整个数组所占有的空间大小。对于strlen(str)来讲,它从str指向的内存开始计数,直到遇到全0的内存('\0'),因此最后获得结果3。

b.

char str1[10] = "abc";
printf("%d %d\n", sizeof(str1), strlen(str1)); //10 3

 

编译器为char str1[10]分配10个数组元素大小的空间,这与初始化它的字符串没有关系,因此sizeof(str1)获得10。

c.

char dog[] = "wangwang\0miao";
printf("%d %d\n", sizeof(dog), strlen(dog)); //14 8
testchar(dog); //4 8

前两句和a中的状况相同,sizeof(dog)输出整个数组所占的内存大小(包括编译器加上去的'\0'),strlen(dog)遇到'\0'就中止,因此输出8。

再看后面的函数调用,数组名dog做为函数实参传入,咱们再来回顾一下testchar函数

void testchar(char str[])
{
    printf("%d %d\n", sizeof(str), strlen(str));
}

咱们发现,这里sizeof(str)并无像sizeof(dog)那样获得14,而是获得了4。这是由于,str是函数形参,尽管它是以数组名的形式出现的,传给它的实参也确实是数组名,但sizeof仅仅把它当成一个char*类型的指针看待,因此,sizeof(str)的结果就是char *类型所占的空间4。至于strlen(str),咱们前面说过,它执行的机理就是从str指向的内存开始向下计数,直到遇到'\0',因此依然获得8。

d.

char *cat = "wangwang\0miaomiao";
printf("%d %d\n", sizeof(cat), strlen(cat)); //4 8 

因为cat明确声明为char*,因此sizeof将它视为指针,获得4。

e.

int arr[10] = { 0 };
printf("%d %d\n", sizeof(arr), sizeof(arr[11])); //40 4
testint(arr); //4

前面说过,当数组名做为函数形参出现时,sizeof仅仅将其视为一个指针,不然,sizeof认为它表明整个数组,因此,sizeof(arr)获得整个数组所占的字节数40,而testint(arr)的结果是int*类型的指针的长度4。

sizeof(int[11])中,很明显数组越界了,但并不会出现运行时错误。缘由是:依据咱们给出的判断准则,sizeof并无真正访问arr[11],根据arr的声明,sizeof知道arr[11]是int型的,因此返回int类型的大小。

至于testint(arr),道理和c中的testchar(dog)相同。

 

最后,基于上面的讨论,给出编码准则:

1.永远不要用sizeof来求字符串长度!它不是干这个活的,因此你也永远不会获得正确答案。

2.不要自做聪明地用sizeof(arr)/sizeof(arr[0])这样的代码求数组的长度!sizeof也不是干这个活的。若是arr是函数形参,获得的结果将是错误的(除非你在32位系统下刚好声明int arr[1]或者char arr[4]等,但这纯属巧合)。既然是数组,长度天然是已知的,求数组长度这一自己,就是画蛇添足的愚蠢行为。

 

写在后面

本文的目的,就是使读者对C语言的基础知识——sizeof和strlen有一个本质的认识,同时对与之相关的易错、易混问题有一个正确、清晰的判断。因为在下才疏学浅,错误疏漏之处在所不免,但愿广大读者积极批评指正,您的批评指正是在下前进的不竭动力。

相关文章
相关标签/搜索