一、理论知识
1.1 sizeof
- 类别:操作符
- 作用:只关注占用内存空间的大小,单位是字节,不关心内存中存放的是什么
- 特点:
-
内部不参与运算
- sizeof在源文件转成exe文件即编译 + 链接的时候,就完成了
,而表达式的运算则是在exe文件到运行的阶段完成了表达式的运算
- sizeof在源文件转成exe文件即编译 + 链接的时候,就完成了
-
没有实际访问sizeof括号里面的内容
-
int main()
{
int num = 10;
sizeof(int) == sizeof(num);
return 0;
}
//sizeof不需要通过访问这个变量来确定大小
//只需要知道这个是什么类型的就可以了
1.2 strlen
- size_t strlen ( const char * str );
- 头文件:#include <string.h>
- 类别:库函数
- 作用:用来计算字符串的长度,传过去元素的地址,然后从这个地址开始数,直到遇到’\0’,返回这之间字符的个数。
- 特点:因为是以’\0’为标志,当没有’\0’的时候,会出现越界访问
非法访问
访问内存无法指哪打哪,只有这个内存分配给了你,才可以访问,否则就是非法访问会报错
1.3 数组名的各种表现形式
- sizeof(数组名)
数组名表示整个数组,计算的是整个数组的大小,单位是字节。注意里面是只有数组名,不能有其他表达式 - &数组名
数组名表示整个数组,取出整个数组的地址,数值上是首元素的地址,但是意义不同,+1跳过的是整个数组 - 其他
代表首元素的地址
1.4 关于二维数组
二、例子解析
2.1一维数组
int main()
{
//一维数组
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a)); //16
printf("%d\n", sizeof(a + 0)); //4/8
printf("%d\n", sizeof(*a)); //4
printf("%d\n", sizeof(a + 1)); //4/8
printf("%d\n", sizeof(a[1])); //4
printf("%d\n", sizeof(&a)); //4/8
printf("%d\n", sizeof(*&a)); //16
printf("%d\n", sizeof(&a + 1)); //4/8
printf("%d\n", sizeof(&a[0])); //4/8
printf("%d\n", sizeof(&a[0] + 1)); //4/8
return 0;
}
解析:
- sizeof(a):
表示计算整个数组的大小,数组有4个元素,每个元素是int类型,大小为4,所以为4*4=16个字节 - sizeof(a + 0):
单独的数组名表示首元素的地址,+0后意义不变,是4/8个字节大小(32位环境下就是4个字节,64位环境下就是8个字节) - sizeof(*a):
对首元素地址解引用,得到的就是首元素,因为首元素就是int类型的,所以大小是4个字节 - sizeof(a + 1):
首元素地址+1,因为是int类型,+1跳过一个int类型,得到第二个元素的地址,是地址大小就是4/8个字节大小 - sizeof(a[1]):
可以翻译成*(a+1),得到的是第二个元素,int类型,大小为4个字节 - sizeof(&a):
&数组名得到的是整个数组的大小,是地址,大小就是4/8个字节 - sizeof(*&a):
解引用整个数组的地址,得到的就是整个数组,这个数组有4个元素,每个元素是4个字节大小,所以整个数组大小为4*4=16 - sizeof(&a + 1):
整个数组地址+1,类型是int(*)[4],+1跳过一个类型大小,得到数组后面的空间,是地址大小就是4/8个字节 - sizeof(&a[0]):
取出第一个元素的地址,大小为4/8个字节 - sizeof(&a[0] + 1):
第一个元素地址+1,得到第二个元素地址,大小为4/8个字节
2.2 字符数组
❤️1.
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr)); //6
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
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;
}
解析:
- sizeof(arr):
单独在sizeof里面的数组名,表示的是整个数组,计算的是整个数组的大小,该数组里面有6个元素,且都是char类型,所以结果为6*1=6字节 - sizeof(arr + 0):
arr表示的是首元素的地址,+0不变,结果是第一个元素的地址,大小为4/8个字节 - sizeof(*arr):
arr表示的是首元素的地址,解引用得到首元素,首元素类型为char,大小为1字节 - sizeof(arr[1]):
可以翻译为*(arr+1),arr表示首元素的地址,得到的结果是第二个元素,类型为char,大小为1字节 - sizeof(&arr):
arr表示整个数组,&arr即取出整个数组的地址,是地址大小就是4/8个字节 - sizeof(&arr + 1):
&arr表示整个数组的地址,类型为char(*)[]+1跳过了整个数组,来到了数组后面的空间,是地址大小就是4/8个字节 - sizeof(&arr[0] + 1):
取出arr[0[的地址,+1跳过一个对应类型(int*),得到第二个元素的地址,大小是4/8个字节 - strlen(arr):
arr表示首元素的地址,strlen是从传进去的地址开始数,直到遇到’\0’,但是arr这个数组里没有’\0’,所以返回的是一个随机值(不知道第几个字符会遇上’\0’) - strlen(arr + 0):
arr表示首元素的地址,+0跳过一个类型,得到首元素的地址,从这个地址数到’\0’,不确定有几个字符,结果为随机值 - strlen(*arr):
arr表示首元素的地址,*arr得到首元素’a’,内存中存的是对应的ASCII值97,因为库函数strlen的参数是const char * str ,所以97被看作是地址,但是随意访问没有分配的内存是非法访问 - strlen(arr[1]):
可以翻译成*(arr+1),得到第二个元素,得到字符’b’,字符在内存的存储是对应的ASCII值,为98,因为库函数strlen的参数是const char * str ,所以98被看作是地址,但是随意访问没有分配的内存是非法访问 - strlen(&arr):
&arr表示取出整个数组的地址,整个数组的地址和数组首元素的地址值相同,只不过一次跳的字节大小不同,从该地址数过去不知道过几个字符才会遇到’\0’,所以结果是随机值 - strlen(&arr + 1):
&arr+1表示取出整个数组的地址,然后+1跳过数组得到数组后面的空间,从该地址数过去不知道过几个字符才会遇到’\0’,所以结果是随机值 - strlen(&arr[0] + 1):
&arr[0]+1表示取出第一个元素的地址,然后+1跳过一个类型,得到第二个元素的地址,从该地址数过去不知道过几个字符才会遇到’\0’,所以结果是随机值
❤️2.
#include <stdio.h>
int main()
{
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
printf("%d\n", strlen(arr)); //6
printf("%d\n", strlen(arr + 0)); //6
printf("%d\n", strlen(*arr)); //非法访问
printf("%d\n", strlen(arr[1])); //非法访问
printf("%d\n", strlen(&arr)); //6
printf("%d\n", strlen(&arr + 1)); //随机值
printf("%d\n", strlen(&arr[0] + 1)); //5
return 0;
}
解析:
- sizeof(arr):
sizeof(数组名)表示计算整个数组的大小,这个数组一共有7个元素,每个元素是char类型,所以结果是7*1=7字节大小 - sizeof(arr + 0):
arr+0表示首元素地址,是地址大小就是4/8字节 - sizeof(*arr):
*arr表示解引用首元素地址,得到首元素,元素是’a’,因为是char类型,所以大小是1字节 - sizeof(arr[1]):
可以翻译为*(arr+1),得到第二个元素,因为是char类型,所以大小是1字节 - sizeof(&arr):
&arr取出的是整个数组的地址,是地址大小就是4/8字节 - sizeof(&arr + 1):
&arr+1得到的是数组后面的空间,还是地址,类型是char(*)[],是地址大小就是4/8字节 - sizeof(&arr[0] + 1):
&arr[0]+1表示取出首元素的地址,+1跳过一个类型大小,得到第二个元素的地址,是地址大小就是4/8字节 - strlen(arr):
arr表示首元素的地址,字符串是隐藏了’\0’的,从首元素开始数,到’\0’,一共有6个元素,所以结果是6 - strlen(arr + 0):
arr+0表示首元素的地址,结果和理由同上 - strlen(*arr):
*arr表示解引用首元素的地址,即得到了首元素’a’,内存中存的是对应的ASCII值97,因为库函数strlen的参数是const char * str ,所以97被看作是地址,但是随意访问没有分配的内存是非法访问 - strlen(arr[1]):
arr[1]表示第二个元素’b’,结果是非法访问,原理同上 - strlen(&arr):
&arr表示取出整个数组的地址,从该地址开始数到’\0’,一共有6个元素,所以结果是6
虽然传过去的类型依旧不匹配,但是从strlen的角度去解读,是6 - strlen(&arr + 1):
&arr+1表示取出整个数组的地址,然后跳过一个char(*)[]类型,得到数组后面的空间,该地址数过去不知道过几个字符才会遇到’\0’,所以结果是随机值 - strlen(&arr[0] + 1):
&arr[0]+1表示取出第一个元素的地址,然后+1跳过一个int*类型,得到第二个元素的地址,从该地址开始数,到’\0’,一共有5个元素,所以结果为5
❤️3.
int main()
{
char* p = "abcdef";
printf("%d\n", sizeof(p)); //4/8
printf("%d\n", sizeof(p + 1)); //4/8
printf("%d\n", sizeof(*p)); //1
printf("%d\n", sizeof(p[0])); //1
printf("%d\n", sizeof(&p)); //4/8
printf("%d\n", sizeof(&p + 1)); //4/8
printf("%d\n", sizeof(&p[0] + 1)); //4/8
printf("%d\n", strlen(p)); //6
printf("%d\n", strlen(p + 1)); //5
printf("%d\n", strlen(*p)); //非法访问
printf("%d\n", strlen(p[0])); //非法访问
printf("%d\n", strlen(&p)); //随机值
printf("%d\n", strlen(&p + 1)); //随机值
printf("%d\n", strlen(&p[0] + 1)); //5
return 0;
}
解析:
- sizeof(p):
p里面放的是常量字符串中首元素的地址,是地址大小就是4/8字节大小 - sizeof(p + 1):
p里面放的是常量字符串中首元素的地址,+1跳过一个char*类型,得到第二个元素的地址,是地址大小就是4/8字节大小 - sizeof(*p):
*p得到常量字符串中的首元素’a’,因为是char类型的,所以结果是1字节 - sizeof(p[0]):
p[0]可以翻译为*(p+0),得到的是常量字符串中的首元素’a’,结果是1字节 - sizeof(&p):
&p表示取出指针变量p的指针,是地址大小就是4/8字节 - sizeof(&p + 1):
&p表示取出指针变量p的指针,+1表示跳过一个char**,得到p后面的空间的地址,是地址大小就是4/8字节大小 - sizeof(&p[0] + 1):
&p[0] + 1表示第二个元素的地址,是地址大小就是4/8字节大小 - strlen§:
p里面放的是常量字符串中首元素的地址,strlen是从传进去的地址开始数,直到遇到’\0’,常量字符串有个隐藏的‘\0’,结果是6 - strlen(p + 1):
p+1表示第二个元素的地址,从第二个元素开始数到’\0’一共有5个字符,所以结果是5 - strlen(*p):
*p表示首元素‘a’,访问没有分配的内存空间,会发生非法访问 - strlen(p[0]):
p[0]表示首元素‘a’,访问没有分配的内存空间,会发生非法访问 - strlen(&p):
&p表示取出指针变量p的指针,从该地址开始数,不知道什么时候才会遇到‘\0’,所以结果是随机值 - strlen(&p + 1):
&p+1表示指针变量p后面的空间地址,从该地址开始数,不知道什么时候才会遇到‘\0’,所以结果是随机值 - strlen(&p[0] + 1):
&p[0] + 1表示第二个元素的地址,从该地址开始数,需要经过5个字符才会遇到‘\0’,所以结果是5
2.3 二维数组
int main()
{
//二维数组
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])); //16
printf("%d\n", sizeof(a[0] + 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))); //16
printf("%d\n", sizeof(&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])); //16
return 0;
}
解析:
- sizeof(a):
sizeof(数组名)表示计算整个数组所占内存空间的大小,该数组共有34=12个元素,每个元素是int类型,为4个字节大小,所以结果为124=48字节 - sizeof(a[0][0]):
a[0][0]可以翻译为*((a+0)+0)或者是(a[0]+0),得到的是第0行第0列的元素,因为元素是int类型的,所占空间大小为4字节,所以结果为4字节 - sizeof(a[0]):
a[0]可以翻译为*(a+0),得到的该数组的第一个一维数组,这个数组一共有4个元素,且是int类型的,所以结果为4*4=16字节大小
或
sizeof(数组名)表示计算整个数组的大小,这里是计算第一个一维数组的大小,为16字节 - sizeof(a[0] + 1):
a[0]是数组名表示的是第一行第一列元素的地址,+1得到第一行第二个元素的地址,是地址大小就是4/8个字节 - sizeof(*(a[0] + 1)):
*(a[0])表示取出第一行第一列的元素,加一得到第一行第二个元素,因为是int类型,所以结果为4字节 - sizeof(a + 1):
a是该二维数组的数组名,表示首元素的地址,该二维数组的首元素是a[0],+1得到a[1]即第二行数组的地址,是地址大小就是4/8个字节 - sizeof(*(a + 1)):
(a+1)表示的是第二行数组的地址,解引用后得到第二行数组,这个数组一共有4个元素,且是int类型的,所以结果为4*4=16字节大小 - sizeof(&a[0] + 1):
&数组名表示取出整个数组的地址,+1来到第二个数组的地址,是地址大小就是4/8个字节 - sizeof(*(&a[0] + 1)):
得到的是第二个数组里的所有元素,大小是4*4=16个字节 - sizeof(a):
a表示首元素a[0]的地址,解引用得到第一个数组里的所有元素,大小是44=16个字节 - sizeof(a[3]):
a[3]是不存在的,如果存在的话,是计算第四个一维数组的大小,为16个字节