Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在C语言中,printf(“Hello”)如何输出;“大提琴”;在任何情况下?_C_String - Fatal编程技术网

在C语言中,printf(“Hello”)如何输出;“大提琴”;在任何情况下?

在C语言中,printf(“Hello”)如何输出;“大提琴”;在任何情况下?,c,string,C,String,它完全想念我怎么能printf(“Hello”)打印Cello。这挑战了我对C的基本理解。但是从下面关于堆栈溢出的问题的最上面的答案(由Carson Myers)来看,这似乎是可能的。你能简单地解释一下这是怎么可能的吗?答案是这样的: 无论何时在源代码中写入字符串,该字符串都是只读的 (否则,您可能会更改 可执行文件——想象一下,如果您编写了char*a=“hello”,然后进行了更改 a[0]到“c”。然后其他地方写了printf(“你好”);。如果你是 允许更改“hello”的第一个字符和编

它完全想念我怎么能
printf(“Hello”)
打印
Cello
。这挑战了我对C的基本理解。但是从下面关于堆栈溢出的问题的最上面的答案(由Carson Myers)来看,这似乎是可能的。你能简单地解释一下这是怎么可能的吗?答案是这样的:

无论何时在源代码中写入字符串,该字符串都是只读的 (否则,您可能会更改 可执行文件——想象一下,如果您编写了char*a=“hello”,然后进行了更改 a[0]到“c”。然后其他地方写了printf(“你好”);。如果你是 允许更改“hello”的第一个字符和编译器 只存储一次(应该),然后printf(“hello”);将输出 大提琴


前面提到的问题:

a指向的“Hello”与您传递给printf的“Hello”不同。(系统中有2个“hello”)

如果您要求printf在a处打印字符串,它将起作用

原因:

  • 编译器通常只存储相同字符串文本的一个副本,因此
    char*a=“hello”中的字符串文本
    和在
    printf(“hello”)
    中可以位于同一个内存位置

  • 链接中的答案假设存储字符串文本的内存位置是可变的,这在现代体系结构中通常是不可变的。但是,如果没有内存访问保护,例如在某些嵌入式体系结构中或在实模式下工作的80386中,这是正确的

  • 因此,当您修改
    a
    引用的字符串时,
    printf
    的值也会更改


  • 这是一个实用的解释(即不受C语言标准的约束):

    首先,在代码中的某个地方声明
    char*a=“hello”

    因此,编译器:

    • 生成一个常量字符串
      “hello”
      ,并将其放置在可执行映像中的只读内存部分(通常在RO数据部分中),但前提是它尚未执行此操作
    • char*a=“hello”
      替换为
      char*a=内存中“hello”的地址
    然后,在代码的其他地方调用
    printf(“hello”)

    因此,编译器:

    • 生成一个常量字符串
      “hello”
      ,并将其放置在可执行映像中的只读内存部分(通常在RO数据部分中),但前提是它尚未执行此操作
    • printf(“hello”)
      替换为
      printf(内存中“hello”的地址)
    现在,理论上(正如@Carson Myers所解释的),如果您可以更改
    “hello”
    中的任何字符,那么它将影响任何引用位于内存中该字符串地址的数据的结果


    实际上,由于编译器将所有常量字符串放在只读内存段中,因此这是不可行的。

    如果您在源代码中的某个位置具有字符串文字“Hello”,则该字符串最终作为代码/数据段的一部分出现在可执行文件中。这在任何时候都应该被认为是只读的,因为编译器可以自由地将同一文本的多次出现优化为单个实体。您的源代码中可能有多个“Hello”,以及多个指向它们的指针,但它们可能都指向同一个地址

    ISO/IEC 9899“编程语言-C”,第6.4.5章“字符串文字”,第6段:

    如果这些数组的元素具有 适当的值。如果程序试图修改这样的数组,则该行为是 未定义

    因此,任何指向此类字符串文字的指针都将被声明为指向常量内容的指针,以在源代码级别明确这一点:

    char const * a = "Hello";
    
    根据这个定义,
    a[0]=“C”不是有效的操作:无法更改
    常量值,编译器将发出错误

    然而,有多种方式可以“欺骗”语言。首先,您可以投射指针:

    char const * a = "Hello";
    char * b = (char *)a;
    b[0] = 'C';
    
    正如上述标准中的代码片段所述,这虽然在语法上是正确的,但在语义上是未定义的行为。它甚至可能在某些平台上“正确”工作(主要是出于历史原因),并实际打印“大提琴”。这可能会影响到其他人

    考虑一下,如果您的可执行文件被烧录到ROM芯片中,并从那里执行,会发生什么


    我说的是“历史原因”。开始时,没有
    常量
    。这就是为什么C将字符串文本的类型定义为
    char[]
    (no
    const

    请注意:

    • C++98将字符串文本定义为
      const
      ,但允许转换为
      char*
    • C++03仍然允许转换,但不支持转换
    • C++11不再允许在没有强制转换的情况下进行转换

    你理解错了。答案基本上是:如果可以修改字符串文字,
    printf(“Hello”)
    将输出
    Cello
    ,这显然不是人们所期望的。如果,你自己试过吗?在我演示和运行C应用程序的近100次中,我从来没有得到过大提琴!!!你收到这个问题了吗?还是你在问它是否会发生?因为在第一种情况下,那是。。。非常奇怪,在第二种情况下,你引用的答案并没有说它发生了——它实际上是在讨论为什么它没有发生。请写
    const
    char
    ,请。。。这正是这种邪恶的根源所在:太懒了,无法正确地限定字符串文本
    const
    …编译器通常只存储一个相同字符串文本的副本,这与