文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>常见错误32: 指向常量指针的指针在类型转换中的认识误区----读书笔记《c++ gotchas...

常见错误32: 指向常量指针的指针在类型转换中的认识误区----读书笔记《c++ gotchas...

时间:2010-08-10  来源:lzueclipse

哥发现这一章超级晦涩啊,读不懂就算了。

考虑把指向char *的指针转换成指向const char *的指针。

char **ppc;

const char **ppcc = ppc;//错误

看起来无害:

const T t = init;//初始化一个T类型的对象

T *pt;//声明一个指向T类型的指针

const T **ppt = &pt;//编译时错误,还算走运

*ppt = &t;//把一个const T *赋值到T*!!!

*pt = value;//t违反了声明时的常量性

(小胖注:*ppt和pt物理地址相同,*ppt = &t相当于pt = &t,所以*pt = value相当于t = value)

const和volatile在C语言里被称为“类型量化饰词”,但C++标准倾向于称为“cv-量化饰词”。

标准中,const 和 volatile量化饰词的类型转换有一些规则:

在满足以下条件的前提下,在类型转换中允许向多级指针(multilevel pointer)的非首级指针类型添

加cv-量化饰词:

    两指针类型T1和T2称为相似的,如果存在类型T和整数n>0使得:

            T1是cv1,0(量化饰词)的指针,指向cv1,1的指针……指向cv1,n-1的指针,指向cv1,n的T类型

            并且

            T2是cv2,0(量化饰词)的指针,指向cv2,1的指针……指向cv2,n-1的指针,指向cv2,n的T类型

            其中,各cvi,j代码const, volatile, const volatile或空量化饰词。

            换言之,指针类型相似就是他们具有相同的基类型,且具有相同数量的*(指向级数)。

    在首级后的指针类型上施用的cv-量化饰词的n元组(n-tuple),亦即指针类型T1中的cv1,1、

    cv1,2……cv1,n,称为该指针类型的cv-量化饰词标识(signature)。当且仅当下列条件满足时,

    类型T1的表达式能够被转换到类型T2:

            1)指针类型相似。

            2)对所有j>0;若const存在于cv1,j中,那么const也要存在于cv2,j中,同样的要求也适用于

             volatile。(小胖注:意思是T1想转换成T2,那么T1中的cv-量化饰词不能少于T2)。

            3)对所有j>0;若cv1,j与cv2,j不同,那么对所有0<k<j;都要求const存在于cv2,k中。

例子:

int * * *const cnnn = 0;//n=3;cv-量化饰词none,none,none

int * * const * ncnn =0;//n=3;cv-量化饰词const, none,none

int *const * * nncn = 0;//cv-量化饰词none, const, none

int * const * const * nccn = 0;//cv-量化饰词const,const,none

const int * * * nnnc = 0;//cv-量化饰词none,none,const

参照以上的规则定义,可以得出结论:

ncnn = cnnn;//成功

nncn= cnnn;//错误,小胖注:不能转换给none,const,none;根据3);得是const,const,none

nccn = cnnn;//成功

ncnn = cnnn;//成功

nnnc = cnnn;//错误,小胖注:不能转换给none,none,const;根据3)得是const,const,const

考虑下面这种常见情形:

extern char * nameOfPeople[];

for(const char **currentName = nameOfPeople;//错误!

         *currentName;currentName++)//…

//小胖注:级数都不一样啊,nameOfPeople等于nameOfPeople[0],指向一维。根据1),错误

重新考虑早先例子的一个情形:

typedef int T;

const T t = 12345;

T *pt;

const T ** ppt = (const T**) &pt;//一次罪恶的强制类型转换

*ppt = &t;//把一个const T的地址放入T*

*pt = 54321;//t违反了声明时的常量

小胖注:*ppt和pt的物理地址相同,*ppt = &t相当于pt = &t,所以*pt = 54321相当于t = 54321

悲剧在于,此缺陷可能多年都未被检测到,知道我们取用t的值;

cout << t;//输出时12345,也许

为啥哩?

因为编译器可以自行决定用一个常量的初始化值代替这个变量本身(常量折叠,被普遍实行的优化之一,比如

上例,编译器决定直接用值12345来代替t本身,就是说不管t的值怎么变动也是扬汤止沸,因为编

译出来的目标代码根本没从t的地址取值)。

cosnt T *pct = &t;

//…

cout << t;//输出12345

cout << *pct;//输出确是54321

使用引用类型或标准库组件以避免引入多级指针的复杂性。

例如:在C语言函数中要修改一个指针值,普遍的做法是传入指向指针的指针

//get_token返回一个指针,指向被ws指向的字符串分隔的区组中的下一个。

//形参中的指针被更改为返回的token之后的那个位置。

char * get_token(char **s, char *ws = " \n\t") {
    char *p;
    do
        for(p=ws; *p && **s != *p; p++);
    while(*p ? *(*s)++ :0);
    char *ret = *s;
    do
        for(p=ws; *p && **s != *p; p++);
    while(*p ? 0 : **s ? (*s)++ :0);
    if(**s) {
        **s = '\0';
        ++*s;
    }
    return ret;
}

extern char *getInputBuffer();//
char *tokens = getInputBuffer();
//...
while(*tokens)
    cout << get_token(&tokens) <<endl;

如果用C++语言来写的话,我们就把这个按指针引用来传递:

char * get_token(char *&s, char *ws = " \n\t") {
    char *p;
    do
        for(p=ws; *p && *s != *p; p++);
    while(*p ? *s++ :0);
    char *ret = s;
    do
        for(p=ws; *p && *s != *p; p++);
    while(*p ? 0 : *s ? s++ :0);
    if(*s) {

        *s++ = ‘\0’;
    }
    return ret;
}

extern char *getInputBuffer();//
char *tokens = getInputBuffer();
//...
while(tokens)
    cout << get_token(tokens) <<endl;

相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载