【问题标题】:How does the compiler add a null-terminator when there is no more space?当没有更多空间时,编译器如何添加空终止符?
【发布时间】:2021-11-26 09:12:15
【问题描述】:

我知道这个问题已经被问过一百万次了,但我仍然感到困惑。我故意使我的字符串大小等于其中的字符数:

int main()
{
    int i = 0;
    char str[5] = "check";
    while((str[i] != '\0') && (i != 10)){ // (i != 10) aborts the func
        printf("str[i] = %c\n", *(str+i));
        printf("i = %d\n", i);
        i++;
    }
}

输出是:

str[i] = c
i = 0
str[i] = h
i = 1
str[i] = e
i = 2
str[i] = c
i = 3
str[i] = k
i = 4

尽管没有分配空间,为什么它仍然在末尾存储空字符?它取决于编译器,还是它们都以相同的方式工作?

【问题讨论】:

  • 这是未定义的行为。
  • 您的代码之所以起作用,是因为偶然地,在您的数组之后分配的内存仍然是空的并初始化为空。情况可能并非总是如此。在为字符串存储定义数组时,您必须在末尾允许一个额外的字符来存储 NULL。

标签: c string memory-management null


【解决方案1】:

你问:

尽管没有分配空间,为什么它仍然在末尾存储空字符?

它没有。请查看您对代码的修改:

#include <stdio.h>

int main(void)
{
    int i = 0;
    char a = 'x';
    char str[5] = "check";
    char b = 'x';
    while((str[i] != '\0') && (i != 10)){ // (i != 10) aborts the func
        printf("str[i] = %c\n", *(str+i));
        printf("i = %d\n", i);
        i++;
    }
}

就我而言,它产生了:

str[i] = c
i = 0
str[i] = h
i = 1
str[i] = e
i = 2
str[i] = c
i = 3
str[i] = k
i = 4
str[i] = x
i = 5
str[i] = x
i = 6
str[i] = 
i = 7

所以它确实在末尾添加\0。下一个地址可能包含\0,但它与arr 没有任何关系。相反,它与编译器决定放在它后面的东西有关。这可能取决于很多事情。其中一个是arr的大小,另一个是同一作用域内的其他变量。

不要做任何假设。这是 UB,这类错误的问题在于它可能会起作用。

如果它确实有效,请不要做任何假设。就像您假设添加了 \0 一样。不是。

【讨论】:

    【解决方案2】:

    标准库有

    字符串是由第一个空字符终止并包括第一个空字符的连续字符序列。

    对于char str[5] = "check";str[] 不是字符串,因为它缺少空字符str[] 可以称为char 的数组。


    经过几次迭代,使用while((str[i] != '\0') &amp;&amp; (i != 10)){,代码尝试str[5],它在str[] 之外。这是未定义的行为 (UB) @Vlad from Moscow。任何事情都可能发生。

    在 OP 的情况下,UB 显然是 str[5] 为零,结束循环。这在随后的运行中可能会有所不同。

    不要依赖这个结果。是UB。


    尽管没有分配空间,为什么它仍然在末尾存储空字符?

    循环可能已经结束,因为内存中的下一个 0 或其他原因。是UB。

    是否依赖于编译器,

    没有基于编译的指定行为。没有规范表明结果是一致的。是UB。

    ...或者它们都以相同的方式工作?

    没有规定它们的工作方式相同。没有规范说明它们的工作方式不同。是UB。

    【讨论】:

    • 感谢 chux 详尽的回答
    【解决方案3】:

    字符串在 C 中的工作方式是,在它的末尾,总是有一个特殊的结束字符,\0(又名null terminator),它表示字符串的结束。

    问题是,你没有为字符串分配足够的空间来包含结束字符,所以你的循环只是继续前进到你的记忆中。

    您的函数在i=10 处中止的原因是因为您的程序在您的计算机内存中不断前进,它达到了您尝试访问的内存被保留用于比您的程序更重要的东西(例如,您系统中的操作系统或文件),因此操作系统类似于:OI LAD, where do you think you are going?,并杀死您的程序,以保护您尝试访问的内存。

    【讨论】:

    • 在您的问题中,您写道:"it reaches a point where the memory you are trying to access is reserved for something more important than your program (say, the OS or files you have in your system)" -- 这句话大部分是不正确的。您似乎将virtual memory 与物理内存混淆了。在大多数现代操作系统上,每个进程都有其own virtual address space。 (...)
    • (...) 此外,访问未映射的内存/保护页比访问包含受保护的重要内容的映射内存更有可能发生访问冲突/分段错误。
    • @AndreasWenzel 到 2021 年,大多数处理器都是嵌入式处理器,每年 1 亿(甚至数十亿?)。 C在那里很受欢迎。许多系统(也许是大多数系统)缺少“在大多数现代操作系统上......”不适用的操作系统。
    • 在问题中循环结束的时间比 10 早得多
    猜你喜欢
    • 2011-12-22
    • 1970-01-01
    • 2012-08-01
    • 2014-01-08
    • 1970-01-01
    • 2023-01-18
    • 2015-04-24
    • 1970-01-01
    相关资源
    最近更新 更多