一、sizeof和strlen的对比
1.1 sizeof
在学习操作符的时候,我们学习了 sizeof , sizeof 计算变量所占内存内存空间大小的,单位是 字节,如果操作数是类型的话,计算的是使⽤类型创建的变量所占内存空间的大小。
sizeof的返回类型size_t。
sizeof 只关注占⽤内存空间的大小,不在乎内存中存放什么数据。
比如:
#include <stdio.h> int main() { int a = 10; printf("%d\n", sizeof(a));//输出4,表示变量a的内存空间4个字节 printf("%d\n", sizeof a);//输出4,没有()表示sizeof是操作符,不是库函数 printf("%d\n", sizeof(int));//输出4,表示int的内存空间大小 return 0; }
注意:1、sizeof计算大小,根据类型推算。
2、sizeof的操作数如果是一个表达式,表达式不参与计算.
举例:
#include <stdio.h> int main() { short a=10;//2个字节 int i=20;//4个字节 int n=sizeof(a=i+4);//表达式具有俩个属性,值属性和类型属性,sizeof()里面类型属性在编译和链接时已经确定,而表达式的值属性在代码运算时才确认;所以编译和链接时表达式不参与计算,以a的类型为准 printf("%d\n",sizeof(a));//输出2 printf("%d\n",n);//输出2 return 0; }
输出:
1.2 strlen
strlen 是C语⾔库函数,功能是求字符串⻓度。函数原型如下:
size_t strlen ( const char * str );
strlen的参数类型是字符指针,返回类型是无符号整型
统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。
strlen 函数会⼀直向后找 \0 字符,直到找到为⽌,所以可能存在越界查找。
#include <stdio.h> int main() { char arr1[3] = {'a', 'b', 'c'}; char arr2[] = "abc"; printf("%d\n", strlen(arr1));//输出随机值,arr1为a的地址,往后找'\0',没有'\0',所以随机值 printf("%d\n", strlen(arr2));//输出3,字符串的实际为a b c \0 printf("%d\n", sizeof(arr1));//输出3,arr1为整个数组,计算数组大小,一共3个字节 printf("%d\n", sizeof(arr2));//输出为4,arr2为整个数组,计算数组大小,数组内存有4个字节 return 0; }
1.3 sizeof 和 strlen的对比
sizeof
1. sizeof是操作符
2. sizeof计算操作数所占内存的大小,单位是字节
3. 不关注内存中存放什么数据
strlen
1. strlen是库函数,使⽤需要包含头⽂件 string.h
2. srtlen是求字符串长度的,统计的是 \0 之前字符的隔个数
3. 关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能 会越界
二、sizeof和strlen习题及解析
2.1一维数组中sizeof的解析
int a[] = {1,2,3,4}; printf("%d\n",sizeof(a)); //16 printf("%d\n",sizeof(a+0)); //a没有单独放在sizeof内部,所以代表首元素地址,地址的大小为4/8个字节 printf("%d\n",sizeof(*a)); //*a代表首元素,大小是4个字节 printf("%d\n",sizeof(a+1)); //a是首元素地址,a+1代表第二个元素地址,地址的大小为4/8个字节 printf("%d\n",sizeof(a[1])); //代表第二个元素大小,为4个字节 printf("%d\n",sizeof(&a)); //&a为整个数组地址,地址大小为4/8个字节 printf("%d\n",sizeof(*&a)); //*与&抵消,也就是sizeof(a),a代表整个数组,数组大小为16个字节 printf("%d\n",sizeof(&a+1)); //&a为整个数组地址,&a+1表示跳过整个数组,是数组后面的地址,地址大小为4/8个字节 printf("%d\n",sizeof(&a[0])); //首元素的地址,地址大小为4/8个字节 printf("%d\n",sizeof(&a[0]+1)); //表示跳过一个元素,第二个元素的地址,地址大小为4/8个字节
在x86环境下输出:
2.2字符数组中sizeof和strlen的解析
2.2.1
char arr[] = {'a','b','c','d','e','f'}; printf("%d\n", sizeof(arr)); //整个数组大小,输出6 printf("%d\n", sizeof(arr+0)); //arr+0为首元素地址,地址大小为4/8个字节 printf("%d\n", sizeof(*arr)); //首元素,大小为1个字节 printf("%d\n", sizeof(arr[1])); //第二个元素,元素类型为char,char大小为1个字节 printf("%d\n", sizeof(&arr)); //&arr整个数组地址,地址大小为4/8个字节 printf("%d\n", sizeof(&arr+1)); //&arr+1为跳过整个数组,数组后面的地址,地址大小为4/8个字节 printf("%d\n", sizeof(&arr[0]+1)); //&arr[0]首元素地址,&arr[0]+1为第二个元素地址,地址大小为4/8个字节
在x86环境下输出:
2.2.2
char arr[] = {'a','b','c','d','e','f'}; printf("%d\n", strlen(arr)); //从首元素地址开始找\0,输出为随机值 printf("%d\n", strlen(arr+0)); //arr+0为首元素地址,同上,输出为随机值 printf("%d\n", strlen(*arr)); //*arr为字符a,ASCII值为97,strlen参数为指针,所以97被当成地址,此情况属于非法访问 printf("%d\n", strlen(arr[1])); //第二个元素'b',ASCII值为98,strlen参数为指针,所以97被当成地址,此情况属于非法访问 printf("%d\n", strlen(&arr)); //&arr为整个数组地址,从首元素地址开始找\0,输出为随机值 printf("%d\n", strlen(&arr+1)); //&arr为跳过整个数组,数组后面的地址,输出为随机值 printf("%d\n", strlen(&arr[0]+1)); //第二个元素地址,从第二个元素向后找\0,输出为随机值
2.2.3
char arr[] = "abcdef"; printf("%d\n", sizeof(arr)); //7 printf("%d\n", sizeof(arr+0)); //首元素地址,地址大小为4/8个字节 printf("%d\n", sizeof(*arr)); //首元素大小为1个字节 printf("%d\n", sizeof(arr[1])); //第二个元素大小为1个字节 printf("%d\n", sizeof(&arr)); //整个数组地址,地址大小为4/8个字节 printf("%d\n", sizeof(&arr+1)); //为整个数组后面的地址,地址大小为4/8个字节 printf("%d\n", sizeof(&arr[0]+1)); //第二个元素地址,地址大小为4/8个字节
在x86环境下输出:
2.2.4
char arr[] = "abcdef"; printf("%d\n", strlen(arr)); //6 printf("%d\n", strlen(arr+0)); //从首元素往后找'\0',输出6 printf("%d\n", strlen(*arr)); //'a'的ASCII值为97,strlen将97看出地址访问,非法访问 printf("%d\n", strlen(arr[1])); //第二个元素'b'的ASCII值为98,strlen将98看出地址访问,非法访问 printf("%d\n", strlen(&arr)); //&arr为整个数组的地址,也是首元素地址,从首元素往后找'\0',输出6 printf("%d\n", strlen(&arr+1)); //&arr+1为整个数组后的地址,随机值 printf("%d\n", strlen(&arr[0]+1)); //第二个元素的地址,从第二个元素往后找'\0',输出5
2.2.5
char *p = "abcdef"; //p里面放着字符串首元素地址 printf("%d\n", sizeof(p)); //p为指针,也就是地址,地址的大小为4/8个字节大小 printf("%d\n", sizeof(p+1)); //p为首元素地址,p+1为第二个元素地址,地址的大小为4/8个字节大小 printf("%d\n", sizeof(*p)); //*p为'a',大小为1个字节 printf("%d\n", sizeof(p[0])); //首元素大小为1个字节 printf("%d\n", sizeof(&p)); //为首元素地址的地址,地址的大小为4/8个字节大小 printf("%d\n", sizeof(&p+1)); //跳过一个P后的地址,地址的大小为4/8个字节大小 printf("%d\n", sizeof(&p[0]+1)); //第二个字符的地址,地址的大小为4/8个字节大小
在x86环境下输出:
2.2.6
char *p = "abcdef"; printf("%d\n", strlen(p)); //首元素地址,从首元素往后找'\0',输出为6 printf("%d\n", strlen(p+1)); //p+1为第二个元素地址,从第二个元素往后找'\0',输出为5 printf("%d\n", strlen(*p)); //*p为字符a,'a'的ASCII值为97,strlen将97看出地址访问,非法访问 printf("%d\n", strlen(p[0])); //p[0]为字符a,'a'的ASCII值为97,strlen将97看出地址访问,非法访问 printf("%d\n", strlen(&p)); //&p为p的地址,p里面存放是首元素地址,从p所占空间起始位置开始查找,随机值 printf("%d\n", strlen(&p+1)); //跳过p后的地址,从跳过p后所占空间起始位置开始查找,随机值 printf("%d\n", strlen(&p[0]+1)); //第二个元素的地址,从第二个元素开始查找,输出为5
2.3 二维数组中sizeof的解析
int a[3][4] = {0}; printf("%d\n",sizeof(a)); //计算整个数组大小,大小48个字节 printf("%d\n",sizeof(a[0][0])); //首元素大小,4个字节 printf("%d\n",sizeof(a[0])); //a[0]为第一行数组名,计算第一行大小,16个字节 printf("%d\n",sizeof(a[0]+1)); //a[0]没有单独放在sizeof内部,所以表示首元素地址,+1表示第一行第二个元素的地址,地址的大小为4/8个字节 printf("%d\n",sizeof(*(a[0]+1))); //表示计算第一行第二个元素大小,4个字节 printf("%d\n",sizeof(a+1)); //表示计算第二行地址的大小,地址的大小为4/8个字节 printf("%d\n",sizeof(*(a+1))); //相当于a[1],计算第二行元素大小,16个字节 printf("%d\n",sizeof(&a[0]+1)); //&a[0]第一行的地址,+1第二行的地址,地址的大小为4/8个字节 printf("%d\n",sizeof(*(&a[0]+1))); //第二行地址的解引用,16个字节 printf("%d\n",sizeof(*a)); //计算第一行的大小,16个字节 printf("%d\n",sizeof(a[3])); //计算第某行的大小,因为sizeof内部不参与运算,16个字节
在x86环境下输出:
总结:
1. sizeof(数组名),这⾥的数组名表⽰整个数组,计算的是整个数组的大小。
2. &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址
三、关于sizeof 和 strlen易错题
练习3.1
下面代码的结果是:
#include <stdio.h> int i; int main() { i--; if (i > sizeof(i)) { printf(">\n"); } else { printf("<\n"); } return 0; }
A. >
B. <
C. 不输出
D. 程序有问题
解析:C语言中,0为假,非0即为真。
全局变量,没有给初始值时,编译其会默认将其初始化为0。
i的初始值为0,i--结果-1,i为整形,sizeof(i)求i类型大小是4,按照此分析来看,结果应该选择B,但是sizeof的返回值类型实际为无符号整形,因此编译器会自动将左侧i自动转换为无符号整形的数据,-1对应的无符号整形是一个非常大的数字,为ff ff ff ff,故实际应该选择A
这道题其实很隐蔽,真是虾仁猪心!!!
因此:选择A
练习3.2
下面代码的结果是:( )
#include <stdio.h> #include <string.h> int main() { char arr[] = {'b', 'i', 't'}; printf("%d\n", strlen(arr)); return 0; }
A. 3
B. 4
C. 随机值
D. 5
解析:strlen是用来获取字符串的有效长度的,结尾标记'\0'不包含在内。
strlen获取的规则非常简单:从前往后依次检测,直到遇到'\0'是就终止检测。
而上题中arr是一个字符数组,不是一个有效的字符串,因为后面没有放置'\0',因此strlen在求解时,从首元素开始将有效字符检测完之后,还会继续向后检测,直到遇到'\0'是才终止,因此答案为不确定,就看紧跟在't'之后的第一个'\0'在什么位置。
因此:答案选C