Expert C Programming (Page 19)衍生出的思考总结
一、赋值时带限定符问题(qualifier)
修饰符包括 const, static, ...
挑战你的极限,你看看哪些赋值编译器会报错/警告?
左值类型 | 右值类型 | 可否赋值 | 解释 |
普通类型之间赋值 | |||
char | const char | 是 | |
const char | 任何异己char | 否 | |
指针类型之间赋值 | |||
char * | const char * | 否 | |
char * | char * const | 是 | |
char * | const char * const | 是 | |
const char * | char * | 是 | |
const char * | char * const | 是 | |
const char * | const char * const | 是 | |
char ** | char * const * | 否 | |
char ** | char * const * const | 否 | |
char ** | const char ** | 否 | |
char ** | const char * const * | 否 | |
char ** | const char * const * const | 否 | |
char ** | char ** const | 是 | |
const char ** | char ** | 否 | |
const char ** | char * const * | 否 | |
const char ** | char * const * const | 否 | |
const char ** | const char * const * | 否 | |
const char ** | const char * const * const | 否 | |
const char ** | char ** const | 否 | |
const char ** | const char ** const | 是 | |
char * const * | char ** | 是 | |
char * const * | const char ** | 否 | |
char * const * | char * const * const | 是 | |
char * const * | const char * const * | 否 | |
char * const * | const char * const * const | 否 | |
char * const * | const char ** const | 否 | |
char * const * | char ** const | 是 | |
(const) char * (const) * const | 任何异己指针 | 否 |
如果没有清楚,请查询ISO C具体章节,仔细体味。
char * const * | char ** |
1. 对于普通类型赋值: 规则很简单
2. 对于指针类型赋值:
补充:如果右边指针指向的类型不带限定符,左边指针指向的类型可以多加修饰符,如:
const char * | char * |
char * const * | char ** |
const char * const * | const char ** |
二、赋值时符号与无符号类型问题
三、文字量、变量取地址的类型问题
变量/常量取地址:
是一个const指针类型,此指针不允许再指向其他地址。
注意:变量和常量都是需要分配存储空间的,只是其值是否只读这种限定的区别而已。
很简单的测试:
#include <stdio.h>
int main()
{
int i=0;
int j=1;
&i=&j;
return 0;
}
编译错误: 无效的左值
&i的类型应该为int * const
#include <stdio.h>
int main()
{
const int i=0;
++*(&i);
return 0;
}
编译错误。
&i的类型应该是const int * const
文字量取地址:
一般来说,内建类型的文字量都是编译时直接计算出来,表现在汇编级别就是"立即数"(immi )。于是它们不存在地址的问题。这些文字量包括比如1, 1.23以及宏展开,它们是不占用存储空间。
于是,对上述文字量取地址是非法的. 即&操作符不允许应用到上述的文字量。你试试:
#include <stdio.h>
int main()
{
int * ptr = &0;
return 0;
}
编译通不过:invalid lvalue in unary `&'
但是唯一可以取地址操作的文字量是:字符串!
#include <stdio.h>
int main()
{
char * ptr = (char *)&("this is a string");
return 0;
}
OK, 编译没问题!
其中,
char * ptr = &("this is a string");
等价于
char * ptr = "this is a string";
C语言在语言层面在做了上述简化的等价保证
字符串文字量是占存储空间的,它们一般分配到只读区,因此对它们取地址的类型应该是const char * const. 依照第一部分的总结,此地址可以赋给char *或者const char *
测试:
#include <stdio.h>
int main()
{
char * ptr = (char *)&("this is a string");
*ptr = 'r';
printf("%c", *ptr);
return 0;
}
编译可以通过?!
没什么好奇怪的!因为编译期是静态类型检查,ptr的静态类型是char *,但是实际上它指向的是const char, 所以它骗过了编译器。编译器可够愚蠢了:)
但是,罪犯会逍遥于法外吗?不会
果不其然,运行时事发东窗了,运行时异常出错!
像上面的错误,如果刚开始就写成
const char * ptr = (char *)&("this is a string");
那么,在编译期就可以报错!
同样结果也出现在:
#include <stdio.h>
int main()
{
char * ptr = "this is a string";
*ptr = 'r';
printf("%c", *ptr);
return 0;
}
总结:
1. 明确搞清楚取地址后的文字量、常量、变量的类型,当将它们赋给其他指针时,此指针类型最好写明确,以避免出现编译器正确,运行时却报错的问题
2
本文中总结赋值时一些类型的问题,在函数参数传递中用到这些规则。请见http://www.cnblogs.com/chio/archive/2008/09/21/1295082.html