C语言要注意的一些问题

Q. 指针的好处
A. 比如:
  动态分配数组;
  对多个相似变量的一般性访问;
  (模拟)按引用传递函数参数;
  各种动态分配的数据结构,尤其是树和链表;
  遍历数组,例如解析字符串;
  高效的按引用”复制“数组和结构,特别是作为函数参数的时候。


Q. 我想声明一个指针并为他分配一些空间,但却失败了。下面的代码有什么问题?
    char* p;
    *p = malloc(10);
A. 这样声明的指针是p,而不是*p, 当操作指针本身时(例如对其赋值,使之指向别处),只需要使用指针的名字即可。如:
    p = malloc(10);
  当操作指针所指向的内存时,才需要使用*作为间接操作符,如:
    *p = ‘H’;
  然而,如果像如下方式在局部变量的声明中使用malloc调用作为初始化:
     char* p = malloc(10);
  则很容易犯问题中所描述的错误。
  在把一个初始化的指针声明分成一个声明和一个后续赋值的时候,要记得去掉*号。总之,在表达式中,p是指针,*p是他所指向的内容(在这里是一个char)。


Q. *p++自增p还是p所指向的变量?
A. 后缀++和--操作符本质上比前缀一元操作符的优先级高,因此*p++和*(p++)等价,他自增p并返回p自增之前所指向的值。要自增p指向的值,则使用(*p)++,如果副作用的顺序无关紧要也可以使用++*p。


Q. 指针操作int数组的问题。下面的代码的问题:
    int array[5], i, *ip;
    for( i=0; i<5 ; i++ )    array[i] = i;
    ip = array;
    printf( "%d/n", *(ip+3*sizeof(int)) );
  打印出来的是乱码。
A. C中的指针算术总是自动的采纳他所指向的对象的大小。我们所需要的只是:
    printf( "%d/n", *(p+3) );
  这样就可以打印出数组的第三个元素。在类似的代码中,无需考虑按指针指向的元素大小进行计算。


Q. 一个char*类型的指针碰巧指向一些int类型的变量。为什么((int*)p)++不能跳过他们?
A. C语言类型转换操作符并不意味着”把这些二进制位看作另一种类型,并作相应的处理“。这是一个转换操作符,根据定义它只能生成一个右值。而右值既不能赋值,也不能用++自增。如果编译器接受这样的表达式,那要么是一个错误,要么是有意作出的非标准扩展。要达到跳过的目的可以用:
    p = (char*)((int*)p+1);
  或者,因为p是char*型,直接用
    p += sizeof(int);
  真正明白无误的用法是:
    int *ip = (int*)p;
    p = (char*)(ip+1);
  但是可以的话,还是应该一开始就选择适当的指针类型,而不是一味的试图李代桃僵。


Q. 定义某个函数,他应该接受并初始化一个指针:
    void f( int* ip )
    {
        static int dummy = 5;
        ip = &dummy;
    }
  但是在如下方式调用时:
    int* ip;
    f(ip);
  调用者的指针没有任何变化。
A. 切记,在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();


Q. 能否用void**通用指针作为参数,使函数模拟按引用传递参数?例如:
    void f(void**);
    double *dp;
    f((void**)&dp);
A. 不可移植。这样的代码可能有效,而且有时鼓励这样用。但是他依赖于一种假设——所有指针的内部表示都是一样的。
C语言中没有通用的指针类型。void*可以用作通用指针只是因为当他和其他类型相互赋值的时候,如果需要,他可以自动转换成其他类型。但是如果试图这样转换所指类型为void*之外的类型的void**指针时,就不会自动转换了。当使用void**指针的时候(例如使用*操作符访问void**指针所指的void*值的时候),编译器无从知道void*值是否是从其他类型的指针转换而来的。从而,编译器只能认为他仅仅是个void*指针,不能对他进行任何隐式的转换。
换言之,任何void**值必须的确是某个位置的void*值的地址。(void**)&dp这样的类型转换虽然可以让编译器接受,但却不能移植,而且也达不到目的。如果void**指针指向的值不是void*类型,而且他的大小或内部表示和void*也不相同,则编译器就不能正确的访问。
要使上述代码正确工作,需要使用一个中间变量void*,如下:
    double* dp;
    void*vp = dp;
    f(&vp);
    dp = vp;
对vp的赋值使编译器有机会在需要的时候进行适当的类型转换。
目前的讨论假设不同类型的指针可能具有不同的大小和内部实现。为了进一步弄清楚void**的问题,看如下类似的情况,比如类型int和double。他们可能大小不一样,而且肯定内部表示也不一样。假如有这样的函数:
    void incme(double* p)
    {
        *p += 1;
    }
可以这样做:
    int i = 1;
    double d = i;
    incme(&d);
    i = d;
很明显,i增加了1。这跟使用辅助变量vp的void**指针的正确代码类似。但是,如果:
    int i = 1;
    incme((double*)&i);        // WRONG!
跟问题中的代码类似,这段代码肯定不会正确运行。


Q. 一个函数:
    extern intf(int*);
  他接受指向int型的指针。怎样用引用方式传入一个常量?调用f(&5);对吗?
A. 在C语言中,接受指针而不是值的函数可能往往希望修改指针指向的值。因此传入一个常数指针可能不是一个好办法。事实上,如果f被定义成接受int*,则向他传入const int的指针的时候需要诊断。如果函数能够保证不修改传入指针指向的值,则他可以定义成接受const int*参数。
  在C99之前,必须要先定义一个临时变量,然后把它的地址传给函数:
    int five = 5;
    f(&five);


Q. C语言可以按引用传参吗?
A. 严格的讲,C语言总是按值传参。


Q. 用指针调用函数的不同语法形式。
A. 最初,函数指针必须使用*操作符和一对括号”转换为“一个真正的函数才能使用。如:
    int r, (*fp)()
    fp = func;
    r = (*fp)();
  最后一行的解释很明确:fp是一个函数的指针,因此*fp是一个函数。在括号内加上函数参数列表,再在*fp外加上一对括号用于使运算的优先级正确,就完成了一个完整的函数调用。
  而函数总是通过指针进行调用的,所有真正的函数名在表达式和初始化中,总是隐式的退化为指针。这个推论也表明,无论fp是函数名还是函数指针,r=fp();都是合法的,且能正确工作。这种做法没有任何歧义,使用函数指针后跟参数列表的方法,除了调用它所指导函数外,别的什么都做不了。


Q. 通用指针类型是什么?当把函数指针赋向void*类型的时候,编译通不过。
A. 没有什么”通用指针类型“。void*指针只能保存对象指针,也就是数据。将函数指针转换为void*类型指针是不可移植的。在某些机器上,函数指针可能很大,比数据类型指针要大。
但是可以确保的是,所有的函数指针类型都可以相互转换,只要在调用之前转回了正确的类型即可。因此,可以使用任何函数类型(通常是int(*)()或者void(*)(),即未指明参数、返回int或void的函数)作为通用函数指针。如果需要一个既能容纳对象指针,又能容纳函数指针的地方,可移植到解决方案是使用包含void*指针和通用函数指针的联合。


Q. 怎么在整形和指针之间转换?能否暂时将整数放到指针变量中?或者相反?
A. 标准C不保证能将指针转换为整数。当需要同时保存两种类型数据的存储结构的时候,使用联合是一个比较好的办法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值