【问题标题】:Address length of a string literal字符串文字的地址长度
【发布时间】:2013-09-09 14:04:07
【问题描述】:

我看到在带有 GCC 的 Linux 系统上,字符串文字的地址似乎比其他变量的地址要小得多。例如下面的代码会生成下面显示的 o/p。

#include <stdio.h>

int main()
{
    char *str1 = "Mesg 1";
    char *str2 = "Mesg 2";
    char str3[] = "Mesg 3";
    char str4[] = "Mesg 4";

    printf("str1 = %p\n", (void *) str1);
    printf("str2 = %p\n", (void *) str2);
    printf("&str3 = %p\n", (void *) str3);
    printf("&str4 = %p\n", (void *) str4);

    return 0;
}

输出:

str1 = 0x400668
str2 = 0x40066f
&str3 = 0x7fffcc990b10
&str4 = 0x7fffcc990b00

是否有单独的常量地址空间用于此类用途?

【问题讨论】:

  • 最有可能的字符串文字只是在文本段中,通常位于相对较低的地址。为什么这对你很重要?
  • 鉴于奇怪的差异,我只是想知道原因。

标签: c gcc memory constants string-literals


【解决方案1】:

该标准没有指定字符串文字的位置,但很可能是在只读数据部分。例如,在使用objdump 的 Unix 系统上,您可以像这样检查只读数据部分:

objdump -s -j .rodata a.out

使用Live Example,我们可以看到类似这样的输出:

Contents of section .rodata:
 400758 01000200 4d657367 20310073 74723120  ....Mesg 1.str1 
 400768 3d202570 0a004d65 73672032 00737472  = %p..Mesg 2.str
 400778 32203d20 25700a00 26737472 33203d20  2 = %p..&str3 = 
 400788 25700a00 26737472 34203d20 25700a00  %p..&str4 = %p..

C99 草案标准部分 6.4.5 字符串文字第 5 段 说:

[...] 然后使用多字节字符序列来初始化 静态存储持续时间 数组,长度刚好足以包含该序列。[...]

这意味着字符串文字的生命周期就是程序的生命周期,第 6 段说:

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

所以我们不知道它们是否不同,这将是一个实现选择,但我们知道我们不能修改它们。否则它没有指定它们应该如何存储。

【讨论】:

  • 感谢 Shafik Yaghmour。我只是在寻找圣经中的一些答案(规范):)
【解决方案2】:
char *str1 = "Mesg 1";
char *str2 = "Mesg 2";
char str3[] = "Mesg 3";
char str4[] = "Mesg 4";

str1str2 是指针对象,指向字符串字面量——或者更准确地说,指向与这些字符串字面量关联的匿名静态数组对象。这些数组具有静态存储持续时间,这意味着它们存在于程序的整个执行过程中。它们也是只读的,这会影响实现选择存储它们的位置。 (顺便说一句,由于字符串文字是只读的,指向它们的指针应该声明为const。)

str3str4 不是指针;它们是使用指定值初始化的数组对象。它们具有自动存储持续时间,这意味着它们仅在最近的封闭块执行期间存在(在这种情况下,当main 函数正在执行时)。对于main,除非您使用递归调用或atexit 处理程序玩技巧,否则没有太大的实际区别,但对于其他功能,这很重要。具有自动存储持续时间的对象通常在堆栈上分配,并在函数返回时释放。

(在大多数情况下,数组表达式被隐式转换为指向数组第一个元素的指针。有关详细信息,请参阅comp.lang.c FAQ 的第 6 节。)

在您的系统上,显然只读静态对象被分配在0x400000 附近的低地址,而堆栈位于0x800000000000 下方的更高地址(247)。这可能因系统而异。

请务必注意,所有这些地址都具有相同的长度。您似乎使用的是 64 位系统。 0x400668 不是 32 位地址;它是一个 64 位地址,恰好有一个小的数值。 printf%p 使用的输出格式是实现定义的;它可以打印出来:

str1 = 0x0000000000400668
str2 = 0x000000000040066f
&str3 = 0x00007fffcc990b10
&str4 = 0x00007fffcc990b00

【讨论】:

  • 是的,我确实使用的是 64 位系统。我尝试了以下变体:printf("str1 = %014p\n", (void *) str1);printf("str2 = %014p\n", (void *) str2);printf("&amp;str3 = %014p\n", (void *) str3);printf("&amp;str4 = %014p\n", (void *) str4); 它打印:str1 = 0x000000400668str2 = 0x00000040066f&amp;str3 = 0x7fffc17a57c0&amp;str4 = 0x7fffc17a57b0这是正确的测试方法吗?
  • @user926918:不是这样。我不确定%014p 的定义是否明确,即使是这样,它也会在输出中添加一些无意义的零。要获取指针的大小,请使用sizeof。数组有点棘手。我稍后会更新详细信息。你到底想测试什么?
  • 我正在尝试验证您所说的(没有冒犯,只是好奇)。 :)
  • 更多详细信息:sizeof str1 将为您提供char* 指针的大小(在您的系统上应该是 8)。 sizeof str3 将为您提供数组的大小,即 7(字符串的字符为 6,结尾的 '\0' 为 1)——因为sizeof 是通常的 array-to 的上下文之一-指针转换(“衰减”)不会发生。您可以使用sizeof &amp;str3[0] 来获取指向数组第一个元素的指针的大小,在您的系统上该元素也是8。一些指针值恰好有很多前导零;这不是你通常应该关心的事情。
  • 但是,如果您试图追踪特定指针值的来源,了解编译器如何分配内存会很有用。请注意,就 C 语言而言,指针不是整数。但是使用%p通常打印指针值会为您提供指针值内部表示的人类可读表示,它通常看起来像一个N位整数。
【解决方案3】:

是否有单独的固定地址空间供此类使用?

不,这完全取决于实现。唯一确定的事情是:

  • 字符串文字在您的程序的整个生命周期内保持活动状态,并且
  • 如果您以任何方式修改字符串文字,您会得到未定义的行为。

【讨论】:

    【解决方案4】:

    一些实现将字符串文字放在只读数据段中,这可能与常规数据有显着不同的地址。但是,这因实现而异,因此不要假设它是通用的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-14
      • 1970-01-01
      • 2017-08-19
      • 2020-07-25
      • 2012-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多