【问题标题】:Who throws the segmentation fault?谁抛出了分段错误?
【发布时间】:2011-07-06 06:15:01
【问题描述】:

鉴于以下程序。

 int main() {
 char *str = "hello word";
 str[0] = 'a';
 return 0;
 }

上述程序抛出分段错误。我知道它会抛出,因为只读段包含 hello world,并且无法修改。如果从“h”变为“a”的存储指令是在 L1 缓存(处理器内部)中完成的,并且 MMU 仅在页面从 L3 刷新到主内存时才出现,几乎立即引发分段错误。

下面的代码有效地做同样的事情,不会导致任何分段错误。为什么?

 int main() {
 char str[] = {'h','e','l','l','o',' ','w','o','r','l','d','\0'};
 str[0] = 'a';
 return 0;
 }

【问题讨论】:

    标签: c unix data-structures operating-system


    【解决方案1】:

    处理器缓存具有与每个缓存行相关联的标签,这些标签告诉它应用于该缓存行中当前保存的内存的权限。因此,在尝试写入缓存时(通常)会立即捕获写入只读内存的尝试;它不会等到该缓存行刷新到主内存。

    就两个示例之间的区别而言,它非常简单:第一个定义了一个静态分配的字符串字面量。尝试修改该字符串文字会导致未定义的行为。第二个定义了一个初始化为特定值的 char 数组——但在它初始化之后,它与任何其他数组非常相似。尤其是修改数组内容的结果定义明确。

    【讨论】:

    • 那么,你的意思是缓存控制器抛出异常?
    • @Bala:CPU 的一部分无论如何都会这样做——我不相信 AMD 或 Intel 指定该特定异常是来自缓存控制器还是“内存写入”功能单元。这种事情甚至可能因一个 CPU 而异。
    • -1,第一个字符串分配在只读段中,第二个分配在堆栈上。在这两种情况下,它都是字符数组。
    • @max taldykin:对不起,你完全错了。第一个不一定分配在只读内存中(例如,在 MS-DOS 下的大多数编译器上,它不会分配)。第二个不一定分配在堆栈上(例如,在 IBM 大型机上运行的编译器上就不会分配)。第一个未定义的部分原因是允许单独的字符串文字共享存储。例如,如果我有char *s="This is a string"char *y="string",编译器可以将两者合并,并将y 指向为x 分配的存储空间的中间——但这不一定是R/O。
    • 是的,你是对的,它依赖于实现。但在这种情况下,由于程序试图写入只读数据段,所以会产生段错误。在其他系统上,或使用其他链接选项,它可以正常运行。
    【解决方案2】:

    在第二个中,您正在制作副本。起初你不是。

    char str[] = {'h','e','l','l','o',' ','w','o','r','l','d','\0'};
    

    这里str 是在堆栈上创建的数组。正在向str 复制内容。怎么过-

    char *str = "hello word";
    

    str 指向驻留在不可修改部分的数据。所以,你不能当尝试结果分段错误。


    cmets 说明

    我不认为,因为原始数据类型 C 和 C++ 会改变它的规则。来自 ISO/IEC 14882:2003(E),第 8.5.2 节

    1. A char array (whether plain char, signed char, or unsigned char) can be 
       initialized by a string- literal (optionally enclosed in braces); a wchar_t 
       array can be initialized by a wide string-literal (option- ally enclosed in 
       braces); successive characters of the string-literal initialize the members of 
       the array.
    
         [Example:
               char msg[] = "Syntax error on line %s\n"; shows a character array 
               whose members are initialized with a string-literal. Note that because 
               ’\n’ is a single character and because a trailing ’\0’ is appended, 
               sizeof(msg) is 25. 
         ] 
    
    2. There shall not be more initializers than there are array elements. 
    
        [Example:
              char cv[4] = "asdf" ;// error is ill-formed since there is no space for the implied trailing ’\0’. 
        ]
    

    因此,示例 2 消除了疑问。

    【讨论】:

    • 你能详细解释一下吗?
    • 另外,NULL 终止字符不是必需的。第二种情况默认添加。
    • 这实际上是在C标准中规定的吗?
    • @Kinopiko - 我不确定它在 C 标准中的指定位置。让我们用一个例子来看看它是否默认添加。 ideone.com/Yymfv
    • @Kinopiko @Bala - 添加了标准中的引用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-12
    • 2022-11-28
    • 1970-01-01
    • 2017-06-20
    • 1970-01-01
    相关资源
    最近更新 更多