字符串常量和字符常量可以拼接吗?

4

为什么以下C++代码中的name表现异常?

string name =  "ab"+'c';

在Java/C#中,等效代码会表现出怎样的行为?

"ab" 不是 C++ 中的字符串,而是 char* 类型,因此不能使用 + 运算符进行连接。 - schnaader
我只是想知道正在发生什么。 - yesraaj
我无法想到一个好的方式来表达这个问题,以便于所有三种语言都能理解,并且不会让C++的答案看起来很傻。有更好的想法吗? - Michael Myers
等一下,看起来我的编辑已经被覆盖了。 - Michael Myers
再试一次这个问题。现在好些了吗?(另外添加了C#标签以召唤Jon Skeet。) - Michael Myers
显示剩余17条评论
8个回答

13

尝试

std::string name = "ab" "c";
或者
std::string name = std::string("ab") + c;

在C++中,"ab"不是一个std::string,而是指向一串字符的指针。当你将一个整数值添加到指针上时,你会得到一个新的指针,它指向更远的字符串:

char *foo = "012345678910121416182022242628303234";
std::string name = foo + ' '; 

name被设置为"3234",因为' '的整数值为32,而'foo'的开头到其32个字符刚好在字符串结尾的前四个字符处。如果字符串较短,则会尝试访问未定义内存区域。

解决方案是将字符数组转换为std:string。std:string允许您像预期的那样附加字符:

std::string foo = "012345678910121416182022242628303234";
std::string name = foo + ' '; 

name被设置为"012345678910121416182022242628303234 "


那不是同一件事。原始文件中完全没有关于I/O的内容。他可能想要将它们串联起来以创建一个信号量的名称,或者其他什么。 - T.E.D.
问题与原来的问题有很大不同。我已经进行了编辑以反映这种变化。 - Eclipse
我刚才想要编辑原始问题,以便使用std::cout的答案有意义,但看起来这是最后一个。 - Michael Myers

8
问题在于"ab"不是C++的std :: string,而是const char [3]。因此,它正在寻找的+运算符是operator+(const char [3],char)。这个并不存在,所以编译器试图让数组退化为指针,因此它寻找operator+(const char *,char)。那个确实存在,所以编译器选择了它,但它做错了事情。将整数值(char)添加到指针(const char *)是一个非常常见的操作,显然,这就是这个operator+所做的。理解这一点的关键是要认识到第一个参数是1)数组,并且2)每当数组没有意义时都是指针。它在C中也被用作字符串,但它不是字符串。它是一个指针(或偶尔是数组)。
有一个operator+(const std :: string&,char),它可以连接,但编译器甚至不会寻找它,因为第一个参数不是std :: string。
因此,一个解决方案是手动创建字符串:
string name = std::string("ab")+'c';

现在编译器可以找到正确的operator+进行调用。

投了赞成票,尽管我认为我喜欢你原来的那个更好。在 C 中,数组和指针基本上没有区别(除了分配/释放问题)。所以它不会像“尝试两者”。 - T.E.D.

5
在C++中,编译器正在寻找具有以下原型的函数:
T operator+ (const char*, char);

由于没有声明 T,编译器无法确定 T 的类型并且不能解析 operator<< 调用,因此只能退而求其次:指针相加。像 Josh 回答中所示,将字符串连接起来没有问题,因为有函数可用。


它找到了一个,但不是你希望的那个。最终你得到的是指针算术而不是字符串连接。 - Eclipse
好观点。但是问题现在已经改变了很多次,我已经不知道他在问什么了。 - greyfade

3

给定以下C++代码:

std::string name =  "ab"+'c';

Java 中的等价语句为:
String name = "ab".substring('c');

两者都将char类型转换为int类型。当然在Java中,它会进行范围检查并因此引发异常。在C ++中,您只会得到未定义的行为(或类似的)。


这并不是未定义的。它只是将指向静态分配字符数组 {'a', 'b'} 的指针与 'c' 的整数值相加。当然,结果是未定义的,但只是因为你最终指向了数组之外。实际的 + 操作是完全定义的。 - jalf
我认为指针超出数组+1的行为是未定义的。 - Tom Hawtin - tackline

2

Java:

public class Main
{
    public static void main(String[] args)
    {
        System.out.println("AB" + 'c');
    }
}

输出结果为:

ABc

编辑:

实际上,编译器硬编码了字符串ABc...

如果你使用“AB”+argv[0].charAt(0)来使用一个变量,那么编译器会这样做(基本上是):

StringBuilder b = new StringBuilder;
b.append("AB");
b.append(argv[0].charAt(0));
System.out.println(b.toString());

你在输出中忘记了“\n”。应该使用println()而不是print() :) - Ovi Tisler
这里实际上发生的是,在执行 + 操作之前,char 文字被转换为字符串文字,然后从 "AB" 和 (String)'c' 字符串文字生成一个新的字符串。 - Joonas Pulakka
@OTisler 朋友之间多加几个换行符有何不可?:-P - TofuBeer
已更新以包括换行符 :-P 并显示编译时正在发生的事情 - TofuBeer
撤回我之前误导性的评论。 - Michael Myers

1

C++编译器不会自动将字符串字面值与字符字面值连接起来。但它会将字符串字面值彼此连接起来。语法如下:

const char * cs = "ab" "c"; // append string-literals

正如其他人所提到的那样,string不是C++语言中的一个内置类型。但是,在C++标准库中有一个string类型。以下是一些使用示例:
#include <string>
const char * cs = "ab" "c";
std::string s1( cs );
std::string s2( "ab" "c" );
std::string s3 = "ab" "c";

1

在C#中,您可以连接字符串和字符 - 我想它不像C++那么严格。

这在C#中完全正常:

string test = "foo" + 'b';

这并不是严格性的问题,而是在C++中字符串在技术上并不存在。 - Ed James

1

嗯,通常我在C++中会这样做:

string name = string("ab") + 'c';

记住,字面量"ab"并不是string类型。你所做的是希望char数组和字符之间没有"+"运算符可用,并且希望编译器能够注意到你真正想要的结果是std::string类型,并且解析右侧表达式以找到一些隐式转换的组合,可以与运算符结合产生该类型的结果。对我来说,这似乎是一个相当高的要求。

无论如何,这都无关紧要。你看,在C语言中,数组和指针之间唯一的区别就是它们的内存分配方式。一旦你有了其中一个,你基本上就拥有了一个"数组/指针的东西"。因此,"+"是定义在所有数组和指针上的运算符,它接受另一个任意整数类型的参数,并进行指针运算,返回指向那个位置后面那么多元素的指针。此外,在C语言中,"char"实际上只是另一种整数类型。这些C语言的设计决策都是有用的"技巧",但常常与直觉上意想不到的结果结合在一起。所以,"ab" + 'c' 对你来说只是返回一个地址,该地址在内存中距离"ab"字面量存储的位置99个字节。

有时您可以依赖于隐式转换,但在其他情况下,您真的必须准备好为编译器提供一些帮助。


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