文章目录
介绍
嗨,我是艾丽卡,很高兴和你聊聊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位置的指针。这意味着你可以有一个指针指向数组的最后一个元素之后的位置,但这个位置实际上是数组的"边界之外"。例如:
c
char str[] = "hello";
char *ptr = str + sizeof(str) / sizeof(str[0]);
在这个例子中,ptr
指向的是数组 str
的末尾元素之后的位置,也就是数组的"边界之外"。这个指针是合法的,因为它没有越界,它只是指向了数组末尾元素之后的一个位置。但是,你不能对这个位置进行解引用操作,因为这样做会产生未定义行为,即程序可能会崩溃或者产生不可预测的结果。
在处理字符串时,这个特性可以用来遍历字符串直到遇到空字符。例如,下面的代码可以用来计算字符串的长度:
c
char str[] = "hello";
char *ptr = str;
while (*ptr) {
ptr++;
}
int length = ptr - str;
在这个例子中,ptr
会遍历字符串直到遇到空字符,然后计算出字符串的长度。但是,一旦 ptr
指向了空字符,就不应该再进行解引用操作,因为那会导致未定义行为
c
#include <stdio.h>
int main() {
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);
return 0;
}
bush
The length of the string is: 5
好奇的是为什么程序while会停止
我们尝试传入\0
c
#include <stdio.h>
int main() {
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);
return 0;
}
bush
he length of the string is: 0
可以看出\0可以终止while,而false也可以。
c
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
,循环体不会被执行。
这里是一个例子:
c
#include <stdio.h>
int main() {
while (NULL) {
printf("This will not be printed.\n");
}
return 0;
}
在这个程序中,while
循环的条件是 NULL
,因此条件评估为 false
,循环内部的 printf
语句不会被执行。
总结来说,NULL
在布尔上下文中被视为 false
,因为它的值是零。这与 \0
(空字符)和 false
(布尔假值)在布尔上下文中的表现是一致的,尽管它们在类型和用途上有所不同。
二,Toofar
在C语言中,越界解引用是指访问数组或字符串超出其有效范围的内存位置。这通常是不安全的,因为它可能导致未定义行为,包括程序崩溃、数据损坏或安全漏洞。下面是一个越界解引用的例子:
c
#include <stdio.h>
int main() {
char str[] = "hello"; // 字符串字面值,包含空字符'\0'
// 故意越界解引用
printf("%c\n", str[sizeof(str) / sizeof(str[0])]); // 这将打印空字符'\0'
// 越界解引用
printf("%c\n", str[sizeof(str) / sizeof(str[0]) + 1]); // 这将尝试访问空字符之后的内存
return 0;
}
bush
0
□
在这个例子中,str
是一个字符数组,它包含了字符串 "hello" 和一个空字符 \0
。sizeof(str) / sizeof(str[0])
计算得到的是数组中的元素数量,不包括空字符。因此,str[sizeof(str) / sizeof(str[0])]
是访问数组中最后一个元素(即空字符)的有效操作。
然而,str[sizeof(str) / sizeof(str[0]) + 1]
尝试访问空字符之后的内存位置,这是一个越界解引用。这个操作是未定义的,因为它访问了数组分配的内存之外的区域。在某些系统和编译器上,这可能导致程序崩溃或不可预测的行为。
越界解引用是编程中应该避免的错误,因为它可能导致严重的安全问题。正确的做法是总是确保在访问数组或字符串时不会超出其界限。在实际编程中,应该使用循环或其他控制结构来确保不会越界,或者使用安全的字符串处理函数,如 strncpy
、snprintf
等,它们可以帮助防止缓冲区溢出和越界错误。