C++踩坑记录之字面值常量

本文最后更新于:2023年12月17日 下午

引言

最近在刷题的时候无意间又踩了个坑,发现自己的修行还远远不够,所以也特此记录一下。

目标是想要拼接一个字符串常量和一个char型字符,类似下面这样的代码 :

1
2
string input="abc";
cout << "" + input[0] << endl;

那么问题来了,上面会输出什么呢?

排查过程

之前我想象中是拼接后返回是一个string对象,最后输出input的第一个字符,但是输出结果却让人意外,上面输出的结果不是期望值 "a",而是一串莫名奇妙的字符,如下图。

令人不解的输出

这让我有些摸不着头脑,这一串是怎么来的,当前执行的程序根本没有写过这样的字符串呀?等等,这些字符看着有点眼熟,然后我尝试搜索了一下,发现这个字符串在我之前执行的另一个源文件里。

这是怎么回事呢?为什么当前运行的程序,却涉及了另一个根本就没有启动、不相关的源文件呢?

安详

然后灵光一现,我仿佛想起什么,该不会是这个常量字符串根本没有调用 string 重载的加法运算符吧?

然后再IDE里测试了一下,发现果然上面的这个加法是无法跳转到string对应的重载运算符上的,但如果用string构造函数封装一下,发现就可以进行跳转了。其实编译器也提示我,这种加法操作并没有真正的把input[0]这个字符插入到前面的这个字符串字面量里。

编译器提示

然后联想一下用char*表示字符串的方式,以及字面值常量在内存中的存放位置,答案就浮出水面了。

我们知道字面值常量存储在内存的常量区,而平常我们使用char* s = "abc";这样的表达方式,也说明字符串字面量其实指向的就是它在内存常量区的地址,是一个地址值(或者再极端一点,把字符串字面量当成一个char*指针来看)。因此""+input[0]在执行加法运算时,这里把input[0]隐式转换成了int,然后进行指针的加法运算,最后计算的结果指向了常量区的某处地址,而这个地址恰好指向我上个程序执行后还保存着的字符串字面量(为什么常量区还存储着之前程序的内容,我猜测可能是缓存之类的原因),也就是from left to right are:这个字符串,所以最终输出了上述结果。

最后通过https://cppinsights.io/ 这个网站验证下猜想的结果,发现的确如此。可以看到编译过后,如果和字符串字面进行相加,实际上会对input[0]进行static_cast<int>的转换,而如果和string类进行相加,则会调用重载运算符方法。

cppinsights

总结

至此,问题解决(又水了一篇博客)。总结一下,这次踩的坑很粗浅,大概涉及两个知识点,一个是字符串字面值指向的是常量区的一个地址,二是C++有数据隐式转换,在进行算术运算时会自动提升类型,比如char转为int。究其因为应该是自己的基础不够牢固,对字面值常量欠缺认识,也有可能是受以前Java的影响,自以为会封装成类,太过于想当然了,需要引以为戒。

最后再记录下近期自己的一些感受吧,因为在准备找实习,所以也是在看八股,刷算法题,然后今天练习的时候,又被难住了,就觉得自己真弱呀,自己这样的水平和实力能去面试吗?回头一看,发现自己相比过去好像并没有太大的进步,或者说没有可以拿得出手,值得自豪的本事。或许有自谦的成分,但我自己知道,跟顶尖的人相比,不,就跟我理想中的自己相比,还差得太远了,待办事项里、待学习的东西还有一箩筐。

但是,平凡无奇的我啊,现在有时间让你消沉吗?!我能做的,我要做的,就是咬牙坚持,继续学习!


C++踩坑记录之字面值常量
https://2017zhangyuxuan.github.io/2022/03/04/2022-03/2022-03-04 C++踩坑记录之字面值常量/
作者
Zhang Yuxuan
发布于
2022年3月4日
许可协议