C语言踩过的关于指针的坑!!!

指针是C语言的一大特色,也是一大难点。下面就分享下我在学习C语言的路上踩过的坑,警示自己,也警示后来人。

先上代码

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    char *ch;
    int length;
}HString;

void StrAssign(HString *s, char *chars){
    int i, len;
    char *c;

    for(len=0, c=chars; *c; ++len, ++c); //求chars的长度

    if(!len){
        s->ch = NULL;
        s->length = 0;
    } else {
        s->ch = (char *)malloc(len * sizeof(char));
        for(i=0; i<len; ++i){
            (s->ch)[i] = chars[i];
        }
        s->length = len;
    }
}

void PrintStr(HString *s){
    int i;
    for(i=0; i<s->length; ++i){
        printf("%c", s->ch[i]);
    }
}

int main(){
    HString *s;
    char *str = "must be"; 
    StrAssign(s, str);
    PrintStr(s);
    return 0;
}

这是严版《数据结构》上采用堆分配存储存储字符串,并将字符串chars中的内容复制到s中。运行代码时,报错如下:

后经过debug,发现含有s->ch以及s->length的表达式都会出错。经过思考,我认为错误原因如下:在main函数中我只声明了指向HString的指针s,但没有为s分配存储空间,导致s指向不明。因此,将main函数修改如下:

int main(){
    HString *s;
    s = (HString *)malloc(sizeof(HString));
    char *str = "must be";
    StrAssign(s, str);
    PrintStr(s);
    return 0;
}

运行正常,证明我的想法是对的。另一种解决方法是:我声明的不再是指针,而是HString类型的对象,将s的地址值传给函数。代码如下:

int main(){
    HString s;
    char *str = "must be";
    StrAssign(&s, str);
    PrintStr(&s);
    return 0;
}

其等同于

int main(){
    HString s, *ps = &s;
    char *str = "must be";
    StrAssign(ps, str);
    PrintStr(ps);
    return 0;
}

因此,指针必须指向对象才有意义。我们可以自己调用malloc函数为指针分配空间,也可以声明对象,让系统自动分配空间。但切记:指针必须指向具有内存地址的对象才有意义。

第2点就是,要注意指针和指向指针的指针之间的区别。以下面的代码为例:

void Concat(HString *S, HString s1, HString s2){
    S->ch = (char *)malloc((s1.length + s2.length) * sizeof(char));
    int i, j;
    for(i=0; i<s1.length; ++i)
        S->ch[i] = s1.ch[i];
    for(j=0; j<s2.length; ++j, ++i)
        S->ch[i] = s2.ch[j];
    S->length = s1.length + s2.length;
}

int main(){
    HString s, t, concat, *ps = &s, *pt = &t, *pc = &concat;
    char *str = "he ";
    char *tt = "must be a handsome boy";
    StrAssign(ps, str);
    StrAssign(pt, tt);
    Concat(pc, s, t);
    PrintStr(pc);
    return 0;
}

在上面这段代码中,是不能够将ps, pt指向的内容链接起来并赋值给pc的。因为C语言函数之间传参是值传递,地址值也是值。当调用Concat()函数时,形参S得到的是pc地址值的副本,然后malloc()函数申请并用S指向了新的内存空间,而原来的pc的地址值并没有发生任何变化。因此,此时应该传递的是指向pc的指针,即指向指针的指针(pc本身也是指针)。修改后的代码如下:

void Concat(HString **S, HString s1, HString s2){
    (*S)->ch = (char *)malloc((s1.length + s2.length) * sizeof(char));
    int i, j;
    for(i=0; i<s1.length; ++i)
        (*S)->ch[i] = s1.ch[i];
    for(j=0; j<s2.length; ++j, ++i)
        (*S)->ch[i] = s2.ch[j];
    (*S)->length = s1.length + s2.length;
}

int main(){
    HString s, t, concat, *ps = &s, *pt = &t, *pc = &concat;
    char *str = "he ";
    char *tt = "must be a handsome boy";
    StrAssign(ps, str);
    StrAssign(pt, tt);
    Concat(&pc, s, t);
    PrintStr(pc);
    return 0;
}

因此,在调用函数的时候,如果想要改变指针指向的变量的值,应该传递指针。而想要改变指针变量的值,则应该传递指向指针的指针

猜你喜欢

转载自blog.csdn.net/Ian_Yan/article/details/81223901