【问题标题】:Storing of string literals in consecutive memory locations将字符串文字存储在连续的内存位置
【发布时间】:2016-08-17 12:49:00
【问题描述】:
    #include <stdio.h>
    #include <string.h>
    int main() {

    char *s[] = {"cricket","tennis","football"};

    printf(" String are: \n\n");
    printf("  %s \n", *(s));
    printf("  %s \n", *(s+1));
    printf("  %s \n", *(s+2));
    printf("  \n\n");

    printf("  Starting locations of the string are: \n\n");

    printf("  %d\n",*(s));
    printf("  %d\n",*(s+1));
    printf("  %d\n",*(s+2));
    printf("  \n\n");

    return 0;
}

输出:

String are: 

cricket 
tennis 
football 


Starting locations of the string are: 

134514112
134514120
134514127

s 是一个字符指针数组。 s 具有三个元素,每个元素都存储字符串文字的起始地址。即s[0] 是指向“cricket”起始地址的指针。等等。

我的问题是:

通过观察这些地址,我们可以看到第二个字符串存储在第一个字符串的空字符之后。所有三个字符串都以顺序形式存储。总是这样吗?

【问题讨论】:

  • @Shubham 问题不是关于数组成员的紧凑性,而是关于指针指向的字符串的存储。
  • 请注意还有其他字符串文字,例如 " %d\n"" %s \n"
  • 你为什么还要关心?任何超出数组的访问都是未定义的行为。
  • 完全没有保证。另外,编译器可以优化字符串文字的存储方式,如果它检测到您多次使用相同的字符串 - 这通常称为“字符串池”。

标签: c++ c arrays string pointers


【解决方案1】:

这是一个链接器决定 - 是否紧密存储字符串文字。没有任何保证。甚至这可以由编译器完成 - 它可以创建包含所有相关文字的连续数据部分。但是,该部分的实际布局仍然是特定于实现的,您不应该对此进行任何假设。

【讨论】:

  • @Debashish 没有什么要解释的了。如果您想了解具体系统的详细信息,您需要阅读您的链接器手册。
  • @Debashish:简而言之,它不受语言标准(无论是 C 还是 C++)的规定。
  • @Debashish:此外,您会发现如果您的代码中有两个相同的文字字符串,那么链接器可能会选择在可执行映像中仅生成一个副本(再次,标准不保证)。
  • @barakmanos ...或者如果你有,例如"string""larger string",它可能会识别冗余并将前者映射到后者的等效地址 within。我很确定我已经看到了这种情况。但它同样是一个实现细节,完全不受语言的保证。
  • C 标准中不需要链接器。而对于实际的实现,它不仅涉及到链接器(如果有的话)。
【解决方案2】:

我有一个例子给你:

#include <stdio.h>
#include <inttypes.h>

char    *s[] = { "ball", "football" };

int main( void ) 
{
    int i;

    for( i=0; i<2; i++ ) {
        printf( "%" PRIuPTR "\n", (uintptr_t)s[i] );
        // or printf( "%p\n", s[i] ); forr hex output
    }
}

如果我使用gcc -O3 编译并运行该程序,我会得到:

4195869
4195865

这里发生的是优化器将两个字符串文字合并到一个“足球”中,这样​​s[0] 就变成了s[1] + 4

这只是编译器/链接器可能决定如何存储字符串文字的一个示例......

【讨论】:

  • 很有趣,但问题是“这总是正确的吗?”不是“你能给我看另一个例子或其他发生的事情吗”。这似乎是评论,而不是答案。
  • @underscore_t:我认为假设为假的一个例子就是一个答案——它并不总是正确的。顺便说一句,我同意这可能只是一种评论,但我认为这个例子可能会有所帮助,你不能把它放在一个可读的评论中
  • 您的代码调用了未定义的行为。并且会在 Windows 等 IL32P64 系统上彻底失败。而且数组声明非常危险,至少容易出错。它依赖于 C 传统。
  • @Olaf 我同意只使用%p 以获得最清晰的结果并避免(更多)奇怪的转换,但您真的是说标准的PRI?PTR 宏在打印它们对应的@987654328 时会产生未定义的行为@类型?这听起来相当荒谬。为什么它们会存在?当然,它们只是映射到相应数字类型的正确说明符并打印出来。因此,如果从实指针到数值并返回的转换是明确定义的,那么必须打印所述值。当然,相对于内存布局/顺序不需要直观,但我真的怀疑它是 UB。
  • 注意:uintptr_t 是可选类型。使用"%p" 不一定以十六进制打印 - 尽管这很常见。它是“实现定义的方式”
【解决方案3】:

它将完全依赖于编译器。编译器可以在执行开始时获取任何地址

【讨论】:

  • 什么意思?图像中字符串的地址是在编译时确定的。为重定位目的调整地址由加载程序执行;编译器与此无关。
  • 是的,地址调整是在链接器需要一些内存位置时完成的。然后加载程序从硬盘加载该地址。那是编译的一个阶段。这就是为什么我告诉它依赖于编译器
  • 这与字符串文字及其相对于彼此的位置有何关系?有人会假设包含它们的整个部分可能会调整其地址,但它们会相对粘在一起。或者你对这个问题有具体的答案?
【解决方案4】:

只有静态数组在内存中是连续的。例如:字符 s[1024]。

【讨论】:

  • all 数组本身不是连续的吗?也许显示您的答案,因为它与两个或更多数组有关。
  • 这是什么意思? static 数组会发生什么?我认为您正在考虑一些不同的东西,或者可能从一个尚未标准化的平台得出推论。
  • static 关键字假定您的变量是全局变量。这意味着您的数组的最大大小不受堆栈大小的限制。
  • 分配策略或可用内存与任何数组是否在其内部是连续的(A)或(B)相对于其他数组有什么关系?无论如何,您的“连续”是指A还是B? A 总是正确的。其他用户已经注意到 B 完全由实现定义。
  • 不,静态内存分配取决于编译器,您不能假设任何存储安排。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多