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

时间:2017-02-26 19:12:28

标签: c pointers string-literals

  

所有答案都非常感谢,并且所有那些花时间澄清这些事情的人 - 非常感谢

我正在学习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";是指向字符串文字的指针。

为什么它会更新到&#39; World&#39;什么时候它在只读内存

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

我现在对我对指针的了解非常困惑。我在这里失踪了什么?

3 个答案:

答案 0 :(得分:3)

  

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

是的,他们是。

  

他们指向哪里?

他们指向一个空字符串文字。

  

char *pointer;char *pointer = "";相同吗?

没有。 char *pointer;是未初始化的指针,而char *pointer = "";是初始化的指针。 ""属于const char[1]类型,其元素为'\0'

  

为何在只读内存中更新为"World"

char *msg = "Hello";相当于

char const *msg = "Hello";  

这意味着不应修改字符串文字msg指向,但此约束在字符串文字上,而不是指向字符串文字的指针。 msg可以修改。

  

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

msg = "World";是指针msg的新字符串文字的赋值。

答案 1 :(得分:2)

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

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

Memory loc'n:  Contents
BASE+0x0000:  # start of string
BASE+0x0000:  '\0'  # end of string

即,包含“无字符”的内存块,后跟一个尾随的NUL字节,用于标记字符串的结尾。

该数据通常被认为是“不变的”。它可能会或可能不会存储在“常量数据”中。这取决于链接器,操作系统等。

然而,只是“常量字符串文字”。您的代码还有第二部分:

char *inp_file = "";

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

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

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

另一方面,同样取决于架构和平台,文件范围常量可以存储在文本段中的数据段中,< / strong>单独的常量段或包含程序代码的同一段。

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

程序运行后,您(可能)执行以下代码:

inp_file = optarg;

这会导致指针变量改变其值。现在,它不是指向您首次创建的文字字符串,而是指向由getopt库确定的字符串。这可能位于argv区域中的某个位置,但它可能位于堆上的strdup ed块中(因为您不知道getopt如何工作,以及它可能在各种系统)。

请注意:在当天,覆盖用作初始值的“常量”字符串实际上是可能的,也是司空见惯的。您可能会发现仍然执行此操作的旧程序。现代C非常积极地劝阻这一点,但大多数代码都是遗留代码。 ; - )

答案 2 :(得分:0)

您没有更新"hello",而是将msg设置为指向其他字符串"World" - 它可能会或可能不会strcpy(msg, "World")代替printf("Before: %p\n", (void*)msg);在系统设置上,但它肯定是未定义的行为,所以不要编写执行此操作的代码。)

要显示此信息,您可以在printf("After: %p\n", (void*)msg);行的任意一侧添加msg = "World";File.SetAttributes(@"C:\Special", File.GetAttributes(@"C:\Special") | (FileAttributes.System | FileAttributes.Hidden));