C语言char, char*, char**, char*[]类型及用法详细测试解析

您所在的位置:网站首页 C语言如何打印char C语言char, char*, char**, char*[]类型及用法详细测试解析

C语言char, char*, char**, char*[]类型及用法详细测试解析

2024-07-15 02:27:02| 来源: 网络整理| 查看: 265

背景:初学C语言时,由于对于char家族一系列类型掌握不佳,遇到char*, char**, char*[]时总是晕头转向。今特地编写用例,详细辨析其中的区别

环境:Win10 64位

语言:C

编译器:gcc (x86_64-posix-sjlj-rev0, Built by MinGW-W64 project) 8.1.0

源码:

#include #include #include int main() { // part 1 char c = 'a'; printf("c = %c, c: 0x%p;\n", c, &c); // part 2 char *s1 = "hello world"; printf("s1 = %s, s1: 0x%p, s1[0] = %c, sizeof(s1) = %d, strlen(s1) = %d;\n", s1, s1, s1[0], sizeof(s1), strlen(s1)); char s2[10] = "thank you"; printf("s2 = %s, s2: 0x%p, s2[0] = %c, sizeof(s2) = %d, strlen(s2) = %d;\n", s2, s2, s2[0], sizeof(s2), strlen(s2)); s1 = "world hello"; printf("s1' = %s;\n", s1); s1 = s2; printf("s1'' = %s;\n", s1); // error: s2 = "you thank"; // error :s2 = s1; // part 3 char *s3[] = {"apple", "orange", "banana"}; printf("s3 = %s, s3: 0x%p;\n", s3, s3); printf("s3[0] = %s, s3[0]: 0x%p, s3[0][0] = %c, s3[0][0]: 0x%p, sizeof(s3[0]) = %d, strlen(s3[0]) = %d;\n", s3[0], s3[0], s3[0][0], &s3[0][0], sizeof(s3[0]), strlen(s3[0])); printf("s3[1] = %s, s3[1]: 0x%p, s3[1][0] = %c, s3[1][0]: 0x%p, sizeof(s3[1]) = %d, strlen(s3[1]) = %d;\n", s3[1], s3[1], s3[1][0], &s3[1][0], sizeof(s3[1]), strlen(s3[1])); printf("s3[2] = %s, s3[2]: 0x%p, s3[2][0] = %c, s3[2][0]: 0x%p, sizeof(s3[2]) = %d, strlen(s3[2]) = %d;\n", s3[2], s3[2], s3[2][0], &s3[2][0], sizeof(s3[2]), strlen(s3[2])); printf("sizeof(s3) = %d, sizeof(*s3) = %d, sizeof(**s3) = %d, strlen(*s3) = %d\n", sizeof(s3), sizeof(*s3), sizeof(**s3), strlen(*s3)); printf("&s3[0]: 0x%p, &s3[1]: 0x%p, &s3[2]: 0x%p\n", &s3[0], &s3[1], &s3[2]); printf("*s3 = %s, *(s3 + 1) = %s, *(s3 + 2) = %s\n", *s3, *(s3 + 1), *(s3 + 2)); char **s4 = s3; printf("s4 = %s, s4: 0x%p\n", s4, s4); printf("*s4 = %s, *s4: 0x%p\n", *s4, *s4); printf("**s4: 0x%p\n", **s4); printf("sizeof(s4) = %d, sizeof(*s4) = %d, sizeof(**s4) = %d, strlen(*s4) = %d\n", sizeof(s4), sizeof(*s4), sizeof(**s4), strlen(*s4)); printf("&s4[0]: 0x%p, &s4[1]: 0x%p, &s4[2]: 0x%p\n", &s4[0], &s4[1], &s4[2]); printf("*s4 = %s, *(s4 + 1) = %s, *(s4 + 2) = %s\n", *s4, *(s4 + 1), *(s4 + 2)); // part 4 char **s5 = (char **)malloc(sizeof(char **)); *s5 = "hello world"; printf("s5: 0x%p, *s5: 0x%p, *s5 = %s\n", s5, *s5, *s5); *s5 = "thank you"; printf("s5': 0x%p, *s5': 0x%p, *s5' = %s\n", s5, *s5, *s5); // part 5 char *s6; char **s7 = &s6; *s7 = "hello world"; printf("s6 = %s, s6: 0x%p\n", s6, s6); s6 = "thank you"; printf("s6 = %s, s6: 0x%p\n", s6, s6); while(getchar() != '\n') {} return 0; }

输出:

// part 1 c = a, c: 0x000000000061FDFF; // part 2 s1 = hello world, s1: 0x0000000000405012, s1[0] = h, sizeof(s1) = 8, strlen(s1) = 11; s2 = thank you, s2: 0x000000000061FDF5, s2[0] = t, sizeof(s2) = 10, strlen(s2) = 9; s1' = world hello; s1'' = thank you; // part 3 s3 = 蚉@, s3: 0x000000000061FDD0; s3[0] = apple, s3[0]: 0x00000000004050CD, s3[0][0] = a, s3[0][0]: 0x00000000004050CD, sizeof(s3[0]) = 8, strlen(s3[0]) = 5; s3[1] = orange, s3[1]: 0x00000000004050D3, s3[1][0] = o, s3[1][0]: 0x00000000004050D3, sizeof(s3[1]) = 8, strlen(s3[1]) = 6; s3[2] = banana, s3[2]: 0x00000000004050DA, s3[2][0] = b, s3[2][0]: 0x00000000004050DA, sizeof(s3[2]) = 8, strlen(s3[2]) = 6; sizeof(s3) = 24, sizeof(*s3) = 8, sizeof(**s3) = 1, strlen(*s3) = 5 &s3[0]: 0x000000000061FDD0, &s3[1]: 0x000000000061FDD8, &s3[2]: 0x000000000061FDE0 *s3 = apple, *(s3 + 1) = orange, *(s3 + 2) = banana s4 = 蚉@, s4: 0x000000000061FDD0 *s4 = apple, *s4: 0x00000000004050CD **s4: 0x0000000000000061 sizeof(s4) = 8, sizeof(*s4) = 8, sizeof(**s4) = 1, strlen(*s4) = 5 &s4[0]: 0x000000000061FDD0, &s4[1]: 0x000000000061FDD8, &s4[2]: 0x000000000061FDE0 *s4 = apple, *(s4 + 1) = orange, *(s4 + 2) = banana // part 4 s5: 0x0000000000976EF0, *s5: 0x0000000000405012, *s5 = hello world s5': 0x0000000000976EF0, *s5': 0x00000000004053CF, *s5' = thank you // part 5 s6 = hello world, s6: 0x0000000000405012 s6 = thank you, s6: 0x00000000004053CF \n \n Part 1:

该部分从简单字符型变量入手,定义一个字符型变量c并初始化,打印其值和地址,需要注意的是,此时的c是栈上分配的内存空间;

Part 2:

分别定义字符指针s1和字符数组s2,对于s2的大小可以隐式声明为char s2[],且虽然s1被定义为字符指针,但仍可以用数组下标的方式访问字符串中的每个字符;

重点一:观察两个sizeof的结果可以发现,s1的大小为8,而s2的大小为10。这是因为s1本质为指针,64位系统的指针大小为 64 / 8 = 8 字节,故指针大小恒定为8字节,与其地址中内容是什么无关;而s2本质为数组,在定义时就已经确定其大小为10,且每个char型变量的大小为1字节,故s2大小为 1 * 10 = 10 字节。而strlen函数的功能为求字符串的长度,也即字符串的字符个数,从给定地址开始计数,直到遇到字符串结束符’\0’时停止计数,不包含结束符;对于隐式声明的字符数组的大小和长度,请各位自行尝试;

重点二:一句话概括:字符指针可以改变其指向,但字符数组不能重复赋值,仅能在初始化时指定其内容。这是因为字符数组的数组名是常量,常量是不能被赋值的。故s1可以指向其他任意字符串或字符数组s2,而s2不能被重新赋值或指向s1,这将会导致编译失败;

Part 3:

该部分为本篇重中之重,分别定义char *[]型变量s3和char **型变量s4,并且使s4指向s3来完成对指针数组元素的访问;

重点三:对于s3,字符串"apple"的地址为0x00000000004050CD,这个地址也是字符串中第一个字符’a’的地址,而这个地址被保存在地址0x000000000061FDD0中;那这个"s3 = 蚉@“是怎么来的呢?查询"蚉"与”@"的编码,分别为0xCD50与0x40,即我们将s3中保存的地址当作一个字符串来打印了,这样做其实并没有意义,只是印证字符串"apple"的地址0x00000000004050CD被当作一个数保存在地址0x000000000061FDD0中;对于**s4的地址为什么是0x0000000000000061,想必你已经有答案了(提示:查询字符’a’的十六进制编码);

重点四:char *[]为指针数组,注意与char (*)[]的区别,后者为数组指针;前者[]优先级比*高,故本质为数组,后者从左向右先遇到()内*,故本质为指针。教大家一个实用口诀来记忆这两者:指针数组即存放指针的数组,数组指针即指向数组的指针;这样一来作用和本质便一目了然;而char**为二级指针,即指向指针的指针,可以理解为保存某一地址的地址;

重点五:由重点四可知,s3本质为数组,s4本质为指针,故两者大小有别;s3中存放3个字符串的地址,故sizeof(s3)为 8 * 3 = 24 字节,s4恒定为8字节;而sizeof(**s3)与sizeof(**s4)皆为1是因为在两次解引用后,**s3和**s4的类型已不是指针,而是字符类型char,故只占1个字节,这也就解答了为什么**s4的地址为什么是0x0000000000000061而不是字符串"apple"中每个十六进制编码的排列集合;注意,系统给s3中三个字符串分配的内存空间是连续,且存放这三个字符串地址的地址也是连续的。通过计算s3[1]的地址减去s3[0]的地址可得结果为6,即字符串"apple"的长度加1个结束符,同理可计算s3[2]的地址减去s3[1]的地址结果为7,即字符串"orange"的长度加1个结束符;但在有些系统中,给s3中三个字符串分配的内存空间并不是连续的(详见:https://blog.csdn.net/daiyutage/article/details/8604720),具体原因猜想与编译器或操作系统对内存分配的实现有关,有待考证;

重点六:对于s3中每个字符串的访问,有两种方式,例如访问字符串"orange",可以用s3[1]或*(s3 + 1);而对于字符串中字符"r"的访问,可以用s3[1][1],也可用*(s3 + 1)[1],也可用*(*(s3 + 1) + 1);

Part 4:

这一部分说明了如何用二级指针改变一级指针的指向。从两行输出结果可以看出,s5两次的地址都没有变化,都是首次申请空间时系统在堆上分配的内存;而*s5变了,也即s5中保存的值变了;

有一个有趣的现象:使*s5第一次指向字符串"hello world"时,*s5的地址为0x0000000000405012,这个地址是不是似曾相识?往前看,发现s1的地址也是这个,这也就表明,他俩在内存中指向的是同一块地址空间;但第二次指向字符串"thank you"时,得到的地址却和s2的地址不同。请大家思考一下这是为什么?

Part 5:

这一部分与上一部分类似,分别用二级指针s7和一级指针s6来改变s6本身的指向,不再赘述。需要注意的是,s6定义时未初始化,指向一个随机无效内存地址,访问可能会引起程序崩溃。

有错误之处或疑问欢迎私信或评论区留言:)



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭