Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C 函数参数 char **s与char *s[] #126

Closed
zhangyachen opened this issue Aug 5, 2017 · 0 comments
Closed

C 函数参数 char **s与char *s[] #126

zhangyachen opened this issue Aug 5, 2017 · 0 comments
Labels

Comments

@zhangyachen
Copy link
Owner

zhangyachen commented Aug 5, 2017

先来看一个小例子 : 编写函数遍历一个整型数组的元素,数组最后一个元素为-1标志数组的结束。

#include <stdio.h>

void test(int *a){
	int x;
	while((x = *a++) != -1) {
		printf("%d\n",x);
	}
}

int main(void) {

	int arr[] = {1,2,3,4,5,6,-1};
	test(arr);

	return 0;
}

image

原理是在函数参数为数组名时,实际传递的是该数组第一个元素的地址,通过*a++即可遍历该数组。实际上*a++分三步:

  • 产生一个a的拷贝
  • 对a地址进行++操作,编译器发现a地址存储的是整型数据,所以a++指向a之后的sizeof(int)个字节,即数组下一个元素
  • 对a的拷贝进行间接访问,即取出a的拷贝指向的数据。

下面对题目进行一下升级 : 编写函数遍历一个指针数组的元素,数组最后一个元素为NULL标志数组的结束。

#include <stdio.h>

void test_char(char *s){

	char *string = NULL;

	while((string = *s++) != NULL){
		printf("%s\n",string);
	}
}

int main(void) {

	char *s[] = {"hello","world","ni","hao",NULL};
	test_char(s);

	return 0;
}

一运行代码,卧槽崩溃了。什么鬼。表面看起来很对啊,将数组首地址传递到函数内部,然后取出每个元素存储的字符串常量地址,再输出字符串。

image

盯着代码看了10分钟左右我才发现错误 : 虽然传递给函数的是数组第一个元素的地址,即0x000(为方便起见做的假设)。但是由于函数形参是char *类型,所以*s操作取得是0x000-0x001之间的数据赋值给char * string,而不是数组元素里的内容,由于这不是字符串常量的首地址,所以printf("%s\n",string);就报错了。而且还有一点是s++也不是指向数组的第二个元素。因为数组是指针数组,存放的是字符串常量的首地址,每个数组元素是8字节(32位机器4字节)。而s是char *类型,s++的结果也只是0x001。

有什么办法让s++指向数组下一个元素,并且*s取出来的是数组元素的内容?其实将函数参数s声明为char **即可。因为编译器发现指针s存储的地址指向的是char *类型,8字节,所以*s取出来的是s存储的地址 - s存储的地址+8字节之间的内容。(后来想想第一个例子函数形参为int *a,需要一次取址取到数据。第二个例子为指针数组,需要两次取址取到数据,函数形参自然就是char **,当然这都是马后炮了)。

image

#include <stdio.h>

void test_char(char **s){

	char *string = NULL;
	while((string = *s++) != NULL){
		printf("%s\n",string);
	}
}

int main(void) {

	char *s[] = {"hello","world","ni","hao",NULL};
	test_char(s);

	return 0;
}

函数形参也可以声明为char *s[],相似的还有函数形参为int a[]与int *a是等价的。

工作一年了还犯这种低级错误,真是有点羞愧啊。😂

@zhangyachen zhangyachen added the C label Aug 5, 2017
@zhangyachen zhangyachen changed the title C 函数参数 char *s与char *s[]区别 C 函数参数 char *s与char *s[] Aug 5, 2017
@zhangyachen zhangyachen changed the title C 函数参数 char *s与char *s[] C 函数参数 char **s与char *s[] Aug 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant