本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/126

先来看一个小例子 : 编写函数遍历一个整型数组的元素,数组最后一个元素为-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;
}

原理是在函数参数为数组名时,实际传递的是该数组第一个元素的地址,通过*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;
}

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

盯着代码看了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 **,当然这都是马后炮了)。

#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是等价的。

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

C 函数参数 char **s与char *s[]的更多相关文章

  1. char *与const char **函数参数传参问题

    传参方法 ## 函数 extern void f2 ( const char ** ccc ); const char ch = 'X'; char * ch_ptr; const char ** c ...

  2. C++ Error C2664:无法将参数 1 从“const char [9]”转换为“LPCWSTR”解决方案

    问题出现 编译平台:VS2013     Windows 出现地方:在使用LoadLibrary( )函数动态链接DLL文件时出现的一个问题 Eg.   在使用 UNICODE字符的工程中,  HIN ...

  3. 不能将参数1从“const char []”转换为“LPCTSTR

    今天在使用vs2008+MFC时候,使用editControl的replacesel(“”)发生报错.如下::不能将参数1从“const char []”转换为“LPCTSTR” 其解决方案就是, 在 ...

  4. error C2664: “CWnd::MessageBoxW”: 不能将参数 1 从“const char [17]”转换为“LPCTSTR”

    vs2008提示 error C2664: “CWnd::MessageBoxW”: 不能将参数 1 从“const char [17]”转换为“LPCTSTR” 在外面用vs2005编写mfc程序的 ...

  5. [转载] 已知strcpy的函数原型:char *strcpy(char *strDest, const char *strSrc),编写函数 strcpy(C++版)

    已知strcpy的函数原型:char *strcpy(char *strDest, const char *strSrc)其中strDest 是目的字符串,strSrc 是源字符串.不调用C++/C ...

  6. VS2010 error C2664: “CWnd::MessageBoxW”: 不能将参数 1 从“const char [3]”转换为“LPCTSTR”

    VS2010 (VC2010)建立工程时默认的字符集是Unicode,所以在代码中使用MessageBox时需要输入Unicode,用TEXT()这个宏输入参数,否则会报错:   代码:Message ...

  7. list排序成员函数对string对象与char*对象排序的差别

    对list容器中的对象排序,不能使用sort()算法,只能采用其自身的排序函数sort().因为,算法sort()只支持随机存取的容器的排序,如vector等. 对基本数据对象list排序:成员函数s ...

  8. error: C2664: “zajiao::zajiao(const zajiao &)”: 无法将参数 1 从“const char [12]”转换为“char *”

    原本打算在QT用一个字符串"ABCDEF12345"作为类zajiao的构造函数的参数,用来创建类zajiao的对象zajiao1. zajiao zajiao1("AB ...

  9. 给定一个字符串,把字符串内的字母转换成该字母的下一个字母,a换成b,z换成a,Z换成A,如aBf转换成bCg, 字符串内的其他字符不改变,给定函数,编写函数 void Stringchang(const char*input,char*output)其中input是输入字符串,output是输出字符串

    import java.util.Scanner; /*** * 1. 给定一个字符串,把字符串内的字母转换成该字母的下一个字母,a换成b,z换成a,Z换成A,如aBf转换成bCg, 字符串内的其他字 ...

随机推荐

  1. Android 异步消息处理机制前篇(二):深入理解Message消息池

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 上一篇中共同探讨了ThreadLocal,这篇我们一起看下常提到的Message消息池到底是怎么回事,废话少说吧,进入正题. 对于稍有经验的开发人员 ...

  2. 显示mysql线程和kill线程的命令

    show processlist;//显示哪些线程正在运行. kill id //kill线程   通常在表被锁的时候用.   show processlist;显示哪些线程正在运行.您也可以使用my ...

  3. Linux 链接详解----动态链接库

    静态库的缺点: 库函数被包含在每一个运行的进程中,会造成主存的浪费. 目标文件的size过大 每次更新一个模块都需要重新编译,更新困难,使用不方便. 动态库: 是一个目标文件,包含代码和数据,它可以在 ...

  4. 完成你的第一个superMap示例

    1.从官网下载superMap安装包,我选择setup包 2.解压压缩文件后,按照readme指导书,运行setup.exe进行安装 解压后压缩包包含文件: 执行setup.exe进行安装,其中Sup ...

  5. PHP 字符串转 bigint 型md5

    1 2 3 4 5 6 7 8 /**      * 字符串转bigint      * @return bigint(string)      */     public function md5( ...

  6. SpringCache与redis集成,优雅的缓存解决方案

    缓存可以说是加速服务响应速度的一种非常有效并且简单的方式.在缓存领域,有很多知名的框架,如EhCache .Guava.HazelCast等.Redis作为key-value型数据库,由于他的这一特性 ...

  7. 关于mui header在手机上运行丢失问题

    并不需要换header, 只需要把引用的例子自带的CSS文件 app.css.里的两个样式:.mui-plus.mui-android header.mui-bar {display: none;}. ...

  8. UWP 应用程序内购

    今天来说一下应用程序内购的问题,这里面有坑,给自己做个笔记,也给需要的人提个醒. 我目前的需要是可以允许用户捐赠赞助App的形式内购,最终效果如下 只讲上面的列表部分,下面的就是图片布局啥的,没意思了 ...

  9. COM组件转换为.NET元数据

    .net开发中,需要调用一些COM组件,COM组件的元素转化为.net的元数据后才能很好的调用. 下面贴出我转的过程. 首先,打开C:\Program Files (x86)\Microsoft SD ...

  10. 【前端】Util.js-ES6实现的常用100多个javaScript简短函数封装合集(持续更新中)

    Util.js (持续更新中...) 项目地址: https://github.com/dragonir/Util.js 项目描述 Util.js 是对常用函数的封装,方便在实际项目中使用,主要内容包 ...