sizeof和strlen的比较
sizeof
在学习操作符的时候,我们学习了 sizeof , sizeof 计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。 sizeof 只关注占用内存空间的大小,不在乎内存中存放什么数据
#include<stdio.h>
int main()
{
int a = 10;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a);
printf("%d\n", sizeof(int));
return 0;
}

所以不管我们的数据是不是数据类型,编译器都会认为sizeof后面的是数据类型来进行处理
strlen
strlen是C语言库函数,功能是求字符串长度
size_t strlen(const char* str);
统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。 strlen 函数会⼀直向后找 \0 字符,直到找到为止,所以可能存在越界查找
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[3] = { 'a', 'b', 'c' };
char arr2[] = "abc";
printf("%d\n", strlen(arr1));
printf("%d\n", strlen(arr2));
printf("%d\n", sizeof(arr1));
printf("%d\n", sizeof(arr2));
return 0;
}

这里strlen(arr1)意思就是计算arr1的字符串长度,但是arr1是字符串嘛?很显然不是,这是一个字符数组,数组中每一个元素都是字符,而字符是没有'\0'的,所以第一个就会一直计算下去,导致strlen越界访问栈区其他内存,直到碰巧找到 '\0'才停止,最终长度取决于栈区随机的内存内容,但每次运行代码时第一个返回值会有所不同
strlen(arr2)意思是计算arr2的字符串长度,arr2是一个字符串数组,字符串末尾默认有'\0',所以就返回字符串的个数,但是'\0'不算在其中,返回值为3
sizeof(arr1)意思是计算arr1的总字节数,有三个元素就返回3
sizeof(arr2)计算字符串的总字节数,返回字节个数,但'\0'要包含在其中,三个字符再加上一个'\0'就是4个总元素

数组和指针解析
一维数组
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a[] = { 1,2,3,4 };
printf("%zd\n",sizeof(a));//16
printf("%zd\n", sizeof(a+0));//首元素地址,类型是int*
printf("%zd\n", sizeof(*a));//首元素地址,*a就是首元素==a[0]
printf("%zd\n", sizeof(a+1));//第二个地址,类型是int*
printf("%zd\n", sizeof(a[1]));//第二个元素
printf("%zd\n", sizeof(&a)); //数组地址,地址大小是4/8
printf("%zd\n", sizeof(*&a));//取出地址后解引用就相互抵消了,类型是int(*)[4],就是数组的地址
printf("%zd\n", sizeof(&a+1));//整个数组地址后加1后的第一个地址
printf("%zd\n", sizeof(&a[0]));//首元素地址
printf("%zd\n", sizeof(&a[0]+1));//数组第二个元素的地址,大小4/8
return 0;
}

首先来看sizeof(a),因为sizeof是计算操作数所占内存的大小,单位是字节,a[]是类型为int型的四个元素的数组,所以大小就是16个字节,
sizeof(a+0)是首元素地址,我们前面说了只有sizeof(数组名)和&数组名才表示整个数组大小,而sizeof(a+0)显然不是,所以 他就是首元素地址,并且类型为int*,那为什么类型是int*,而不是int呢?因为a表示的是首元素的"地址",对地址加减编译器会隐式转化为指针运算,只有指针才能对地址进行操作,所以类型是int*,大小是4或8个字节
sizeof(*a)是首元素地址,还是回到之前的内容,只有sizeof(数组名)和&数组名才表示整个数组大小,而第三个前面有一个"*"号,所以就是对首地址进行解引用,大小是4个字节
sizeof(a+1)就是第二个元素的地址,类型也是int*,大小是4或8个字节
sizeof(a[1])第二个元素的大小,4个字节
sizeof(&a),&数组名表示整个数组的地址,类型是int*,所以大小就是4或8个字节
sizeof(*&a)取出地址后解引用就相互抵消了,类型是int(*)[4],就是数组的地址,所以大小就是16个字节
sizeof(&a+1),&a取出整个数组的大小然后再对整个数组的地址加1,大小就是4或8个字节
sizeof(&a[0]),取出首元素地址,大小就是4或8个字节
sizeof(&a[0]+1)数组第二个元素的地址,大小4/8个字节
字符数组
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char arr[] = {'a','b','c','d','e','f'};
printf("%zd\n", sizeof(arr));
printf("%zd\n", sizeof(arr+0));
printf("%zd\n", sizeof(*arr));
printf("%zd\n", sizeof(arr[1]));
printf("%zd\n", sizeof(&arr));
printf("%zd\n", sizeof(&arr+1));
printf("%zd\n", sizeof(&arr[0]+1));
return 0;
}

sizeof(arr)取出整个数组大小,大小是6个字节
sizeof(arr+0),我们分开来看,首先看"arr+0"表示首元素地址,所以它是地址,然后sizeof对地址进行计算大小,所以大小是4或8个字节
sizeof(*arr)和上面的表示一样,表示对首元素地址进行解引用,大小就是1个字节
sizeof(arr[1])是第二个元素,大小1个字节
sizeof(&arr),取出整个数组的地址,地址大小就是4或8个字节
sizeof(&arr+1),"&arr+1"是取出整个数组的地址后加1就跳过整个数组,大小是4或8个字节
sizeof(&arr[0]+1)取出第二个元素地址,大小是4或8个字节
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{ //strlen的参数是const char*,返回值是size_t size_t strlen(const char*)
char arr[] = { 'a','b','c','d','e','f' };
printf("%zd\n", strlen(arr));//arr首元素地址,数组中没有\0就会导致绝界访问,会生成随机值
printf("%zd\n", strlen(arr+0));//arr+0首元素地址,数组中没有\0就会导致绝界访问,会生成随机值
//printf("%zd\n", strlen(*arr));//arr+0首元素地址,*arr是首元素,就是a的ascii值,就相当与吧97传给了strlen,就会得到野指针,代码会出问题
//printf("%zd\n", strlen(arr[1]));//将第二个元素ascii值传给strlen也是错的
printf("%zd\n", strlen(&arr));//数组地址,起始位置是数组的第一个元素的位置,随机值
printf("%zd\n", strlen(&arr+1));//随机值
printf("%zd\n", strlen(&arr[0]+1));//第二个元素向后统计,得到的也是随机值
return 0;
}
strlen(arr),arr是首元素地址,数组中没有\0就会导致绝界访问,会生成随机值
strlen(arr+0)arr+0首元素地址,数组中没有\0就会导致绝界访问,会生成随机值
strlen(*arr),首先来看一下这个代码
size_t strlen(const char*);
strlen的参数是const char*,返回值是size_t,而这里我们写的是*arr,就是首元素'a',而a的ascii值是97,就相当与把97作为地址传给了strlen,但97这个地址里面不一定是我的值,所以就会得到野指针,代码报错,大家可以试一下
strlen(arr[1]),将第二个元素ascii值传给strlen也是错的,arr[1]==*(arr+1)
strlen(&arr),数组地址,起始位置是数组的第一个元素的位置,数组中没有"\0",所以就会生成随机值
strlen(&arr+1),指的是第二个元素'b',将b的ascii值作为地址传给strlen,生成随机值
strlen(&arr[0]+1)是取出第二个元素地址后向后统计,没有"\0"就生成随机值

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char arr[] = "abvdef";
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;
}
sizeof(arr),求整个数组的占用字节数,大小为7,因为sizeof包含字符串后面的'\0'
sizeof(arr+0),求首元素的地址大小,地址大小为4或8个字节
sizeof(*arr),对首地址解引用就是arr的大小,为1个字节'
sizeof(arr[1]),求第二个元素的大小,1个字节
sizeof(&arr),求整个数组的地址大小,为4或8个字节
sizeof(&arr+1),对整个数组的地址加1就是跳过整个数组地址,大小为4或8个字节
sizeof(&arr[0]+1),求第二个地址的大小,为4或8个字节

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "abvdef";
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;
}
strlen(arr):arr首元素地址,从首元素开始往后计算,直到遇到"\0"结束,字符串默认在最后带有"\0",所以返回6
strlen(arr + 0):首元素地址开始计算,答案也是6
strlen(*arr):将首元素的字符的ascii值作为地址传给strlen,代码报错
strlen(arr[1]):第二个元素的字符的ascii值作为地址传给strlen,代码报错
strlen(&arr):取出整个数组的地址,从地址的第一个位置开始计算,答案也是6
strlen(&arr + 1):取出整个数组的地址加1,生成随机值
strlen(&arr[0] + 1):从第二个元素计算,答案是5

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
const char* p = "abcdef";
printf("%d\n", sizeof(p));//p是指针变量
printf("%d\n", sizeof(p+1));//p+1是b的地址
printf("%d\n", sizeof(*p));//p的类型是const char*,*p就是char类型,1个字节
printf("%d\n", sizeof(p[0]));//p[0]==*(p+0)==*p=='a',大小就是1个字节
printf("%d\n", sizeof(&p));//取出的是P的地址
printf("%d\n", sizeof(&p+1));//跳过p的指针变量皇后的地址
printf("%d\n", sizeof(&p[0]+1));
return 0;
}
sizeof(p):p是一个指针变量,求指针变量的大小4或8
sizeof(p+1):p同样是指向首元素'a',而p+1就是指向'b'的指针变量,大小4或8
sizeof(*p):对p指针进行解引用指向首元素'a',大小就是1个字节
sizeof(p[0]):是取出首元素,大小就是1个字节
sizeof(&p):取出的是p的地址
sizeof(&p+1):对取出p的地址加1,大小还是指针变量
sizeof(&p[0]+1):取出首元素的地址往后加1后就是第二个元素的地址,大小为4或8个字节

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
const char* p = "abcdef";
printf("%d\n", strlen(p));
printf("%d\n", strlen(p + 1));
printf("%d\n", strlen(*p));// *p就是'a'的ascii值,err
printf("%d\n", strlen(p[0]));//p[0]==*(p+0)==*p=='a'
printf("%d\n", strlen(&p));//取出的是P的地址,但字符串有自己的地址,所以没有太大关系,随机值
printf("%d\n", strlen(&p + 1));//跳过p的指针变量皇后的地址
printf("%d\n", strlen(&p[0] + 1));
return 0;
}
里面的有一部分我们在前面说过,这里就不再说了,大家尝试一下自己理解
二维数组
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a[3][4] = { 0 };
printf("%d\n",sizeof(a));//48
printf("%d\n", sizeof(a[0][0]));//第一行第一个元素地址
printf("%d\n", sizeof(a[0]));//第一行的地址,大小为16
printf("%d\n", sizeof(a[0]+1));//没有单独的a[0],所以就是第一行第二个元素地址
printf("%d\n", sizeof(*(a[0] + 1)));//第一行第二个元素地址解引用的内容
printf("%d\n", sizeof(a+1));//第二行的地址,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));// a表示二维数组首元素地址,也就是第一行的地址,*a就是第一行,大小16字节,*a==*(a+0)==a[0]
printf("%d\n", sizeof(a[3]));
return 0;
}
sizeof(a):48
sizeof(a[0][0]):第一行第一个元素,大小为4
sizeof(a[0]):"a[0]"表示第一行的数组,将第一行的数组单独放在sizeof里面,大小为16
sizeof(a[0]+1):这里没有将数组名单独放在sizeof里面,就表示首元素地址加1,就是第一行的第二个元素地址,大小为4或8个字节
sizeof(*(a[0] + 1)):对第一行的第二个地址解引用得到元素大小就是4个字节
sizeof(a+1):a+1表示首元素的地址二维数组的首元素地址是第一行的地址,加1后就是第二行的地址,大小为4或8个字节
sizeof(*(a + 1)):对第二行的地址进行解引用就是16个字节
sizeof(&a[0]+1):取出第二行的地址大小,4或8个字节
sizeof(*(&a[0] + 1)):取出第二行的数组大小,就是16个字节
sizeof(*a):二维数组首元素地址就是第一行的地址,大小为16个字节
sizeof(a[3]):第四行的数组名,计算第四行的数组大小,但我们这里并没有第四行,但sizeof前面说过不需要内容,只需要类型,类型为int的数组a,所以大小为16
