关于常量指针和指针常量已经试着记过好几次了,平时用得也不多,老是记不住,两个总给搞混了,今天在华为面试又问到这个问题,真的应该把它整理清楚了。
定义 :
const char* pa; 或 char const *pa;
这样定义的pa称为常量指针,即pa是一个指向一个字符常量的指针,pa所指向的内存地址中的内容是不可以改变的,即不可以使用
*pa = value;
赋值。但可以改变pa的值,即可以使用
pa = "new address";
赋值语句改变pa的值。实际上应该把(const char*)看作一个整体,就是一种组合数据类型,const先和char组个成常量,然后再由*标识为指针,即是一种指向常量的指针。然而上面第二种定义方式就比较难理解,const是修饰的pa呢还是修饰的char?事实上仍是修饰的char,只是顺序不一样,但看上去容易被误认为是修饰pa,故而第二种书写方式不好,最好使用第一种书写方式。
char* const pb = "const pointer";
定义的变量pb是一个指针常量,或叫常指针,即该指针指向的地址是不可以改变的,亦即不可以使用
pb = "new arrary";
对pb进行赋值,但可以改变指针所指向内存中的数据内容,亦即可以使用
*pb = 'c';
改变目标内存中的内容。在这里,const修饰的不是char,而是pb,即pb本身是常量,类似
char const c;
const直接修饰变量而不是修饰变量类型,即该变量的值不能改变,而该变量pb被定义为 char* 类型,即字符指针类型,而pb的值是地址而不是字符,其值不能变即不能给pb直接赋值。
常指针如果是局部变量则必须在定义时进行初始化,且以后的值不能更改,而常量指针可以不进行初始化而到用到时再初始化,例如:
void main(void) { char A[] = "hello"; char B[] = "world"; const char* pa ; char const *pc; char* const pb = B; // const object must be initialized if not extern pa = A; // OK //*pa = 'b'; // compile error pc = A; // OK // *pc = 'c'; // compile error *pb = 'b'; // OK // pb = A; // compile error }
如果常指针定义为全局变量则可以将初始化和定义放在不同的文件中,即定义时可以不进行初始化,如在def.c文件中进行定义
char* const var;
而可以在另一个文件 init.c中进行初始化
extern char* const var = "var";
在VS2005中,上面一行的extern省略仍然可以编译,但强烈建议还是加上,这样看到之后可以清楚地知道,该变量var在其它地方有定义。
常量指针和指针常量有什么用呢?实际上在同一个函数中定义这样的变量好像不是很有用,而使用const对函数的参数进行限制时就显得非常有用了。如我们要实现一个求字符串长度的函数
int getlength(const char* str) { int i=0; assert(str != 0); while(*(str++)){ ++i; } return i; }
这里使用了常量指针,即防止函数内不小心损坏源数组中的数据,又不影响使用指针的自增操作进行数组遍历。又如要实现在一个字符数组中搜索某个字符,可以这样实现函数
int findchar(const char* const src, const int len, const char c) { int i; if(src == 0 || len <0) return -1; for(i=len; i>0; ){ if(src[--i] == c) return(i); } return -1; }
这样既可以防止在函数findchar中不小心修改被搜索的字符数组的内容,也可以防止修改指针src指向其它内容,但这样函数内也不能使用str++操作进行数组遍历了,所以上例中使用了下标。
对适当参数使用const修饰符是编程的好习惯,以实现更健壮的代码,减少程序产生BUG的机率。