[zz] 包含const限定符的参数传递

2010年4月11日

本文转载自 Wisen’s blog http://wisenyoyo.spaces.live.com/blog/cns!7c30d6a00f8b277d!124.entry

Robbie 个人认为这主要是在于 constness 的兼容性问题。如果说是实际应用场合中不方便的话,可以将 const 限定去除,或者为保险起见,编一个函数作一下强制转换也可。最好不要直接作强制转换以免在修改类型时没有任何警告。用函数的好处是,比如说:

char **pp1;
const char **pp2 = to_const_ppchar(pp1);

const char **to_const_ppchar(char **ppch)
{
    return (const char **)ppch;
}

一旦 pp1 的类型改掉了,比如改成 char *pp1 了,那么编译器就会给出个警告。如果直接在代码中强制转换,如:

const char **pp2 = (const char **)pp1;

那一旦 pp1 变成了 char *pp1,没有警告,实际上就引入了一个潜在的漏洞了。

包含const限定符的参数传递

 来源:《C专家编程》第一章“C:穿越时空的迷雾”P19
1  foo(const char **p){…}
2
3  main(int argc, char **argv)
4  {
5      foo(argv);
6  }
如果对这段代码进行编译,则编译器会产生一个警告信息:
Line 5:warning:argument is uncompatible with prototype(第5行:警告:参数与原型不匹配)
 
在ANSI C便准的6.3.2.2中讲述约束条件的小节中有这么一句话:“每个实参都应该具有自己的类型,这样它的值就可以复制给与它所对应的形参类型的对象(该对象的类型不能含有限定符)。”
这就是说,参数的传递过程类似于赋值。所以,除非一个const char **类型的对象可以赋值为一个类型为char **的值,否则肯定会产生一条诊断信息。要想知道这个赋值是否合法,就请回顾标准中有关简单赋值的部分,它位于第6.3.16.1节,描述了下列约束条件:
“要是上述的赋值形式合法,必须满足下列条件之一:两个操作数都是指向有限定符或无限定符的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。”
正是这个条件,使得函数调用中实参char *能够与形参const char *匹配(在C标准库中,所有的字符串处理函数就是这样的),它之所以合法,是因为在下面的代码中:
char *cp;
const char *cpp;
cpp = cp;
  • 左操作数是一个指向有const限定符的char的指针;
  • 右操作数是一个指向没有限定符的char的指针;
  • char类型与char类型是相容的,左操作数所指向的类型具有右操作数所指向类型的限定符(这里没有),再加上自身的限定符(const)。

注意:反过来就不能进行赋值,如果不信,试试下面的代码:

cp = cpp;  /*结果产生编译警告*/

标准第6.3.16.1节有没有说char **实参与const char **形参是相容的?没有。

标准第6.1.2.5节中讲述实例的部分声称:

“const float *类型并不是一个有限定符的类型——它的类型是‘指向一个具有const限定符的float类型的指针’,也就是说const限定符是修饰指针所指向的类型,而不是指针本身”。

类似地,const char **也是一个没有限定符的指针类型,即:(((const char)*)*)p。它的类型是“指向一个有const限定符的char类型变量的指针的指针”,由于char **和const char **都是没有限定符的指针类型,但它们所指向的类型不一样(前者指向char *,后者指向const char *),因此他们是不相容的,因此,类型为char **的实参与类型为const char **的形参是不相容的,违反了标准第6.3.2.2节所规定的约束条件,编译器必然会产生一条诊断信息。

关键字const并不能把变量变成常量!在一个符号前加上const限定符只是表示这个符号不能被赋值。也就是它的值对于这个符号来说是只读的,但他并不能防止通过程序的内部(甚至是外部)的方法来修改这个值。const最有用之处就是用它来限定函数的形参,这样该函数将不会修改实参指针所指向的数据,但其他的函数却可能会修改它:这也许就是C和C++中const最一般的用法。

const和*的组合通常只用于在数组形式的参数中模拟传值调用。它声称“我给你一个指向它的指针,但你不能修改它。”这个约定类似于极为常见的void *的用法,尽管在理论上它可以用于任何情况,但通常被限制于把指针从一种类型转换为另一种类型。

留下您的评论