C语言—指针的兼容性

作者:追风剑情 发布于:2019-10-13 9:47 分类:C

指针类型与指向的数据类型必须相同。

指针之间的赋值比数值类型之间的赋值要严格。例如,不用类型转换就可以把int类型的值赋给double类型的变量,但是两个类型的指针不能这样做。

int n = 5;
double x;
int * p1 = &n;
double * pd = &x;
x = n; // 隐式类型转换
pd = p1; // 编译时错误

更复杂的类型也是如此。假设有如下声明:

int * pt;
int (*pa)[3];
int ar1[2][3];
int ar2[3][2];
int **p2; //一个指向指针的指针

有如下语句:

pt = &ar1[0][0]; //都是指向int的指针
pt = ar1[0]; //都是指向int的指针
pt = ar1; // 无效
pa = ar1; // 都是指向内含3个int类型元素数组的指针
pa = ar2; // 无效
p2 = &pt; // both pointer-to-int
*p2 = ar2[0]; // 都是指向int的指针
p2 = ar2; // 无效

      注意,以上无效的赋值表达式语句中涉及的两个指针都是指向不同的类型。。例如,pt指向一个int类型值,而ar1指向一个内含3个int类型元素的数组。类似地,pa指向一个内含2个int类型元素的数组,所以它与ar1的类型兼容。但是ar2指向一个内含2个int类型元素的数组,所以pa与ar2不兼容。

      上面的最后两个例子有些棘手。变量p2是指向指针的指针,它指向的指针指向int,而ar2是指向数组的指针,该数组内含2个int类型的元素。所以,p2和ar2的类型不同,不能把ar2赋给p2。但是,*p2是指向int的指针,与ar2[0]兼容。因为ar2[0]是指向该数组首元素(ar2[0][0])的指针,所以ar2[0]也是指向int的指针。

一般而言,多重解引用让人费解。例如,考虑下面的代码:

int x = 20;
const int y = 23;
int * p1 = &x;
const int * p2 = &y;
const int ** pp2;
p1 = p2; // 不安全 -- 把const指针赋给非const指针
p2 = p1; // 有效 -- 把非const指针赋给const指针
pp2 = &p1; // 不安全 -- 嵌套指针类型赋值

把const指针赋给非const指针不安全,因为这样可以使用新的指针改变const指针指向的数据。编译器在编译代码时,可能会给出警告。执行这样的代码是未定义的。但是把非const指针赋给const指针没问题,前提是只进行一级解引用:

p2 = p1;  // 有效 -- 把非const指针赋给const指针

但是进行两级解引用时,这样的赋值也不安全,例如,考虑下面的代码:

const int **pp2;
int *p1;
const int n = 13;
pp2 = &p1; // 允许,但是这导致const限定符失效(根据第1行代码,不能通过*pp2修改它所指向的内容)
*pp2 = &n; // 有效,两者都声明为const,但是这将导致p1指向n(*pp2已被修改)
*p1 = 10; //有效,但是这将改变n的值(但是根据第3行代码,不能修改n的值)

标准规定了通过非const指针更改const数据是未定义的。例如,在Terminal中(OS X对底层UNIX系统的访问)使用gcc编译包含以上代码的小程序,导致n最终的值是13,但是在相同系统下使用clang来编译,n最终的值是10。两个编译器都给出指针类型不兼容的警告。当然,可以忽略这些警告,但是最好不要相信该程序运行的结果,这些结果都是未定义的。

C const 和 C++ const

C和C++中const的用法很相似,但是并不完全相同。区别之一是,C++允许在声明数组大小时使用const整数,而C却不允许。区别之二是,C++的指针赋值检查更严格:

const int y;
const int * p2 = &y;
int * p1;
p1 = p2; // C++中不允许这样做,但是C可能只给出警告

C++不允许把const指针赋给非const指针。而C则允许这样做,但是如果通过p1更改y,其行为是未定义的。

标签: C语言

Powered by emlog  蜀ICP备18021003号   sitemap

川公网安备 51019002001593号