当前位置: 首页 > news >正文

C语言:指针(5)

1. sizeof与strlen的对比

1.1 sizeof

sizeof属于是操作符,用于计算变量所占的空间大小,单位为字节。如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。

sizeof只计算数据在内存中所占的空间大小,而不在乎内存中存放的数据类型。

例如:

int main()
{int a = 10;printf("%d\n",sizeof a);printf("%d\n", sizeof(a));printf("%d\n", sizeof(int));return 0;
}
4
4
4

1.2 strlen

strlen是C语言库函数,其功能为求字符串长度。函数原型如下:

 size_t strlen ( const char * str );

strlen函数会统计指针str指向的那个地址向后直到字符 '\0' 之前的所有字符个数。

但是,strlen函数会一直向后寻找直到找到 '\0' 为止,所以说有越界访问的可能性。

int main()
{char arr1[] = "abd";char arr2[] = {'a','b','c'};printf("%d\n", strlen(arr1));printf("%d\n", strlen(arr2));return 0;
}
3
6

这里在创建第二个字符串时没有加上 '\0' ,所以第二行输出的是一个随机结果。像arr1这样创建字符串,编译器会自动在末尾加上一个 '\0' ,所以第一行的输出结果是确定的。

1.3 sizeof 和strlen的对比

sizeof

1.sizeof是操作符

2.sizeof计算操作数所占内存的大小,单位是字节

3.不关注内存中存放什么数据

strlen

1. strlen是库函数,使用需要包含头文件 <string.h>

2.strlen用于求字符串长度,统计字符串中字符 '\0' 之前字符的个数

3.关注内存中是否存在字符 '\0' ,如果不存在,就会一直向后寻找,可能会越界。

2. 数组和指针题目解析

2.1 一维数组

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;
}

下面为解析:

int main()
{int a[] = {1,2,3,4};printf("%d\n",sizeof(a));  //sizeof中a单独出现,表示整个数组,结果为16printf("%d\n",sizeof(a+0));//(a+0),这里a表示数组首元素地址,整体属于整型指针,结果为4/8printf("%d\n",sizeof(*a));//(*a),这里的a表示数组首元素地址,*a表示首元素,类型为整型,结果为4printf("%d\n",sizeof(a+1));//(a+1),a表示数组首元素地址,与整数1相加,整体属于整型指针,结果为4/8printf("%d\n",sizeof(a[1]));//a[1],a表示数组首元素地址,利用操作符[1]来访问数组中下标为1的元素,类型为整型,结果为4printf("%d\n",sizeof(&a));//&a,得到的是整个数组的地址,类型为int(*)[4],结果为4/8printf("%d\n",sizeof(*&a));//(*&a),操作符*与&抵消,最终为a,表示整个数组,结果为16printf("%d\n",sizeof(&a+1));//&a+1,&a得到整个数组地址,+1后跳过整个数组,但是其结果类型依旧为 int(*)[4],结果为4/8printf("%d\n",sizeof(&a[0]));//&a[0],a[0]访问数组中下标为0的元素,即首元素,操作符&得到该元素的地址,类型为整型指针,结果为4/8printf("%d\n",sizeof(&a[0]+1));//&a[0]+1,&a[0]得到数组首元素地址,+1后跳过一个元素,结果类型为整型指针,结果为4/8return 0;
}
16
8
4
8
4
8
16
8
8
8

2.2 字符数组

2.2.1 字符串末尾无 '\0'

代码1:

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;
}

解析:

int main()
{char arr[] = {'a','b','c','d','e','f'};printf("%d\n", sizeof(arr));//szieof中数组名arr单独出现,表示整个数组,结果为6printf("%d\n", sizeof(arr+0));//arr+0,表示数组首元素地址,类型为字符指针,结果为4/8printf("%d\n", sizeof(*arr));//*arr,arr表示数组首元素地址,*arr表示首元素,类型为字符,结果为1printf("%d\n", sizeof(arr[1]));//arr[1],表示数组中下标为1的元素,类型为字符,结果为1printf("%d\n", sizeof(&arr));//&arr,&arr得到整个数组的地址,类型为 char(*)[6],结果为4/8printf("%d\n", sizeof(&arr+1));//&arr+1,&arr得到整个数组的地址,+1跳过整个数组,但结果类型依旧为为char(*)[6],结果为4/8printf("%d\n", sizeof(&arr[0]+1));//&arr[0]+1,&arr[0]得到首元素的地址,+1后向后跳过一个元素,结果类型为字符指针,结果为4/8return 0;
}
6
8
1
1
8
8
8

代码2:

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;
}

解析:

int main()
{char arr[] = {'a','b','c','d','e','f'};printf("%d\n", strlen(arr));//arr表示数组首元素地址,但是数组中没有字符'\0',strlen函数会从arr指向的地址向后一直寻找直到找到'\0',结果为随机值printf("%d\n", strlen(arr+0));//arr表示首元素地址,+0后依旧为首元素地址,结果同第一行结果printf("%d\n", strlen(*arr));//arr为首元素地址,*arr得到首元素字符a,被传递给函数后,a会被转化为其ASCII码值97,并且函数会将97作为地址处理,但是这个地址通常是无法访问的,因此编译时会报错,运行会导致程序崩溃printf("%d\n", strlen(arr[1]));//arr[1],为数组中下标为1的元素,即为b,其结果同第三行printf("%d\n", strlen(&arr));//&arr,&arr得到整个数组的地址,strlen函数会从arr指向的地址向后一直寻找直到找到'\0',结果为随机值printf("%d\n", strlen(&arr+1));//&arr+1,&arr得到整个数组的地址,+1后跳过整个数组,strlen函数向后一直寻找直到找到'\0',结果为随机值    printf("%d\n", strlen(&arr[0]+1));//&arr[0]+1,&arr[0]得到数组首元素的地址,+1跳过一个元素,strlen函数向后一直寻找直到找到'\0',结果为随机值    return 0;
}

将编译错误的语句注释之后得到如下的打印结果:

11
11
11
5
10

2.2.2 字符串末尾存在 '\0'

代码3:

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;
}

解析:

int main()
{char arr[] = "abcdef";//这样创建字符串时,编译器为自动在字符串末尾加上一个'\0'printf("%d\n", sizeof(arr));//sizeof中数组名arr单独出现,表示整个数组,数组中一共有"abcdef"以及末尾的'\0',类型都为字符,因此结果为7//其他的打印不受影响,不过&arr的类型变为了char(*)[7],结果与上面一致,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
8
1
1
8
8
8

代码4:

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;
}

解析:

int main()
{char arr[] = "abcdef";//这样创建字符串,编译器会自动在字符串末尾加上'\0'printf("%d\n", strlen(arr));//arr,arr表示数组首元素,字符串末尾中有'\0',结果为6printf("%d\n", strlen(arr+0));//arr+0,arr表示数组首元素,+0后依旧为数组首元素,字符串末尾中有'\0',结果为6printf("%d\n", strlen(*arr));//编译器报错,原因同代码2printf("%d\n", strlen(arr[1]));//编译器报错,原因同代码2printf("%d\n", strlen(&arr));//&arr,&arr得到整个数组的地址,字符串末尾中有'\0',结果为6printf("%d\n", strlen(&arr+1));//&arr+1,&arr得到整个数组的地址,+1后跳过整个数组,strlen函数向后一直寻找直到找到'\0',结果为随机值printf("%d\n", strlen(&arr[0]+1));//&arr[0]+1,&arr[0]得到数组首元素的地址,+1后跳过一个元素,字符串末尾存在'\0',结果为5return 0;
}

将报错语句注释掉后的打印结果:

6
6
6
5
5

这里,倒数二行的输出结果为5属于是巧合,将字符串中的字符删去两个后,其结果如下:

int main()
{char arr[] = "abcf";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[0]+1));return 0;
}
4
4
4
5
3

可以发现,除了倒数第二行打印结果不变外,其余打印结果都发生了变化,并且刚好是原本结果减去2。

2.2.3 用字符型指针存储常量字符串地址

代码5:

int main()
{char *p = "abcdef";printf("%d\n", sizeof(p));printf("%d\n", sizeof(p+1));printf("%d\n", sizeof(*p));printf("%d\n", sizeof(p[0]));printf("%d\n", sizeof(&p));printf("%d\n", sizeof(&p+1));printf("%d\n", sizeof(&p[0]+1));return 0;
}

解析:

int main()
{char *p = "abcdef";//将字符串首字符地址存储在字符型指针p中printf("%d\n", sizeof(p));//p在sizeof中单独使用,表示字符串首字符的地址,类型为字符型指针,结果为4/8printf("%d\n", sizeof(p+1));//p+1,p代表字符串首字符的地址,+1后跳过一个字符,结果类型依旧为字符型指针,结果为4/8printf("%d\n", sizeof(*p));//*p,p为字符串首字符地址,*p得到首字符,即为a,类型为字符,结果为1printf("%d\n", sizeof(p[0]));//p[0],编译器处理p[0]时,转化为*(p+0),结果同第三行printf("%d\n", sizeof(&p));//&p,得到指针p的地址,类型为char**,结果为4/8printf("%d\n", sizeof(&p+1));//&p,得到指针p的地址,+1后跳过一个char**类型的空间大小即跳过指针p的地址,指向存储指针p处地址后的地址,结果类型依旧为char**,结果为4/8printf("%d\n", sizeof(&p[0]+1));//&p[0]+1,转化为&*(p+0)+1,*与&抵消,因此结果为p+1,指向字符中第二个字符处,即为存储字符b的地址,结果类型为char*,结果为4/8return 0;
}
8
8
1
1
8
8
8

代码6:

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;
}

解析:

int main()
{char *p = "abcdef";//字符串末尾存在'\0'printf("%d\n", strlen(p));//p为字符串首字符地址,传递给函数strlen,结果为6printf("%d\n", strlen(p+1));//p+1,p为字符串首字符地址,+1后跳过一个字符,传递给函数strlen,结果为5printf("%d\n", strlen(*p));//编译器报错,结果同代码2printf("%d\n", strlen(p[0]));//编译器报错,结果同代码2printf("%d\n", strlen(&p));//&p,&p得到指针p的地址,地址未知,但肯定不与字符串中任意一个字符重叠,传递给函数strlen后,结果为随机值printf("%d\n", strlen(&p+1));//&p,&p得到指针p的地址,+1后地址依旧未知,但肯定不与字符串中任意一个字符重叠,传递给函数strlen后,结果为随机值printf("%d\n", strlen(&p[0]+1));//&p[0]+1,&p[0]得到字符串首字符地址,+1后跳过一个字符,传递给函数strlen,结果为5return 0;
}
6
5
0
5
5

这里的倒数第二行打印结果也属于巧合,可以跳过修改字符串验证:

int main()
{char *p = "abef";printf("%d\n", strlen(p));printf("%d\n", strlen(p+1));printf("%d\n", strlen(&p));printf("%d\n", strlen(&p+1));printf("%d\n", strlen(&p[0]+1));return 0;
}
4
3
0
5
3

2.3 二维数组

代码7:

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;
}

解析:

int main()
{int a[3][4] = {0};//创建一个二维数组并将内部元素全部初始化为0printf("%d\n",sizeof(a));//sizeof中数组名a单独出现,表示整个数组,结果为48printf("%d\n",sizeof(a[0][0]));//a[0][0],a[0][0]表示数组中首元素,类型为整型数据,结果为4printf("%d\n",sizeof(a[0]));//二维数组a中,当sizeof中a[0]单独出现,表示数组中的整个第一行,结果为16printf("%d\n",sizeof(a[0]+1));//a[0]+1,此处a[0]表示数组第一行首元素地址,+1后跳过一个元素,最终结果类型为整型指针,打印结果为4/8printf("%d\n",sizeof(*(a[0]+1)));//*(a[0]+1),a[0]+1表示数组中第一行第二个元素的地址,加上*表示该地址处存储的数据,该数据类型为整型,结果为4printf("%d\n",sizeof(a+1));//a+1,a代表二维数组首行的地址,+1后跳过一整行,表示数组中第二行的地址,结果为4/8printf("%d\n",sizeof(*(a+1)));//*(a+1),a+1表示数组中第二行的地址,加上*,得到整个第二行,结果为16printf("%d\n",sizeof(&a[0]+1));//&a[0]+1,&a[0]表示数组中整个第一行的地址,类型为int(*)[4],+1后跳过一整行,但数据类型依旧为int(*)[4],结果为4/8printf("%d\n",sizeof(*(&a[0]+1)));//&a[0]+1,&a[0]表示数组中整个第一行的地址,类型为int(*)[4],+1后跳过一整行,此时指向第二行地址,加上*后,得到整个第二行,结果为16printf("%d\n",sizeof(*a));//*a,a表示二维数组首行的地址,加上*后,表示整个数组首行,结果为16printf("%d\n",sizeof(a[3]));//a[3]表示数组整个第四行,虽然此时属于数组访问越界,但是编译器依旧将其作为数组的行类型处理,结果为16return 0;
}
48
4
16
8
4
8
16
8
16
16
16

数组名的意义:

1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。

2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。

3. 除此之外所有的数组名都表示首元素的地址。

http://www.lryc.cn/news/622438.html

相关文章:

  • 智能指针:C++内存管理的利器
  • c++程序示例:多线程下的实例计数器
  • [HDCTF 2023]Normal_Rsa(revenge)
  • 主流开源实时互动数字人大模型
  • 读书笔记-积极心理学 《心流,最优体验心理学》
  • 条件变量的基本介绍与有界缓冲区问题
  • 小红书帖子评论的nodejs爬虫脚本
  • 补充日志之-配置文件解析指南(Centos7)
  • CAXA电子图板2026(国产CAD之光)
  • 机器学习之PCA
  • pyqt5无法显示opencv绘制文本和掩码信息
  • OpenCV 阈值处理
  • 应用侧华为云LoTDA设备接入平台
  • 重塑工业设备制造格局:明远智睿 T113-i 的破局之道
  • IgH初始化--未接从站
  • 【160页PPT】机械行业数字化生产供应链产品解决方案(附下载方式)
  • 光伏工单智能管理,故障处理快人一步
  • 状态流程框架(cola-component-statemachine)
  • WinForm 简单用户登录记录器实现教程
  • 五、ZooKeeper、Kafka、Hadoop、HBase、Spark、Flink集群化软件的部署
  • Sui 主网升级至 V1.53.2
  • 【DDIA】第三部分:衍生数据
  • 俄罗斯信封套娃问题-二维最长递增子序列
  • day29-进程和线程(2)
  • python自学笔记9 Seaborn可视化
  • 47.分布式事务理论
  • Elasticsearch:使用 Gradio 来创建一个简单的 RAG 应用界面
  • LeetCode刷题记录----236.二叉树的最近公共节点(medium)
  • 终极手撸cpu系列-详解底层原理-CPU硬核解剖:从0和1到 看透CPU逻辑设计内部原理,弄清楚现代多线程cpu工作原理
  • IC(Integrated Circuit,集成电路)是什么?