我现在正在学习C并且对字符数组 - 字符串感到困惑.
char name[15]="Fortran";
Run Code Online (Sandbox Code Playgroud)
没问题 - 它的数组可以容纳(最多?)15个字符
char name[]="Fortran";
Run Code Online (Sandbox Code Playgroud)
C计算我的字符数,所以我没有 - 整洁!
char* name;
Run Code Online (Sandbox Code Playgroud)
好的.现在怎么办?我所知道的是,这可以容纳后来分配的大量字符(例如:通过用户输入),但是
提前谢谢,喇嘛
t0m*_*13b 33
我认为这可以用这种方式解释,因为一张图片胜过千言万语......
我们将从开始char name[] = "Fortran"
,这是一个字符数组,长度在编译时已知,确切地说是7,对吧?错误!它是8,因为'\ 0'是一个空终止字符,所有字符串都必须有.
char name[] = "Fortran"; +======+ +-+-+-+-+-+-+-+--+ |0x1234| |F|o|r|t|r|a|n|\0| +======+ +-+-+-+-+-+-+-+--+
在链接时,编译器和链接器为符号name
提供了0x1234的内存地址.使用下标运算符,name[1]
例如,编译器知道如何计算内存中偏移处的字符,0x1234 + 1 = 0x1235,并且它确实是"o".这很简单,此外,使用ANSI C标准,char
数据类型的大小为1个字节,这可以解释运行时如何获取此语义的值name[cnt++]
,假设cnt
是一个int
eger并且值为3,例如,运行时自动加1,从零开始计数,偏移量为't'.到目前为止这很简单.
如果name[12]
被执行会怎么样?好吧,代码会崩溃,或者你会得到垃圾,因为数组的边界是从索引/偏移0(0x1234)到8(0x123B).之后的任何东西都不属于name
变量,这将被称为缓冲区溢出!
name
内存中的地址是0x1234,如示例中所示,如果您这样做:
printf("The address of name is %p\n", &name); Output would be: The address of name is 0x00001234
为了简洁和保持示例,内存地址是32位,因此您可以看到额外的0.很公平?对,让我们继续吧.
现在指向...
char *name
是指向char
....的类型的指针
编辑: 我们将它初始化为NULL如图所示感谢Dan指出小错误...
char *name = (char*)NULL; +======+ +======+ |0x5678| -> |0x0000| -> NULL +======+ +======+
在编译/链接时,name
它没有指向任何东西,但是具有符号的编译/链接时间地址name
(0x5678),实际上它是NULL
,指针地址name
是未知的,因此是0x0000.
现在,请记住,这是至关重要的,符号的地址在编译/链接时是已知的,但在处理任何类型的指针时指针地址是未知的
假设我们这样做:
name = (char *)malloc((20 * sizeof(char)) + 1); strcpy(name, "Fortran");
我们调用malloc
为20个字节分配一个内存块,不,它不是21,我加上1的大小的原因是'\ 0'nul终止字符.假设在运行时,给出的地址是0x9876,
char *name; +======+ +======+ +-+-+-+-+-+-+-+--+ |0x5678| -> |0x9876| -> |F|o|r|t|r|a|n|\0| +======+ +======+ +-+-+-+-+-+-+-+--+
所以当你这样做时:
printf("The address of name is %p\n", name); printf("The address of name is %p\n", &name); Output would be: The address of name is 0x00005678 The address of name is 0x00009876
现在,这就是" 阵列和指针相同的幻觉在这里发挥作用 "
当我们这样做时:
char ch = name[1];
运行时会发生什么:
name
查找符号的地址ch
.上面的内容对于理解这种区别至关重要,数组和指针之间的区别在于运行时如何使用指针获取数据,还有一个额外的取向间接.
请记住,T类型 的数组总是会衰减为 T类型的第一个元素的指针.
当我们这样做时:
char ch = *(name + 5);
name
查找符号的地址ch
.顺便说一下,你也可以对字符数组这样做...
进一步,通过在阵列即环境中使用标运算符char name[] = "...";
和name[subscript_value]
真的一样*(名称+ subscript_value).即
name[3] is the same as *(name + 3)
既然表达*(name + subscript_value)
是可交换的,那就相反了,
*(subscript_value + name) is the same as *(name + subscript_value)
因此,这解释了为什么在上面的一个答案中你可以这样写(尽管如此,即使它是非常合理的,也不推荐这种做法!)
3[name]
好的,我如何获得指针的值?这就是*
用于的,假设指针的name
指针存储器地址为0x9878,再次参考上面的例子,这就是它的实现方式:
char ch = *name;
这意味着,获取0x9878的内存地址所指向ch
的值,现在将具有值'r'.这称为解除引用.我们只是取消引用一个name
指针来获取值并将其赋值给ch
.
此外,编译器知道a sizeof(char)
为1,因此您可以像这样执行指针递增/递减操作
*name++; *name--;
指针会自动向上/向下逐步上升/下降.
当我们这样做时,假设指针内存地址为0x9878:
char ch = *name++;
*name的值是什么,地址是什么,答案是,*name
现在将包含't'并将其分配给ch
,并且指针存储器地址是0x9879.
在这里你必须要小心,与前面关于内存边界的内容相同的原则和精神(参见上文中"如果名称[12]被执行时会发生什么")结果将是相同的,即代码崩溃和烧伤!
现在,如果我们解除分配的内存块会发生什么指向name
通过调用C函数free
以name
作为参数,即free(name)
:
+======+ +======+ |0x5678| -> |0x0000| -> NULL +======+ +======+
是的,内存块被释放并传回运行时环境,供另一个即将执行的代码执行使用malloc
.
现在,这就是分段错误的常用符号发挥作用的地方,因为name
它没有指向任何东西,当我们取消引用它时会发生什么,即
char ch = *name;
是的,代码将崩溃并以"分段故障"刻录,这在Unix/Linux下很常见.在Windows下,将出现一个对话框,其中包含"不可恢复的错误"或"应用程序发生错误,您是否希望将报告发送给Microsoft?"....如果指针尚未显示malloc
,则试图取消引用它,保证崩溃和燃烧.
另外:记住这一点,因为每一个malloc
都有相应的free
,如果没有相应的free
,你有一个内存泄漏,其中分配了内存但没有释放.
而且你有它,就是指针的工作方式以及数组与指针的不同之处,如果你正在阅读一本说它们相同的教科书,那就撕下那个页面并撕掉它!:)
我希望这有助于你理解指针.