写在前面数组
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有一个本质的认识,同时对与之相关的易错、易混问题有一个正确、清晰的判断。因为在下才疏学浅,错误疏漏之处在所不免,但愿广大读者积极批评指正,您的批评指正是在下前进的不竭动力。