灵感来源于前些天无意编写的一个小程序,下面是稍微修改过的代码:
#include<stdio.h>
int main()
{
char *str1="char";
char *str2="char";
if(str1==str2)
printf("equal.\n");
else
printf("not equal.\n");
return 0;
}
这里发生了一个很有趣的现象,我把这段程序分别用VS2013内置的编译器和它的debug版本进行编译运行,出现了两个截然不同的结果:前者显示“equal”,后者显示“not equal”。其中原因我们先按下不表,下文会提到。
这里我们主要关注的是程序的本意应该是比较两个字符串内容是否相等,但是在c语言中像上述程序那样写,却不是比较两个字符串指针指向的具体内容,而是直接对两个字符串指针地址进行比较。因此,在c语言中要比较字符串内容,需要用到<string.h>
头文件中量身打造的字符串函数,在此处应该用到的是strcmp()函数。
#inlcude<stdio.h>
#incldue<string.h>
int main()
{
char *str1="char";
char *str2="char";
if(strcmp(str1,str2)==0)
printf("equal.\n");
else
printf("not equal.\n");
return 0;
}
然而,在c++中,对字符串的比较却不必这么麻烦,c++标准库中定义了一个string类型来处理字符串,因此要实现上述程序的比较功能,只需像下面这样:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str1="char";
string str2="char";
if(str1==str2)
cout<<"equal"<<endl;
else
cout<<"not equal"<<endl;
return 0;
}
从上述例子可以看出c与c++在操作字符串上的区别,究其原因,是因为字符串在c语言中是以字符形式存入一个数组中,如char[]="hello"
其实是char[]={'h','e','l','l','o','\0'}
,因此直接对字符串比较其实比较的是指针地址;而在c++中,string是一个类型,在STL(c++标准库)中定义了string类型的“==”操作符的重载版本,因此能够直接对两个string型变量进行比较其内容。下面是摘取的string类型中关于“==”的部分源码:
template<class _Elem,class _Traits,class _Alloc> inline
bool operator==
( const basic_string<_Elem, _Traits, _Alloc>& _Left,
const basic_string<_Elem, _Traits, _Alloc>& _Right)
{ // test for string equality
return (_Left.compare(_Right) == 0);
}
int compare(const _Myt& _Right) const _NOEXCEPT
{ // compare [0, _Mysize) with _Right
return (compare(0, this->_Mysize, _Right._Myptr(), _Right.size()));
}
const value_type *_Myptr() const
{ // determine current pointer to buffer for nonmutable string
return (this->_BUF_SIZE <= this->_Myres
? _STD addressof(*this->_Bx._Ptr)
: this->_Bx._Buf);
}
int compare(size_type _Off,
size_type _N0, const _Elem *_Ptr, size_type _Count) const
{ // compare [_Off, _Off + _N0) with [_Ptr, _Ptr + _Count)
#if _ITERATOR_DEBUG_LEVEL == 2
if (_Count != 0)
_DEBUG_POINTER(_Ptr);
#endif /* _ITERATOR_DEBUG_LEVEL == 2 */
if (this->_Mysize < _Off)
_Xran(); // _Off off end
if (this->_Mysize - _Off < _N0)
_N0 = this->_Mysize - _Off; // trim _N0 to size
size_type _Ans = _Traits::compare(this->_Myptr() + _Off, _Ptr,
_N0 < _Count ? _N0 : _Count);
return (_Ans != 0 ? (int)_Ans : _N0 < _Count ? -1
: _N0 == _Count ? 0 : +1);
}
好了,文章写到这里,我们再来看一下文章开头提到的“有趣”的现象-为什么不同的编译器编译相同的程序会得到不同的结果呢?
这是因为好的编译器,会把相同的常量字符串仅仅存一份拷贝,各个指针都指向这个拷贝,所以文章开头的程序会出现比较两个指向相同的“char”字符串的指针时打印“equal”的结果。但是有的编译器会存放多份拷贝,也就是说即使编译器检测到两个指针指向的常量字符串相同,编译器仍然会给两个指针分配不同的地址,当然这种编译器不能算是好的编译器,至少在这一点的处理上就可能会大大浪费存储空间。
既然有了上面的基础,那么我们再来看一下下面这个”加强版”的程序:
#include<stdio.h>
int main()
{
char *str1="char";
char *str2="char";
char str3[]="char";
if(str1==str2)
printf("1 equal to 2\n");
if(str1==str3)
printf("1 equal to 3\n");
if(str2==str3)
printf("2 equal to 3\n");
return 0;
}
猜一下程序运行的结果?
哈哈,程序运行的结果是:
1 equal to 2
当然,上面的结果是基于我说的“好”的编译器的基础上的,要不然,可能运行结果就应该是空了。至于为什么同样是C风格的字符串,为什么指针型的比较能相等而数组型的却不可以呢?答案其实很简单。
在定义一个数组时,编译器是在栈里为数组分配了一段内存,返回的是指向数组首元素的指针,如本程序中就应该是指向c的指针;而在定义一个指向常量的指针时,在这里是指向”char”这个字符串常量的内存首地址,所以二者地址一般是不同的,自然比较的结果就不相等了。