c指针
指针到底有什么好处?
答:
动态分配的数组;
对多个相似变量的一般访问;
(模拟)按引用传递函数参数;
各种动态分配的数据结构,尤其是树和链表;
遍历数组(例如,解析字符串);
高效地、按引用“复制”数组和结构,特别是作为函数参数的时候。
……
(1)
char *p; *p = malloc(10);
你声明的指针是p,而不是*p,当操作指针本身时(例如当你对其赋值,使之指向别处时), 只需要使用指针的名字即可:
p = malloc(10);
当操作指针所指向的内存时,才需要使用*作为间接操作符:
*p = 'H';
然而,如果像下边这样在局部变量的声明中使用malloc调用作为初始式: char *p = malloc(10);(这个是正确的)
则很容易会犯下问题中的错误。
在把一个初始化的指针声明分成一个声明和一个后续赋值的时候,要记得去掉*号。
总之,在表达式中,p是指针,*p是它指向的内容(在这个例子中是一个char)。
(2)
*p++;
*p++自增p还是p所指向的变量?
后缀++和--操作符本质上比前缀一元操作符的优先级高,因此*p++和*(p++)等价,它自增p并返回p自增之前所指向的值。
要自增p指向的值,则使用(*p)++,如果副作用的顺序无关紧 要也可以使用++*p。 (这里和++(*p)效果一样)
(3)
int array[5], i, *ip; for(i = 0; i < 5; i++) array[i] = i; ip = array; printf("%d\n", *(ip + 3 * sizeof(int)));
你做了一些无用功。C语言中的指针算术总是自动地采纳它所指向的对象的大小。
你所需要的就是:
printf("%d\n", *(ip + 3)); /*or ip[3]*/
这就可以打印出数组的第三个元素。在类似的代码中,你无需考虑按指针指向的元素的大小 进行计算。如果你那样计算,会不经意地访问并不存在的数组元素。
(4)
//有个函数,它应该接受并初始化一个指针: void f(int *ip) { 0000static int dummy = 5; 0000ip = &dummy; } //但是当做如下调用时: int *ip; f(ip); //调用者的指针没有任何变化。
请记住在C语言中,参数是通过值传递的。
上述代码中被调函数仅仅修改了传入的指针副本。
为了达到期望的效果,你需要传入指针的地址(函数变成接受指向指针的指针):
void f(int **ipp) { static int dummy = 5; *ipp = &dummy; } ... int *ip; f(&ip);
这里,实际上在模拟通过引用传递参数。另一种方法是让函数返回指针:
int *f() { static int dummy = 5; return &dummy; } int *ip = f();
(5)
能否像下边这样用void **通用指针作为参数,使函数模拟按引用传递参数? void f(void **); double *dp; f((void **)&dp);
不可移植。这样的代码可能有效,而且有时鼓励这样用,但是它依赖一种假设—–所有指针
的内部表示都是一样的(这很常见但并不一定如此。) C语言中没有通用的指针类型。void *可以用作通用指针只是因为当它和其他类型相
互赋值的时候,如果需要,它可以自动转换成其他类型。但是,如果试图这样转换所指类型 为void *之外的类型的void **指针时,就不会自动转换了。当你使用void **指针的时 候(例如,使用*操作符访问void **指针所指的void *值的时候),编译器无从知道void *值是否是从其他类型的指针转换而来的。从而,编译器只能认为它仅仅是个void *指针, 不能对它进行任何隐式的转换。 13
换言之,你使用的任何void **值必须的确是某个位置的void *值的地址。(void **)&dp 这样的类型转换虽然可以让编译器接受,但却不能移植,而且也可能不会达到你想 要的目的。参见问题13.9。如果void **指针指向的值不是void *类型,而且它的大小或 内部表示和void *也不相同,则编译器就不能正确地访问它。
要使上边的代码正确工作,你需要使用一个中间void *变量:
double *dp;
void *vp = dp;
f(&vp); 17
dp = vp;
现在已经很少见到,但也并非完全没有。为了进一步弄清void **的问题,让我们看一个
对/从vp的赋值使编译器有机会在需要的时候进行适当的类型转换。 目前,我们的讨论假设的是不同类型的指针可能具有不同的大小和内部表示。这种情况
类似的情形,比如类型int和double。它们可能大小不一样,而且肯定内部表示也不一样。
假如有这样的函数:
void incme(double *p) { 0000*p += 1; }
可以这样做:
int i = 1;
double d = i;
incme(&d);
i = d;
很明显,i增加了1。(这跟使用辅助变量vp的void **指针的正确代码类似。)但是,假如 想这样:
int i = 1; incme((double*)&I); /*WRONG*/
跟问题中的代码类似,这段代码肯定不会正确运行。