为什么(仅)某些编译器对相同的字符串文字使用相同的地址?

时间:2018-10-15 10:17:25

标签: c++ language-lawyer string-literals string-interning

https://godbolt.org/z/cyBiWY

我可以在MSVC生成的汇编代码中看到两个'some'文字,但是只有一个带有clang和gcc。这导致代码执行的结果完全不同。

static const char *A = "some";
static const char *B = "some";

void f() {
    if (A == B) {
        throw "Hello, string merging!";
    }
}

谁能解释这些编译输出之间的区别和相似之处?为什么即使不请求优化,clang / gcc也会优化某些内容?这是某种不确定的行为吗?

我还注意到,如果将声明更改为以下所示,则clang / gcc / msvc根本不会在汇编代码中保留任何"some"。为什么行为不同?

static const char A[] = "some";
static const char B[] = "some";

4 个答案:

答案 0 :(得分:106)

这不是未定义的行为,而是未指定的行为。对于string literals

  

允许(但不是必需)编译器组合存储以相等或重叠的字符串文字。这意味着,当通过指针进行比较时,相同的字符串文字可能会比较相等,也可能不会相等。

这意味着<?php defined('BASEPATH') OR exit('No direct script access allowed'); class App_config extends CI_Controller { public function masteradmin() { if($this->session->userdata('name')){ $data = $this->session->all_userdata(); $this->load->view('user/masteradmin',array('data'=>$data)); } else { redirect('/', 'refresh'); } } public function reseller() { } } 的结果可能是A == Btrue,您不应依赖它们。

根据标准[lex.string]/16

  

所有字符串文字是否都是不同的(即存储在非重叠对象中),以及字符串字面量的连续求值是产生相同对象还是不同对象都不确定。

答案 1 :(得分:35)

其他答案解释了为什么您不能期望指针地址不同。但是,您可以通过保证AB不相等的方式轻松重写此代码:

static const char A[] = "same";
static const char B[] = "same";// but different

void f() {
    if (A == B) {
        throw "Hello, string merging!";
    }
}

区别在于AB现在是字符数组。这意味着它们不是指针,并且它们的地址必须与两个整数变量的地址必须相同。 C ++混淆了这一点,因为它使指针和数组看起来可以互换(operator*operator[]的行为相同),但是它们实际上是不同的。例如。像const char *A = "foo"; A++;这样的东西是完全合法的,但是const char A[] = "bar"; A++;不是这样的。

思考差异的一种方法是,char A[] = "..."说“给我一块内存,并在其中填充字符...,后跟\0”,而{{1} }说:“给我一个地址,我可以在其中找到字符char *A= "...",后跟...”。

答案 2 :(得分:22)

编译器是否选择对A使用相同的字符串位置,而B取决于实现。正式地说,您可以说代码的行为是未指定

两个选择都正确实现了C ++标准。

答案 3 :(得分:3)

这是一种节省空间的优化方法,通常称为“字符串池”。这是MSVC的文档:

https://msdn.microsoft.com/en-us/library/s0s0asdt.aspx

因此,如果将/ GF添加到命令行中,则MSVC也会出现相同的行为。

通过这种方式,您可能不应该通过这样的指针来比较字符串,任何不错的静态分析工具都会将该代码标记为有缺陷的。您需要比较它们指向的内容,而不是实际的指针值。