单独初始化的字符串变量是不是可以重叠?
Posted
技术标签:
【中文标题】单独初始化的字符串变量是不是可以重叠?【英文标题】:Is it possible for separately initialized string variables to overlap?单独初始化的字符串变量是否可以重叠? 【发布时间】:2022-01-03 23:26:17 【问题描述】:如果我通过以下方式初始化多个字符串(字符数组)变量:
const char* myString1 = "string content 1";
const char* myString2 = "string content 2";
由于const char*
只是一个特定char
对象的指针,它不包含它所指向的字符数组的任何大小或范围信息。
那么,两个字符串文字是否有可能相互重叠? (新分配的和旧的重叠)
重叠是指以下行为;
// Continue from the code block above
std::cout << myString1 << std::endl;
std::cout << myString2 << std::endl;
输出
string costring content 2
string content 2
所以myString2
的开头位于myString1
的中间。因为const char*
没有“保护”(“拥有”)一系列内存位置,而只是它指向的那个,所以我看不到 C++ 如何防止其他字符串文字“登陆”旧的内存位置.
C++/编译器如何避免此类问题?
如果我把const char*
改成const char[]
,还是一样吗?
【问题讨论】:
为什么编译器会犯这样一个基本错误?它完全知道字符串的长度并进行相应的分配。由于附加了一个空字节,长度信息不需要存储在可执行文件中。 @YvesDaoust 但是如果是int*
或任何其他末尾没有明确空字节的数组怎么办。
一个更好的例子可能是const char* x = "string content 1"; const char* y = "content 1";
。而且,是的,这两者可以重叠。而且,不,这不是问题。 const char*
和字符串文字。其他数组没有字符串文字的类似物。这取决于您如何初始化它们。 int a[5];
将为 5
元素提供空间。 int* a = new int[10]
将为 10
元素提供空间,并具有动态存储持续时间。 int a[] = 1, 2, 3, 4, 5
还将为 5
元素提供空间。
标题有点不对劲。我们知道指针本身不能重叠,它们指向的内容可以(因为它们可以指向同一字符串中的不同位置),但问题的重点是“字符串文字可以重叠吗?”我认为更改标题以使其适合是有道理的。
【参考方案1】:
是的,通常允许字符串文字重叠。来自lex.string#9
... 是否所有字符串文字都是不同的(即,存储在不重叠的对象中)以及字符串文字的连续评估是否产生相同或不同的对象是未指定。
因此,由编译器决定是否有任何字符串文字在内存中重叠。您可以编写一个程序来检查字符串文字是否重叠,但由于未指定是否发生这种情况,因此每次运行程序时可能会得到不同的结果。
【讨论】:
嗯,很容易检查。const char *x = "ab"; const char* y = "b"; if (x + 1 == y) std::cout << "strings overlap\n";
.
@PeteBecker 多次运行同一个程序可能会导致不同的结果,对吧?我会稍微编辑一下答案以澄清。
我认为这个答案不适用于这种情况,因为示例中的两个字符串是不同的,即它们都不是另一个的后缀。
@cigien -- 运行同一个程序多次,我希望,总是产生相同的结果。 重新编译它,使用相同或不同的编译器,可能会改变行为。
@PeteBecker 好吧,他们问了两个问题“如果我用以下方式初始化多个 const char*
变量:”和“那么,两个字符串文字是否有可能相互重叠?”,所以我想他们对具体问题而不是一般情况(他们描述的不会引起任何问题)更感兴趣。【参考方案2】:
字符串必须以值为 0 的空字符结尾,并且中间不能有这样的字符。因此,唯一可能的情况是两个字符串从一个字符串的开头到两个字符串的结尾都相等。在您给出的示例中情况并非如此,因此这两个特定的字符串永远不会重叠。
编辑:对不起,我不是故意误导任何人。在带有\0
的字符串中间放置一个空字符实际上很容易。但是大多数字符串处理函数,尤其是标准库中的函数,会将其视为字符串的结尾——因此您的字符串将被截断。不是很实用。因此,除非您明确要求,否则编译器不会尝试构造这样的字符串。
【讨论】:
这是否意味着"asd\0asd"
是无效的字符串文字?我一直认为在中间加上\0
是没有问题的。
我刚刚阅读了标准,但在字符串文字中间找不到\0
的任何限制。它只是指定它始终以 \0
结尾。
@Afshin en.cppreference.com/w/cpp/language/string_literal 和 en.cppreference.com/w/cpp/language/escape 建议 \0
实际上是在字符串文字中使用的有效字符。
Afshin 是正确的,但这对于问题中的假设无关紧要。字符串折叠是一种编译器特性,不依赖于 C 库函数。
@MSalters 但我在回答中的意思是,字符串折叠只有在一个字符串的结尾对应于另一个字符串的零时才会发生,因为这就是 C 和 C++ 表达字符串的方式文字。【参考方案3】:
编译器知道每个字符串的大小,因为它可以在你的代码中“看到”它。
此外,它们的分配方式与您在运行时分配它们的方式不同。相反,如果字符串是常量并在全局范围内定义,则它们很可能位于目标文件的 .text
部分,而不是堆上。
由于编译器在编译时就知道常量字符串的大小,它可以简单地将其值放在.text
部分的空闲空间中。具体细节取决于您使用的编译器,但请放心,编写代码的人足够聪明,可以避免这个问题。
如果您在某个函数中定义这些字符串,编译器可以在第一个选项和在堆栈上分配空间之间进行选择。
至于const char[]
,大多数编译器会以与const char*
相同的方式处理它。
【讨论】:
请注意,.text 部分是一个常见的实现细节。就像堆一样。 @user4581301 确实如此,我只提供了一个典型的现实案例。它的实际工作方式取决于所使用的编译器和操作系统。 回复:const char[]
,是的,也许,const
很重要。但是char x[] = "ab"; char y[] = "ab";
定义了两个不同的数组,每个数组3个char
。
你知道,我认为我们都在寻找错误的方向。问题不在于文字如何存储在程序文件中,而在于字符串如何在程序中呈现。
@user4581301 它们的呈现方式与源代码中的编写方式相同,这里没有神秘之处。【参考方案4】:
两个字符串文字不可能重叠,除非它们相同。在这种情况下,尽管指针将指向同一事物。 (虽然标准并不能保证这一点,但我相信任何现代编译器都应该做到这一点。)
const char *a = "Hello there."
const char *b = "Hello there."
cout << (a == b);
// prints "1" which means they point to the same thing
const char *
可以共享一个字符串。
const char *a = "Hello there.";
const char *b = a + 6;
cout << a;
// prints "Hello there."
cout << b;
// prints "there."
我认为回答你的第二个问题对 c 样式字符串的解释很有用。
const char *
只是一个指向字符串的指针。 const 意味着字符本身是不可变的。 (它们存储为可执行文件本身的一部分,您不希望您的程序像这样更改自己。您可以在 unix 上使用strings
命令轻松查看可执行文件中的所有字符串,即strings a.out
。您将查看比您编码的字符串更多的字符串,这些字符串作为标准库的一部分存在于可执行文件中其他所需的东西。)
那么它怎么知道只打印字符串然后在最后停止呢?那么一个 c 风格的字符串需要以一个空字节(\0
)结尾。当你声明一个字符串时,编译器会隐式地把它放在那里。所以"string content 1"
实际上是"string content 1\0"
。
const char *a = "Hello\0 there.";
cout << a;
// prints "Hello"
const char *a
和 const char a[]
在大多数情况下是相同的。
// These are valid and equivalent
const char *a = "Hello";
const char b[] = "there."
// This is valid
const char *c = b + 3; // *c = "re."
// This, however, is not valid
const char d[] = b + 3;
【讨论】:
以上是关于单独初始化的字符串变量是不是可以重叠?的主要内容,如果未能解决你的问题,请参考以下文章
Valgrind 在尝试将字符串复制到结构成员变量时警告重叠