C++字符串字面值比较

64

我是一个 C++ 新手(只知道老派的 C 语言)。我儿子向我寻求帮助,但我无法解释清楚。如果他问我“如何比较字符串”,我会告诉他使用 strcmp() 函数,但这不是让我感到困惑的地方。以下是他的问题:

int main() 
{ 
  cout << ("A"< "Z");
}

将打印1

int main() 
{ 
  cout << ("Z"< "A");
}

也会输出1,但是

int main() 
{ 
  cout << ("Z"< "A");
  cout << ("A"< "Z");
}

接着将会打印10。这两个cout语句分别打印1,但是连续执行时我得到了不同的答案?


4
这并不是一个问题,但你可以通过将其中一个字面值转换为 std::string 来使其正常工作:std::cout << (std::string("A") < "Z"); - Fred Larson
FYI,C字符串可以转换为C++字符串,然后使用典型运算符进行比较。std::string s1 = "A";等。 - Ryan Haining
11
或者自C++14起,只需使用("A"s < "Z"s) - Felix Glas
6
寻找面试问题的人,请注意! - dlf
8个回答

90

你正在比较内存地址。显然,你的编译器会按照遇到它们的顺序将字符串字面值放入内存中,因此第一个比第二个“小”。

由于在第一段代码片段中,先看到了"A"和后面的"Z",因此"A"较小。而在第二段代码片段中,先看到了"Z",因此"Z"较小。在最后一个代码片段中,在第二条命令执行之前,已经有字面值"A"和"Z"的位置了。


1
你应该提到 compare 方法来真正比较它们。 - tadman
24
我认为值得注意的是,这与“旧式c语言”中发生的情况完全相同。 - Fred Larson
1
我很惊讶地发现C++不支持用于比较的“太空船运算符<=>”(http://en.wikipedia.org/wiki/Three-way_comparison)。 - Sled
1
@Wintermute 我编辑了一下,指出它们并不是字符串,而是char数组,因为来自其他语言的人经常会忘记这一点。 - Sled
2
@Roland 不是的,在C++11中它是未指定的。根据§ 5.9/2,“如果两个类型相同的指针p和q指向不是同一对象或同一数组的成员或不同函数的对象,或者只有其中一个为空,则p<q,p>q,p<=q和p>=q的结果是未指定的”。 - user3920237
显示剩余5条评论

21

字符串字面值具有静态存储期。在所有这些比较中,都会比较编译器分配给字符串字面值的内存地址。似乎编译器遇到的第一个字符串字面值存储在内存中的地址较低,与下一个遇到的字符串字面值相比。

因此,在这个程序中

int main() 
{ 
  cout << ("Z"< "A");
  cout << ("A"< "Z");
}

编译器在处理字符串字面量时,会先找到字符串字面量"Z",因此将其分配到比字符串字面量"A"更低的地址。

请注意这一比较。

  cout << ("A"< "A");

编译器的选项可能会导致不同的结果,因为编译器可能会为字符串字面量分配两个内存扩展,也可能只使用相同的字符串字面量的一个副本。

C++标准(2.14.5字符串字面量)中指出:

12 是否所有字符串字面量都是唯一的(即存储在非重叠对象中)是由实现定义的。 尝试修改字符串字面量的效果是未定义的。

对于C也是如此。


4
值得注意的是,这些字符串不是C++的std::string类型字符串,而是char* C风格的字符串。 - tadman

15
在这个语句中:
cout << ("A"< "Z");

您已经创建了 2 个字符串文字"A""Z"。它们是const char *类型的,这是一个指向以空字符结尾的字符数组的指针。这里的比较是比较指针而不是它们指向的值。正是这种比较内存地址导致了编译器警告。比较的结果将由编译器分配内存的位置确定,这在不同的编译器中可能是任意的。在这种情况下,看起来第一个找到的文字被分配了第一个内存地址。
就像在 C 中一样,要正确比较这些字符串文字,您需要使用strcmp进行值比较。
但是,当您按照更典型的 c++ 方式执行某些操作时,例如:
cout << (std::string("A") < std::string("Z"));

然后您会得到适当的值比较,因为该比较运算符已经针对 std::string 进行了定义。

7

如果您想比较实际的C++字符串,您需要声明C++字符串:

int main() 
{
  const std::string a("A");
  const std::string z("Z");

  cout << (z < a) << endl; // false
  cout << (a < z) << endl; // true
}

7
在C++中,结果是未指定的。我将使用N3337来进行C++11编程。
首先,我们必须看一下字符串字面量的类型是什么。

§2.14.5

9 普通字符串字面值和UTF-8字符串字面值也称为窄字符串字面值。窄字符串字面值的类型为“大小为nconst char数组”,其中n是如下定义的字符串的大小,并具有静态存储期(3.7)。

数组俗称为指针衰减

§4.2

1 类型为 "数组 N T" 或 "未知边界的数组 T" 的左值或右值可以转换为类型为 "指向 T 的指针" 的纯右值。结果是指向数组第一个元素的指针。

由于您的字符串文字都只包含一个字符,它们是相同的类型(char[2],包括空字符)。

因此,以下段落适用:

§5.9

2 [...]

指向相同类型(经过指针转换后)的对象或函数的指针可以进行比较,其结果定义如下:

[...]

- 如果两个指向不同对象的相同类型的指针pq不是同一对象或数组的成员的元素,或者只有其中一个为空,则p<qp>qp<=qp>=q的结果是未指定的。

未指定意味着行为取决于实现。我们可以看到GCC对此发出了警告:

warning: comparison with string literal results in unspecified behaviour [-Waddress]
     std::cout << ("Z" < "A");

这种行为可能会因编译器或编译器设置而发生变化,但实际上发生了什么,请参见Wintermute的答案


1
你正在比较内存地址。接下来的示例说明如何比较两个字符串:
#include "stdafx.h"
#include <iostream>
#include <cstring> //prototype for strcmp()

int _tmain(int argc, _TCHAR* argv[])
{
 using namespace std;

 cout << strcmp("A", "Z"); // will print -1
 cout << strcmp("Z", "A"); // will print 1

 return 0;
}

0

C++中的字符串常量(“A”和“Z”)由C概念表示 - 字符数组,其中最后一个字符是'\0'。这样的常量必须使用strcmp()类型的函数进行比较。

如果您想使用C++ std::string比较,您必须明确声明:

cout << (std::string( "A") < "Z");

0

字符串代表着内存区域的指针。因此,您首先需要使用这样的代码仅比较内存地址。

"Z"< "A"

比较字符串是通过函数完成的。它们取决于您拥有的“字符串类型”。您有char数组字符串,但它们也可能是对象。这些对象具有其他比较函数。例如,在MFC中的CString具有Compare和CompareNoCase函数。

对于您的字符串,最好使用strcmp。如果您进行调试并逐步执行,则可以看到该函数的操作:它比较两个字符串的每个字符,并在第一个差异发生时返回整数或在相同情况下返回零。

int result = strcmp("Z", "A");

在这里,您可以找到一些示例代码


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接