为什么我可以更新指向(常量)字符串文字的指针?

Why can I update a pointer to a (constant) string literal?

All answers are highly appreciated, and to all those devoting their time clarifying these things - thank you very much .

我正在学习 C,刚刚完成了有关指针的章节。 在这本书中,我正在阅读一个示例代码,这让我非常困惑。

部分示例代码:

...

 1  char *inp_file = "";
 2  char *out_file = "";
 3  char ch;
 4  
 5  while ( ( ch = getopt( argc, argv, "i:o:" )) != EOF )
 6  {
 7      switch( ch )
 8      {
 9          case 'i':
10              inp_file = optarg;
11              break;
12          case 'o':
13              out_file = optarg;
14              break;
15  
16          default:
17              fprintf( stderr, "Unknown option: '%s'/n", optarg );
18              return 2;
19      }
20  }
21  
22  argc -= optind;
23  argv += optind;

...

我的理解是 char *inp_file = ""char *out_file = "" 是指向 字符串文字。

他们指的是哪里?考虑到它是一个空 """

当它们存储在只读存储器中时,如何更新它们(第 10、13 行)?

char *pointer;char *pointer = "";一样吗?


此外,我尝试了这个并且成功了。

#include <stdio.h>

int main( int argc, char *argv[] )
{
    char *msg = "Hello";

    msg = "World";

    printf("%s\n", msg );// Prints 'World'
}

我 100% 确定 char *msg = "Hello"; 是一个指向字符串文字的指针。

为什么它在只读内存中时会更新为'World'?

这是一个全新的重新分配还是什么?

我现在对指针的了解真的很困惑。我在这里错过了什么?

My understanding is that char *inp_file = "" and char *out_file = "" are pointers to string literals.

是的,他们是。

Where are they pointing to ?

它们指向空字符串文字。

Is char *pointer; same as char *pointer = ""; ?

没有。 char *pointer; 是未初始化的指针,而 char *pointer = ""; 是已初始化的指针。 "" 属于 const char[1] 类型,具有元素 '[=19=]'.

Why it gets updated to "World" when it's in read-only memory ?

char *msg = "Hello"; 等同于

char const *msg = "Hello";  

意思是msg指向的字符串字面量不能被修改,但是这个约束是针对字符串字面量的,而不是指向字符串字面量的指针。 msg可以修改。

Is it a complete new reassignment or what ?

msg = "World"; 是对指针 msg.

的新字符串文字赋值

您没有更新 "hello",您正在将 msg 设置为指向不同的字符串,"World" - 改为 strcpy(msg, "World") 可能会也可能不会(取决于系统设置,但它绝对是未定义的行为,所以不要编写执行此操作的代码)。

为了显示这一点,您可以在 msg = "World"; 行的任一侧添加 printf("Before: %p\n", (void*)msg);printf("After: %p\n", (void*)msg);

实际上有两件事正在发生。首先是字符串文字。您创建了一个零长度字符串 "",它仍然以 NUL 结尾,因为所有 C 字符串都是以 NUL 结尾的——这就是您知道结尾在哪里的方式。

所以你有一块内存看起来像这样:

Memory loc'n:  Contents
BASE+0x0000:  # start of string
BASE+0x0000:  '[=10=]'  # end of string

即包含"no characters"的内存块,后跟一个尾随的NUL字节来标记字符串的结尾。

那个数据一般被认为是 "constant." 它可能存储也可能不存储在 "constant data." 这取决于链接器, OS 等

但是, 这只是 "constant string literal." 您的代码还有第二部分:

char *inp_file = "";

您已经声明了一个指向常量字符串文字的指针。该指针是一个指针大小的对象(如果您有 32 位地址 space,则为 4 字节;如果您有 64 位地址,则为 8 字节 space;如果您有不同或混合地址 space) 并包含常量字符串文字的第一个字节的 内存地址

Memory loc'n:      Contents
PTR_BASE+0x0000:   (BASE+0x0000)
PTR_BASE+0x0008:   ...

因为您在任何函数之外声明了 inp_file,它被认为具有 文件范围文件范围初始化变量存储在数据段中(更多关于内存布局here)。 (请注意,未初始化的变量可能存储在零段未初始化的段中,具体取决于体系结构。)

另一方面,再次取决于体系结构和平台,文件范围常量可能存储在数据段文本段,单独的常量段或包含程序代码的同一段。

所以你有两个内存位置,可能在不同的程序段中。第一个是您创建的"literal string",""。第二个是你声明的指针变量,inp_file。指针在加载时使用文字字符串的地址进行初始化。

一旦你的程序是 运行,你(可能)执行的代码是:

inp_file = optarg;

这导致指针变量改变它的值。现在,它不再指向您最初创建的文字字符串,而是指向由 getopt 库确定的字符串。这可能在 argv 区域的某处,但它可能在堆上的 strduped 块中(因为您不知道 getopt 是如何工作的,以及它可能在各种系统)。

请注意:在过去,覆盖用作初始值的 "constant" 字符串实际上是可能且司空见惯的。您可能会发现仍然执行此操作的旧程序。现代 C 非常积极地阻止这种情况,但大多数代码都是遗留代码。 ;-)