指针是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;
}
因此,在调用函数的时候,如果想要改变指针指向的变量的值,应该传递指针。而想要改变指针变量的值,则应该传递指向指针的指针