日刷百题,题刷百日!
归纳编程学习的感悟,
记录奋斗路上的点滴,
希望能帮到一样刻苦的你!
如有不足欢迎指正!
共同学习交流!
🌎欢迎各位→点赞 👍+ 收藏⭐️ + 留言📝
冰冻三尺非一日之寒,水滴石穿非一日之功。
一起加油!
一、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
size_t strlen ( const char * str );
strlen的参数类型是字符指针,返回类型是无符号整型
#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和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环境下输出:
三、关于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