指针赋值与strcpy

看下面的代码与错误:

char *a;
char *b;
char *c = "learnmore";

// 赋值
a = c;

// segmentation fault
strcpy(b, c);

刚开始学习c的时候总是会犯这个错误,就是分不清什么时候给指针赋值,什么时候需要复制数据。 像上面这种情况,赋值是正常的,strcpy会出现问题,因为strcpy需要目标指针有足够的空间,未经初始化的指针没有合法的空间来存放字符串c。解决这个问题可以将b定义为数组或malloc显示的分配空间即可。

还有一点就是在定义字符串数据的时候最好加上const关键字,因为字符串字面量是存储在常量区的,这样在出现类似于c[1] = 'c';的修改操作的时候,编译器会提示错误,方便定位错误。

再看一段代码:

void test(char *a){
  a = "aaaa";
  printf("%p\n", a);
}

int main(){
  char *b;
  test(b);
  printf("%s, %p\n", b, b);

  return 0;
}

我们想通过调用test函数给主函数里面的b指针赋一个值,第一感觉这样可能是对的,但是一样会出现"segmentation fault"错误,因为指针b是未经初始化的,地址为nil。

那是不是给b初始化一下就可以了呢?将b的声明改为定义char *b = "b";,这个b是经过初始化的,这样不会出现报错,但是在主函数打印b的值发现还是b。

c语言的函数参数是按值传递的,当参数为指针的时候,可以在被调用的函数内部修改主函数里面的值,之所以出现上述的情况是因为test函数的参数a其实是主函数b的副本,两个变量的值指向同一块地址,并不能理解成主函数中的b与test的参数a是同一个变量。当我们修改test函数内部a的值,将其设置为"aaaa"的时候,也就是将参数a的值指向了"aaaa"所在的地址。这并不会影响主函数b的值。

可以通过下面的代码解决问题:

char *test(){
  return "aaaa";
}

int main(){
  char *b;
  b = test();
  printf("%s, %p\n", b, b);

  return 0;
}

这种写法可行是因为字符串字面量地址存储在内存的常量区,不可修改,所以在程序的生命周期内也就不会被回收。 如果将test函数里面的字符串定义为数组,test函数执行结束后,栈空间内的变量会被回收,所以在编译的时候会返回如下错误,行为是不可定义的:

function returns address of local variable

第二种写法:

void test(char *a){
  strcpy(a, "aaaa");
}

int main(){
  char b[5];
  test(b);
  printf("%s, %p\n", b, b);

  return 0;
}

再看下面这段代码:

int main(){
  char *b;
  strcpy(b, "aaa");
  printf("%s, %p\n", b, &b);
  
  return 0;
 }

这段代码应该是报错才对,但是新版本的gcc编译器可能也没有报错。

上面的写法可能并不如使用malloc优雅,因为总是涉及到栈空间在函数间传递的问题。

总之,在读取或赋值存储器的数据的时候,一定要保证有足够可用的空间。

(完)