🎐 前情提要
✨本篇接上一篇C语言进阶の指针的知识,帮助大家进一步理解指针and数组的知识!
让我们开始吧!
👩🏻🏫作者: 初入编程的菜鸟哒哒
📚系列文章目录:一、TvT——C语言初阶の数据存储
二、TvT——C语言进阶の指针
三、TvT——C语言进阶の字符函数和字符串函数
四、TvT——C语言进阶の自定义类型:结构体,枚举,联合
文章目录
🥝一、sizeof与strlen的计算
🍭1.一些使用sizeof与strlen的基本认识
- 先对
数组名
有个简要认知 - 数组名是首元素地址,但是有两个例外
- sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小,单位是字节
- &数组名 - 数组名表示整个数组,取出的是数组的地址
除上面两种特殊情况外,所有的数组名表示数组首元素地址
- 再对
sizeof
和strlen
有个简要认知:
- strlen: 是一个库函数,计算的是字符串的长度,并且只能针对字符串,遇‘\0’停止,计算的是\0之前的字符个数。
- sizeof: 是一个操作符(运算符),sizeof是用来计算变量所占内存空间的大小,任何类型都可以使用,只关注空间大小。
🍭2.sizeof计算整型数组
#include<stdio.h>
int main()
{
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a + 0));
printf("%d\n", sizeof(*a));
printf("%d\n", sizeof(a + 1));
printf("%d\n", sizeof(a[1]));
printf("%d\n", sizeof(&a));
printf("%d\n", sizeof(*&a));
printf("%d\n", sizeof(&a + 1));
printf("%d\n", sizeof(&a[0]));
printf("%d\n", sizeof(&a[0] + 1));
return 0;
}
请大家计算一下上面的结果都是什么呢?
✅答案是:16 4/8 4 4/8 4 4/8 16 4/8 4/8 4/8
解析:
sizeof(a);
:16 - 数组名a单独放在szieof内部 - 计算的是数组总大小 - 单位是字节 :4*4=16
sizeof(a + 0)
: 4/8 - a表示的首元素的地址,a+0还是首元素地址,地址的大小就是(32位是4,64位是8)字节
sizeof(*a)
: 4 - a表示的首元素的地址,*a就是对首元素地址的解引用,*a就是首元素a[0],sizeof(*a)就是4字节
sizeof(a + 1)
: 4/8 - a表示的首元素的地址,a+1是第2个元素地址,地址的大小就是(32位是4,64位是8)字节
sizeof(a[1])
: 4 - a[1]是数组第二个元素的大小,4字节
sizeof(&a)
: 4/8 - &a取出的是数组的地址,但是数组的地址也是地址,地址的大小就是(32位是4,64位是8)字节
sizeof(*&a)
: 16 - 可以理解为 * 和&抵消效果,* &a相当于a,sizeof(a)是16字节
本质理解:
&a -> int( * )[4]
&a是数组的地址,它的类型是int(*)[4]数组指针,如果解引用,访问的就是4个int的数组,大小是16个字节
sizeof(&a + 1)
:4/8 - &a是数组地址,&a+1虽然地址跳过整个数组,但还是地址,所以是(32位是4,>64位是8)字节,补充:&a到&a+1跳过了一个数组,a到a+1跳过了一个元素
sizeof(&a[0])
: 4/8 - &a[0]就是第一个元素的地址(32位是4,64位是8)字节
sizeof(&a[0] + 1)
:4/8 - &a[0]就是第一个元素的地址,再+1就是第二个元素的地址(32位是4,64位是8)字节
🍭3.sizeof与strlen计算不同形式的字符数组
1️⃣ char arr[] = { ‘a’,‘b’,‘c’,‘d’,‘e’,‘f’ };
🍐 sizeof()计算char arr[] = { ‘a’,‘b’,‘c’,‘d’,‘e’,‘f’ };
#include<stdio.h>
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
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;
}
请大家计算一下上面的结果都是什么呢?
✅答案是:6 4/8 1 1 4/8 4/8 4/8
解析:
sizeof(arr);
:6 - arr作为数组名单独放在sizeof内部,计算的整个数组的大小,单位是字节,6字节
sizeof(arr + 0)
: 4/8 - arr是首元素的地址,arr+0还是首元素的地址,地址的大小就是(32位是4,64位是8)字节
sizeof(*arr)
: 1 - arr是首元素地址,*arr就是首元素,首元素是一个字符,大小是1个字节
sizeof(arr[1])
: 1 - arr[1]相当于第二个元素的大小,是一个字符,大小就是1字节
sizeof(&arr)
: 4/8 - &arr虽然是数组的地址,但还是地址,地址的大小就是(32位是4,64位是8)字节
sizeof(&arr + 1)
:4/8 - &arr+1是跳过整个数组后的地址,但还是地址,地址的大小就是(32位是4,64位是8)字节
sizeof(&arr[0] + 1)
:4/8 - &arr[0]是第一个元素的地址,&arr[0]+1就是第二个元素的地址,地址的大小就是(32位是4,64位是8)字节
🍐 strlen()计算char arr[] = { ‘a’,‘b’,‘c’,‘d’,‘e’,‘f’ };
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = { 'a','b','c','d','e','f'};
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;
}
请大家计算一下上面的结果都是什么呢?
✅答案是:随机值 随机值 err err 随机值 随机值 随机值
解析:
strlen(arr);
:随机值 - arr是首元素的地址,但是arr数组中没有\0,计算的时候就不知道什么时候停止
strlen(arr + 0)
: 随机值 - arr是首元素的地址,arr+0还是首元素的地址
strlen(*arr)
: err - strlen需要的是一个地址,从这个地址开始向后找字符,直到\0,统计字符的个数。
但是*arr是数组的首元素,也就是’a’,这是传给strlen的就是’a’的ascii码值97,strlen函数会把97作为起始地址,统计字符串,会形成内存访问冲突
strlen(arr[1])
: err - 和上一个一样,内存访问冲突
strlen(&arr)
: 随机值 - &arr是arr数组的地址,虽然类型和strlen的参数类型有所差异,但是传参过去后,还是从第一个字符的位置向后数字符,结果还是随机值
strlen(&arr + 1)
:随机值 - &arr是数组的地址,+1跳过整个数组,但是arr数组中没有\0,计算的时候就不知道什么时候停止
strlen(&arr[0] + 1)
:随机值 - 这里相当于从第二个字符的地址开始向后访问,又因为没有\0,所以还是随机值
2️⃣ char arr[] = “abcdef”;
🍐 sizeof()计算char arr[] = “abcdef”;
#include<stdio.h>
int main()
{
char arr[] = "abcdef";
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;
}
请大家计算一下上面的结果都是什么呢?
✅答案是:7 4/8 1 1 4/8 4/8 4/8
解析:
sizeof(arr);
:7 - sizeof(arr)计算的数组的大小,单位是字节
sizeof(arr + 0)
:4/8 - arr+0是首元素的地址,既然是地址,(32位是4,64位是8)字节
sizeof(*arr)
: 1 - arr是首元素地址,首元素地址被解引用,*arr拿到的就是首元素,大小是1字节
sizeof(arr[1])
: 1 - arr[1]相当于第二个元素的大小,是一个字符,大小就是1字节
sizeof(&arr)
: 4/8 - &arr虽然是数组的地址,但还是地址,地址的大小就是(32位是4,64位是8)字节
sizeof(&arr + 1)
:4/8 - &arr+1是跳过整个数组后的地址,但还是地址,地址的大小就是(32位是4,64位是8)字节
sizeof(&arr[0] + 1)
:4/8 - &arr[0]是第一个元素的地址,&arr[0]+1就是第二个元素的地址,地址的大小就是(32位是4,64位是8)字节
🍐 strlen()计算char arr[] = “abcdef”;
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "abcdef";
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;
}
请大家计算一下上面的结果都是什么呢?
✅答案是:6 6 err err 6 随机值 5
解析:
strlen(arr);
:6 - arr表示首元素地址,数到\0停下
strlen(arr + 0)
: 6 - arr表示首元素地址,+0还是首元素地址,数到\0停下
strlen(*arr)
: err - strlen需要的是一个地址,从这个地址开始向后找字符,直到\0,统计字符的个数。
但是*arr是数组的首元素,也就是’a’,这是传给strlen的就是’a’的ascii码值97,strlen函数会把97作为起始地址,统计字符串,会形成内存访问冲突
strlen(arr[1])
: err - 和上一个一样,内存访问冲突
strlen(&arr)
: 6 - &arr表示取出的整个数组地址,但是整个数组的地址还是这个数组的起始地址,还是从起始元素数到\0停止
strlen(&arr + 1)
:随机值 - &arr表示取出的整个数组地址,+1表示跳过这整个数组,也跳过了原数组的\0,此时又找不到\0了,随机值
strlen(&arr[0] + 1)
:5 - &arr[0]就是第一个元素的地址,+1就是第二个元素的地址,即从第二个元素开始数到\0停止
3️⃣ char* p = “abcdef”;
🍐 sizeof()计算char* p = “abcdef”;
#include<stdio.h>
int main()
{
char* p = "abcdef";
printf("%d\n", sizeof(p)); // 4/8 p是一个指针变量,sizeof(p)计算的就是指针变量的大小,(32位是4,64位是8)字节
printf("%d\n", sizeof(p + 1)); // 4/8 p是指针变量,是存放地址的,p+1也是地址,(32位是4,64位是8)字节
printf("%d\n", sizeof(*p)); // 1 p是char*的指针,解引用访问1个字节,sizeof(*p)是1字节,可理解为p[0]-->*(p+0)-->*p
printf("%d\n", sizeof(p[0])); // 1 p[0]-->*(p+0)-->*p
printf("%d\n", sizeof(&p)); // 4/8 &p也是地址,(32位是4,64位是8)字节,&p是二级指针
printf("%d\n", sizeof(&p + 1)); // 4/8 &p是地址,+1后还是地址,(32位是4,64位是8)字节,&p + 1,是p的地址+1,在内存中跳过p变量后的地址
printf("%d\n", sizeof(&p[0] + 1));// 4/8 p[0]就是a,&p[0]就是a的地址,&p[0]+1就是b的地址,(32位是4,64位是8)字节
return 0;
}
请大家计算一下上面的结果都是什么呢?
✅答案是:4/8 4/8 1 1 4/8 4/8 4/8
解析:
sizeof(p);
:4/8 - p是一个指针变量,sizeof§计算的就是指针变量的大小,(32位是4,64位是8)字节
sizeof(p + 0)
: 4/8 - p是指针变量,是存放地址的,p+1也是地址,(32位是4,64位是8)字节
sizeof(*p)
: 1 - p是char*的指针,解引用访问1个字节,sizeof(p)是1字节,可理解为p[0]–>(p+0)–>*p
sizeof(p[0])
: 1 - p[0]–>*(p+0)–>*p
sizeof(&p)
: 4/8 &p也是地址,(32位是4,64位是8)字节,&p是二级指针
sizeof(&p + 1)
:4/8 - &p是地址,+1后还是地址,(32位是4,64位是8)字节,&p + 1,是p的地址+1,在内存中跳过p变量后的地址
sizeof(&p[0] + 1)
4/8 - p[0]就是a,&p[0]就是a的地址,&p[0]+1就是b的地址,(32位是4,64位是8)字节
🍐 strlen()计算char* p = “abcdef”;
#include<stdio.h>
#include<string.h>
int main()
{
char* p = "abcdef";
printf("%d\n", strlen(p));
printf("%d\n", strlen(p + 1));
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));
return 0;
}
请大家计算一下上面的结果都是什么呢?
✅答案是:6 5 err err 随机值 随机值 5
解析:
strlen(p);
:6 - p中存放的是’a’的地址,strlen§就是从’a’的位置向后求字符串的长度,长度是6
strlen(p + 0)
: 5 - p+1是’b’的地址,从b的位置开始求字符串长度是5
strlen(*p)
: err - strlen需要的是一个地址,从这个地址开始向后找字符, 直到\0,统计字符的个数。
但是*p是数组的首元素,也就是’a’,这是传给strlen的就是’a’的ascii码值97,strlen函数会把97作为起始地址,统计字符串,会形成内存访问冲突
strlen(p[0])
: err - 理由同上
strlen(&p)
: 随机值 - p中存放的是’a’的地址,&p放的是p的地址,在p的地址中strlen找不到\0,所以随机值
strlen(&p + 1)
:随机值 - 理由类似上部
strlen(&p[0] + 1)
: 5 - p[0] -> *(p+0) -> *p ->‘a’ ,&p[0]就是首字符的地址,&p[0]+1就是第二个字符的地址从第2 字符的位置向后数字符串,长度是5
🍭4.sizeof计算二维数组
#include<stdio.h>
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a[0][0]));
printf("%d\n", sizeof(a[0]));
printf("%d\n", sizeof(a[0] + 1));
printf("%d\n", sizeof(*(a[0] + 1)));
printf("%d\n", sizeof(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));
printf("%d\n", sizeof(a[3]));
return 0;
}
请大家计算一下上面的结果都是什么呢?
✅答案是:48 4 16 4/8 4 4/8 16 4/8 16 16 16
解析:
sizeof(a)
:48 - 数组名单独放置sizeof内部,计算的是整个数组的大小,344=48字节
sizeof(a[0][0])
:4 - a[0][0]表示第一行第一个元素的大小,类型是int,大小为4字节
sizeof(a[0])
: 16 - a[0]表示第一行的数组名,a[0]作为数组名单独放在sizeof内部,计算的是第一行数组的大小:16字节
但是*p是数组的首元素,也就是’a’,这是传给strlen的就是’a’的ascii码值97,strlen函数会把97作为起始地址,统计字符串,会形成内存访问冲突
sizeof(a[0] + 1)
: 4/8 - a[0]作为第一行的数组名,没有&,没有单独放在sizeof内部,所以a[0]表示的就是首元素的地址,即a[0][0]的地址,a[0]+1就是第一行第二个元素的地址,(32位是4,64位是8)字节
sizeof(*(a[0] + 1))
:4 - 既然解引用了,就是第一行第二个元素的大小4字节
sizeof(a + 1)
:4/8 - a是二维数组的数组名,没有&,没有单独放在sizeof内部,a表示首元素的地址,即第一行的地址,a+1就是第二行地址。(32位是4,64位是8)字节,是类型为int()[4]的数组指针。
sizeof(*(a + 1))
: 16 -(a+1)就是第二行,相当于第二行的数组名,(a+1)–>a[1],sizeof((a+1))计算的是第二行的大小,16字节
sizeof(&a[0] + 1)
: 4/8 - a[0]是第一行的地址,&a[0]是第1行的地址,&a[0] + 1就是第二行的地址,(32位是4,64位是8)字节
sizeof(*(&a[0] + 1))
: 16 - *(&a[0] + 1)就相当于第二行,也就是a[1],sizeof(a[1]),大小是16字节
ssizeof(*a))
:16 - a二维数组的数组名,没有&,没有单独放在sizeof内部,a表示首元素的地址,*a就是二维数组的首元素,也就是第一行。a–>(a+0)–>a[0]
sizeof(a[3])
: 16 - 感觉a[3]是越界了,但是没关系,依旧把它当成是求某行的大小,16字节