文章目录
介绍
嗨,我是艾丽卡,很高兴和你聊聊C语言中的字符串和数组。想象一下,字符串就像一列小火车,每个车厢代表一个字符,而最后一个特别的车厢——空字符
'\0'
——告诉我们火车到站了,也就是字符串的结束。
- 界限(Bound):- 这就像数火车车厢的数量,包括那个告诉我们火车结束的特别的车厢。
- 低位地址(Lo):- 这是火车队列的第一个车厢的地址,也就是字符串的第一个字符。
- 高位地址(Hi):- 这是最后一个车厢的地址,也就是空字符的地址。
- TooFar:- 这就像是火车队列结束后,我们再往前多走一步的位置。虽然我们可以指向那里,但是如果真的去那里,就会超出火车的范围,导致未定义行为。
- 目标大小(Tsize):- 这就像测量整个火车队列占用的轨道长度,也就是整个字符串包括空字符在内的总字节数。
- 空字符结尾(Null-terminated):- 这表示我们的火车队列总是以那个特别的车厢结束,这样我们就知道火车什么时候到站。
- 长度(Length):- 这是计算火车车厢数量的方法,但不包括那个特别的结束车厢。
处理字符串时,我们得像列车长一样小心翼翼,确保不会让火车出轨,也就是说,要确保我们的代码不会越界,不会溢出缓冲区,这样才能保证我们的程序既安全又稳定。
一、\0 NULL false
在C语言中,
sizeof
运算符用于获取一个变量或类型在内存中所占的字节数。对于数组,
sizeof(array)
会返回整个数组所占的字节数,包括数组中的所有元素。
当你有一个数组时,比如
char str[] = "hello";
,
sizeof(str)
会返回字符串 “hello” 所占的字节数,包括最后的空字符(null terminator)
\0
。这是因为在C语言中,字符串是以空字符结尾的字符数组。
C标准确实允许创建指向数组末尾元素之后加1位置的指针。这意味着你可以有一个指针指向数组的最后一个元素之后的位置,但这个位置实际上是数组的“边界之外”。例如:
char str[]="hello";char*ptr = str +sizeof(str)/sizeof(str[0]);
在这个例子中,
ptr
指向的是数组
str
的末尾元素之后的位置,也就是数组的“边界之外”。这个指针是合法的,因为它没有越界,它只是指向了数组末尾元素之后的一个位置。但是,你不能对这个位置进行解引用操作,因为这样做会产生未定义行为,即程序可能会崩溃或者产生不可预测的结果。
在处理字符串时,这个特性可以用来遍历字符串直到遇到空字符。例如,下面的代码可以用来计算字符串的长度:
char str[]="hello";char*ptr = str;while(*ptr){
ptr++;}int length = ptr - str;
在这个例子中,
ptr
会遍历字符串直到遇到空字符,然后计算出字符串的长度。但是,一旦
ptr
指向了空字符,就不应该再进行解引用操作,因为那会导致未定义行为
#include<stdio.h>intmain(){char str[]="hello";// 包含空字符的字符串char*ptr = str;// 指向字符串的开始// 遍历字符串直到遇到空字符while(*ptr){
ptr++;// 移动指针但不解引用TooFar位置}// 此时ptr指向TooFar位置,即空字符之后的位置// 我们不应该解引用ptr,因为它指向的是未定义的内存区域// 计算字符串的长度(不包括空字符)int length = ptr - str;printf("The length of the string is: %d\n", length);return0;}
The length of the string is: 5
好奇的是为什么程序while会停止
我们尝试传入\0
#include<stdio.h>intmain(){char str[]="hello";// 包含空字符的字符串char*ptr = str;// 指向字符串的开始// 遍历字符串直到遇到空字符// while (*ptr) {// ptr++; // 移动指针但不解引用TooFar位置// }while('\0'){
ptr++;// 移动指针但不解引用TooFar位置}// 此时ptr指向TooFar位置,即空字符之后的位置// 我们不应该解引用ptr,因为它指向的是未定义的内存区域// 计算字符串的长度(不包括空字符)int length = ptr - str;printf("The length of the string is: %d\n", length);return0;}
he length of the string is: 0
可以看出\0可以终止while,而false也可以。
while(false){
ptr++;// 移动指针但不解引用TooFar位置}
#
\0
(空字符):-\0
是一个字符常量,代表ASCII码中的空字符,其整数值是0。在C语言中,字符串以空字符\0
结尾,这是字符串结束的标志。当\0
用作条件表达式时,它的值是0,因此在布尔上下文中被视为false
。- **
false
**:-false
是C语言中的一个布尔值,代表逻辑假。在C99及以后的标准中,bool
类型被引入,其中false
是预定义的布尔值,其值为0。
尽管
\0
和
false
在整数值上都是0,并且在布尔上下文中都被视为
false
,但它们在语义上是不同的。
\0
用于表示字符串的结束,而
false
用于表示布尔逻辑中的假值。
在C语言中,
NULL
是一个宏,它被定义为一个空指针常量。它的值通常是
(void *)0
,即一个指向
void
类型的指针,其值为0。
NULL
用于表示一个空(或无效)的指针。
当
NULL
被用在
while
循环的条件中时,它的值实际上是一个指针,而不是一个整数。在C语言中,任何非零值在布尔上下文中都被视为
true
,而零值(包括
NULL
指针)被视为
false
。因此,
while (NULL)
这个条件会被评估为
false
,循环体不会被执行。
这里是一个例子:
#include<stdio.h>intmain(){while(NULL){printf("This will not be printed.\n");}return0;}
在这个程序中,
while
循环的条件是
NULL
,因此条件评估为
false
,循环内部的
printf
语句不会被执行。
总结来说,
NULL
在布尔上下文中被视为
false
,因为它的值是零。这与
\0
(空字符)和
false
(布尔假值)在布尔上下文中的表现是一致的,尽管它们在类型和用途上有所不同。
二,Toofar
在C语言中,越界解引用是指访问数组或字符串超出其有效范围的内存位置。这通常是不安全的,因为它可能导致未定义行为,包括程序崩溃、数据损坏或安全漏洞。下面是一个越界解引用的例子:
#include<stdio.h>intmain(){char str[]="hello";// 字符串字面值,包含空字符'\0'// 故意越界解引用printf("%c\n", str[sizeof(str)/sizeof(str[0])]);// 这将打印空字符'\0'// 越界解引用printf("%c\n", str[sizeof(str)/sizeof(str[0])+1]);// 这将尝试访问空字符之后的内存return0;}
0
□
在这个例子中,
str
是一个字符数组,它包含了字符串 “hello” 和一个空字符
\0
。
sizeof(str) / sizeof(str[0])
计算得到的是数组中的元素数量,不包括空字符。因此,
str[sizeof(str) / sizeof(str[0])]
是访问数组中最后一个元素(即空字符)的有效操作。
然而,
str[sizeof(str) / sizeof(str[0]) + 1]
尝试访问空字符之后的内存位置,这是一个越界解引用。这个操作是未定义的,因为它访问了数组分配的内存之外的区域。在某些系统和编译器上,这可能导致程序崩溃或不可预测的行为。
越界解引用是编程中应该避免的错误,因为它可能导致严重的安全问题。正确的做法是总是确保在访问数组或字符串时不会超出其界限。在实际编程中,应该使用循环或其他控制结构来确保不会越界,或者使用安全的字符串处理函数,如
strncpy
、
snprintf
等,它们可以帮助防止缓冲区溢出和越界错误。
版权归原作者 艾丽卡和木森的区块链日记 所有, 如有侵权,请联系我们删除。