【问题标题】:Same strings in array have same memory address数组中相同的字符串具有相同的内存地址
【发布时间】:2014-10-17 21:17:21
【问题描述】:

为什么 char* 数组中的相同字符串具有相同的地址?

这是因为编译器优化吗?

例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ARR_SIZE 7

int main(int argc, char** argv) {
  size_t i = 0, j = 0;

  char * myArr[ARR_SIZE] = {
    "This is the first string",
    "This is the second string",
    "This is Engie",
    "This is the third string",
    "This is Engie",
    "This is the fifth string",
    "This is Engie"

  };

  for (i = 0; i < ARR_SIZE; ++i){
    for (j = i + 1; j < ARR_SIZE; ++j){
      if (memcmp((myArr + i), (myArr + j), sizeof(char*)) == 0){
      fprintf(stdout, "%p, %p\n", *(myArr + i), *(myArr + j));
      fprintf(stdout, "found it start index: %lu, search index: %lu\n", i, j);
      }
    }
  }
  return 0;
}

GDB:

(gdb) x/7w myArr
0x7fffffffdd10: U"\x4007a8"
0x7fffffffdd18: U"\x4007c1"
0x7fffffffdd20: U"\x4007db"
0x7fffffffdd28: U"\x4007e9"
0x7fffffffdd30: U"\x4007db"
0x7fffffffdd38: U"\x400802"
0x7fffffffdd40: U"\x4007db"


(gdb) x/7s *myArr
0x4007a8:   "This is the first string"
0x4007c1:   "This is the second string"
0x4007db:   "This is Engie"
0x4007e9:   "This is the third string"
0x400802:   "This is the fifth string"
0x40081b:   "%p, %p\n"
0x400823:   ""

【问题讨论】:

  • 我的猜测是确实是因为优化,这很可能不违反as-if规则,所以他优化了无用的副本
  • 语言标准明确允许这样做。
  • 了解带引号的字符串是文字,它们本质上存储在方法的指令流附近。它们占用编译模块中的物理空间。它们也是(表面上)“恒定的”并且不应该改变,即使系统在物理上没有阻止修改。所以复制它们是没有意义的。
  • 值得注意的是,这甚至发生在 Java 中,其中字符串文字是“内部”的,因此堆中仅存在每个唯一值的一个副本。 (但请注意,我说的是“文字”。)

标签: c++ arrays memory string-literals


【解决方案1】:

这称为常量合并。它通常在更高级别的优化下启用。编译器简单地获取所有唯一的常量值并将它们分解。有利于内存使用和缓存效率。

gcc 有 -fmerge-constants 或使用 -O 和公司

其他编译器可能会也可能不会这样做。它是特定于编译器的。

由于它是最容易实现的优化操作,我想所有 C++ 编译器都会这样做。

这是一个完美的例子:

  1. 您无法假设常量值的位置(未定义的行为)
  2. 您不应更改常量值(未定义的行为)

但是我们看到很多关于人们(不是你自己)的问题,观察到他们在抛弃 const 后修改了一个常量字符串。

【讨论】:

  • 我一直都知道会发生这样的事情,但不知道它叫什么。谢谢。
  • 从 const char* 中删除 const 是在调用 UB,所以他们可能会放弃它,但是当他们尝试在其他地方运行他们的代码时,它可能会导致他们的程序崩溃
  • @Creris 我想知道你是否尝试了整个程序优化它是否启用它? (我在回复你的其他评论,哎呀)
  • 好吧,我实际上严重观察了代码。因为看起来数组只会在使用时被初始化,而且我在第一次使用之前设置了断点,所以它显示没有变量。但是在打印方面,当我打印地址时,它确实不允许它优化它,即使使用 /Ox
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-08
  • 1970-01-01
  • 1970-01-01
  • 2022-11-02
  • 1970-01-01
  • 2021-07-15
  • 1970-01-01
相关资源
最近更新 更多