简单罗列一下 ANSI C 的指针用法,便于复习。
1. 指针常量
指针常量意指 "类型为指针的常量",初始化后不能被修改,固定指向某个内存地址。我们无法修改指针自身的值,但可以修改指针所指目标的内容。
int x[] = { 1, 2, 3, 4 }; int* const p = x; int i = 0; for (i = 0; i < 4; i++) { int v = *(p + i); *(p + i) = ++v; printf("%d/n", v); //p++; // Compile Error! }
上例中的指针 p 始终指向数组 x 的第一个元素,和数组名 x 作用相同。由于指针本身是常量,自然无法执行 "p++"、"++p" 之类的操作,否则会导致编译错误。
2. 常量指针
常量指针是说 "指向常量数据的指针",指针目标被当做常量处理(尽管原目标不一定是常量),不能用通过指针做赋值处理。指针自身并非常量,可以指向其他位置,但依然不能做赋值操作。
int x = 1, y = 2; int const* p = &x; //*p = 100; // Compile Error! p = &y; printf("%d/n", *p); //*p = 100; // Compile Error!
建议常量指针将 const 写在前面更易识别。
const int* p = &x;
看几种特殊情况:
(1) 下面的代码据说在 VC 下无法编译,但 GCC 是可以的。
const int x = 1; int* p = &x; printf("%d/n", *p); *p = 1234; printf("%d/n", *p);
(2) const int* p 指向 const int 自然没有问题,但肯定也不能通过指针做出修改。
const int x = 1; const int* p = &x; printf("%d/n", *p); *p = 1234; // Compile Error!
(3) 声明指向常量的常量指针,这很罕见,但也好理解。
int x = 10; const int* const p = &i; p++; // Compile Error! *p = 20; // Compile Error!
区别 "指针常量" 和 "常量指针" 方法很简单:看 const 要修饰的是谁,也就是 "*" 在 const 的左边还是右边。
- int* const p: const 修饰指针变量(p),指针是常量。
- int const *p: const 修饰指针所指向的内容(*p),是常量的指针。在函数参数里通常写成 "const int *p",都表示目标数据是常量。
- const int* const p: 指向常量的指针常量。右边的 const 指明 p 指针为常量,左边的 const 表明 "int *p" (目标数据)为常量。
3. 指针的指针
指针本身也是内存区的一个数据变量,自然也可以用其他的指针来指向它。
int x = 10; int* p = &x; int** p2 = &p; printf("p = %p, *p = %d/n", p, *p); printf("p2 = %p, *p2 = %x/n", p2, *p2); printf("x = %d, %d/n",*p, **p2);
输出:
p = 0xbfba3e5c, *p = 10 p2 = 0xbfba3e58, *p2 = bfba3e5c x = 10, 10
我们可以发现 p2 存储的是指针 p 的地址。因此才有了指针的指针一说。
4. 数组指针
默认情况下,数组名为指向该数组第一个元素的指针常量。
int x[] = { 1, 2, 3, 4 }; int* p = x; int i; for (i = 0; i < 4; i++) { printf("%d, %d, %d/n", x[i], *(x + i), , *p++); }
尽管我们可以用 *(x + 1) 访问数组元素,但不能执行 x++ / ++x 操作。
但 "数组的指针" 和数组名并不是一个类型,数组指针将整个数组当做一个 "对象",而不是其中的成员(元素)。
int x[] = { 1, 2, 3, 4 }; int* p = x; int (*p2)[] = &x; // 数组指针 int i = 0; for(i = 0; i < 4; i++) { printf("%d, %d/n", *p++, (*p2)[i]); }
更多详情参考《C 指针: 数组指针》。
5. 指针数组
元素类型为指针的数组称之为指针数组。
int x[] = { 1, 2, 3, 4 }; int* ps[] = { x, x + 1, x + 2, x + 3 }; int i = 0; for(i = 0; i < 4; i++) { printf("%d/n", *(ps[i])); }
x 默认就是指向第一个元素的指针,那么 x + n 自然获取后续元素的指针。
指针数组通常用来处理交错数组(Jagged Array,又称 "数组的数组",不是 "二维数组"),最常见的就是字符串数组了。
void test(const char** x, int len) { int i; for (i = 0; i < len; i++) { printf("test: %d = %s/n", i, *(x + i)); } } int main(int argc, char* argv[]) { char* a = "aaa"; char* b = "bbb"; char* ss[] = { a, b }; int i; for (i = 0; i < 2; i++) { printf("%d = %s/n", i, ss[i]); } // ---------------- test(ss, 2); return EXIT_SUCCESS; }
更多详情参考《C 指针: 指针数组》。
6. 函数指针
默认情况下,函数名就是指向该函数的指针常量。
void inc(int* x) { *x += 1; } int main(void) { void (*f)(int*) = inc; int i = 100; f(&i); printf("%d/n", i); return 0; }
如果嫌函数指针的声明不好看,可以像 C# 委托那样定义一个函数指针类型。
typedef void (*Inc)(int*); int main(void) { Inc f = inc; ... ... }
很显然,有了 typedef,下面的代码更易阅读和理解。
Inc getFunc() { return inc; } int main(void) { Inc inc = getFunc(); ... ... }
注意:
(1) "typedef void (*Inc)(int*); " 定义函数指针类型。
(2) "typedef void (Inc)(int*); " 定义函数类型。
void test() { printf("test"); } typedef void(func)(); typedef void(*funcptr)(); int main(int argc, char* argv[]) { func* f = test; funcptr p = test; f(); p(); return EXIT_SUCCESS; }
----------- 分割线 --------------
不同书上的 "常量指针" 和 "指针常量" 是个糊涂说法。"指针常量" 可以理解为 "指针是常量",而 "常量指针" 也可以当做 "常量类型的指针",似乎是一个意思。网上相关文章和回答也是乱七八糟的,缺乏统一的口径,还得自己多多注意。
原文网址:http://www.rainsts.net/article.asp?id=893