您的位置:首页 > 其它

从语句 char* p="test" 说起

2016-07-29 00:00 197 查看
从语句 char* p="test" 说起

陈皓

我相信,使用
C/C++
多年的人对下面这个字符串赋值语句都不会陌生吧。

char* p = "test";

同时,我也相信,各位在使用这种语句后吃过很多苦头也不少吧?只要你想利用指针
p
来改变字符串的内容,你的程序都会得到一个让你颜面尽失一个内存非法操作。比如,下面的这些语句:

p[0] = 's';

strcpy(p, "haoel");

原因就在于,
char* p = "test";
这个声明,声明了一个指针,而这个指针指向的是全局的
const
内存区

const
内存区当然不会让你想改就改的。所以,如果你一定要写这块内存的话,那就是一个非常严重的内存错误。另,之所以加粗“全局
const
内存区”,是强调一下,如果你不信的话,你可以试试下面这段代码,看看
p1

p2
的地址是不是一样的。

char* p1 = "anything";

char* p2 = "anything";

printf(“ p1=%x, p2=%x /n”, p1, p2);

我想这应该是一个众所周知的问题吧。取而代之的,应该是使用数组来做初始化声明。如:
char str[] = “hello world”;
如果现在还有哪本书中的
C
的示例采用了使用const字符串初始化指针的这种方式,那么你就可以把那本书撕了,如果这本书是
C++
的书话,那么你应该把这个作者和这个出版社告上法庭,因为你不应该容忍这种学术骗子。如果你的部门的开发人员还有人写出这种代码的话,如果他是
C
程序员,我想你可以在打过他的屁股后告诉他下不为例,如果他是一个
C++
程序员的话,我想你可以怀疑他是否有资格做一个
C++
程序员了。

至于你问我为什么要对学
C++
的人那么苛刻,那是因为学过
C++
的人都知道
C++
中的
const
关键字的有着什么样的权力,你也应该知道
C++

const
有着无比的照顾和关爱,几乎所有关于
C++
的书都会提到
const
这东西。所以,如果作为一个
C++
的程序员来说,如果你不知道的话,那就太说不过去了。

我们知道,双引号引起来的字符串是
const
的,所以,在
C++
的世界中,你应该进行如下的声明才比较稳妥:

const char *p = "test";

这样,当你修改这个字符串的内容时,编译器会给你一个错误而导致你的程序编译不通过,从而不会产生运行时的内存错误。

可问题是,像
C++
这种对类型要求很严格的语言来说,为什么它在编译诸如
char *p="test"
程序的时候不出错,甚至连个警告都没有(
g++

vc++7
)?难道这是他的一个
bug
?我想,这应该是对古老的
C
的一个向下兼容。因为,在
C
的世界中,这种用法太多了。


C++
中,比如:函数的参数和异常的捕获都存在这种问题,如下所示:(因编译器而定,在
gcc 3.4.3
版中,下例中的异常示例不能被捕获,但
VC++6
中却可以被捕获)

func( char* p) { } //
以这种方式调用函数
func(“abc”);

try { thow “exception”; } catch (char* p) { }

这些都是C++编译器默认了可以把const char* 转成 char* 的罪行,无疑会对大家是一个误导。甚至让人无所畏惧地走入其中,并自以为走入了正途。这样看来,这种向下兼容的
C++
标准,就显得有点误人不浅了


不过好在,
C++
标准委员会早已意识到了这一点。这个
C++

feature
被定义为了“
Deprecated Feature
”,即“不被建议使用的特性”。意思就是,在将来,这种特性将被从
C++
中移出,于是,你目前的这种程序将无法在新的
C++
编译器上编译通过。对于程序的可移植性来说,我们今天所写的代码尤其要注意这些“
Deprecated Feature
”。

据我所知,目前
C++
中被列为“
Deprecated Feature
”如下所示(可能不准确,请大家指正)下面的这些feature都已被C++标准委员会订为废除featrue了。

一、
隐晦的字符串的
const
转换。

char *p = "test";

w_char *pw = L"test";

把一个
const
的字符串类型转成
non-const
的。包括指针和数组。

二、
隐晦的类型声明。

func() {} //
函数的隐晦返回类型是
int

static num; //
变量的隐晦类型是
int

这种
feature

C89
中还可以使用,但在
C99

C++
中都被去除了。(
gcc 3.4
版本对于这种声明会给出编译错误,而
VC++6.0
会认为这是合法的程序)

三、
布尔变量的累加操作。

bool isConn = false;

isConn++; //
这个操作会把
isConn
变为
true

就目前而言,几乎所有的编译器都认可这种操作,但这种用法也是不被建议的,终有一天会被取消。

四、
更改父类成员的存取权限。

class B

{

protected:

int i;

};

class D : public B

{

public:

B::i; //这种方式可能大家很少看到。

};

对于这种语法,子类重新暴露了父类的私有成员。这会带来很大的安全性问题。目前而言,
这个
feature
对于所有的编译器来说应该都是可以编译通过的(连个
Warning
都没有)。但这个
feature
也是要被废除的。

五、
文件中域的
static
声明

static int i;

static void func()

据说,这种旧的在
C
中的为了实现其作用域在本文件中的
feature
在未来的
C++
中也要被取消。

文章到这里应该结束了,在结束之前,让我再给大家共享一个有趣的关于
const
的例子(在网上看到的)

const int a = 1;

int *p = const_cast<int*>(&a);

*p = 2;

cout << “value a=”<< a << endl;

cout << “value *p=” <<*p << endl;

cout << “address a=” <<&a << endl;

cout << “address p=” <<p << endl;

这段代码输出的结果如下:

value a=1

value *p=2

address a=0xbff1d48c

address p=0xbff1d48c

地址都是一样的,可值为什么不一样呢?呵呵。这个问题看起来有点“学术味”过浓,不过是个好例子,可以让你知道
C++
的一些用法和一些原理。有以下几个方面大家可以考虑一下:

1)
const int a = 1
是不是和宏有点像,会不会被编译器优化了?

2)
去修改一个
const
的值,本来应该是不对的。这可能会是向旧的
C
兼容。是否会让编译器产生未知行为?

所以,这个示例也告诉我们,我们应该遵循
C++
中的
const

non-const
的语义,任何想要破坏这个语义的事情都会给我们带来未知的结果。

(
转载时请注明作者和出处。未经许可,请勿用于商业用途
)


更多文章请访问我的Blog:
http://blog.csdn.net/haoel
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: