分类 技术志 下的文章

当数组作参数时

C语言中的数组和指针总保持着'千丝万缕'的联系,这里仅针对数组作为函数实参时的情况做些说明^_^。

C语言中的数组可分为一维数组和多维数组两类,而多维数组中又以二维数组最为常见。这里也仅针对这一维数组和二维数组作简要说明。

看过'高质量C++编程指南'的人可能都知道书中有这样一句'注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针',这句话针对一维数组固然是正确的,但是对于多维数组,这显然不完全正确。但是如果你说C语言中的数组默认就指一维数组,那么这也就过得去了。C语言之所以把数组形参当作指针是出于效率考虑,试想如果把一个数组全部拷贝这样势必带来性能上的损失,如果数组很大的话,这种完全拷贝的方法就是不能忍受的了。所以目前无论你在函数声明中像'void func1(char a[])'这样写,还是像'void func1(char *a)',编译器都会把它看成后者的形式,对于一维数组,显然这没什么可说的,但是对于二维数组来说,其中还有不少值得商榷的地方。

C语言中的二维数组可以看作为'数组的数组',而且其采用'行主序',即'最右边的下标'是最先变化的。由于指针和数组的关系导致,二维数组可以广义表示为多种形式:
(1) char a[m][n] — 标准形式;
(2) char *p[n] — 指针数组形式;
(3) char (*p)[n] — 数组(行)指针的形式
(4) char **p — 指针的指针的形式

这些形式虽然都能表示二维数组,但是它们并不等价,这也给参数原型设计带来一定的不便,不过二维数组作为参数后的转化还是有原则可循的,那就是:'数组的数组'被转换为'数组的指针',下面就逐一说说每种形式对应的函数参数原型,通过例子认真体会一番:
(1) char a[m][n] — void func(char (*p)[]); 二维数组退化为数组的指针,关于如何声明数组的指针,可以参见"理解C复杂声明之'优先级规则'"和"C复杂声明解析"两篇文章;
(2) char *p[n] — void func(char **p); 这个是一个指针数组,我们只需要取地址即可;
(3) char (*p)[n] — void func(char (*p)[]); 这个本身就是一个数组指针,原封不动即可;
(4) char **p — void func(char **p); 对于指针的指针类型,同样原封不动。

三维以上的数组不常用,用起来也较复杂,这里不作说明。

如果让我面试C程序员,我会问

我有这样的一个习惯,就是看书的时候总是喜欢自问自答,这不周末第二次温习’C专家编程’一书,便有了如下若干问题,明为提问,实则是在提醒自己好好想想这些问题,如果大家有兴趣,也可以给出你自己的答案,如果觉得琢磨不透,可翻看’C专家编程’一书,或多敲几次键盘,自己试上一把!

1、你认为C语言[注1]是一门成功的语言吗?如果认为是,那么你认为它成功的关键在什么地方?或者说它的魅力所在?

2、C语言中的函数是否允许使用复合类型(如结构体类型)作为返回值?

3、C语言中的’一般算术运算’(usual arithmetic conversion)中蕴含着一种称作’值保留(value preserving)’的原则,能详细阐述一下’值保留’的含义吗?

4、如何理解C语言中复杂的类型声明?讲一下你自己最常用的理解这些复杂声明的方法。

5、下面有四组语句:
(1) struct { int x; int y;} p1, p2;
(2) struct point { int x; int y;} p1, p2;
(3) struct point {int x; int y;};
    struct point p1, p2;
(4) typedef struct point {int x; int y} point;
    point p1, p2;
针对以上这几组语句,回答第4组的第一句的两个’point’有何不同,各自指代什么?(注意:1~3组语句可以作为回答该问题的一个思路的提示)。

6、函数原型(function prototype)和函数签名(function signature)的区别?它们的各自的主要用处在哪?

7、谈谈下面的两组语句是否可行?如果不可行,说明理由!
   (1) #define cup int
       unsigned cup i;

   (2) typedef cup int;
       unsigned cup i;

8、指针与数组的不同点(无需说全,谈谈自己的看法即可),举例说明?

9、谈谈在编译链接时,从静态库和动态库提取符号方式有何区别?

10、下面有一段程序:
char    g_c[60];
int     g_i1;
int     g_i2    = 10;

int main() {
        int     l_i     = 89;
        return 0;
}
请尝试说明
(1) 变量g_c、g_i1、g_i2以及l_i各自在目标文件a.out中哪些段(section)中?(供选择的段text, data, bss, 某些变量可能不在任何一个段中)
(2) 变量g_c、g_i1、g_i2以及l_i各自在运行进程的地址空间的哪些段(section)中?(供选择的段text, data, bss, heap/stack某些变量可能不在任何一个段中)

11、为下面的函数设计参数类型:
(1) 将char a[5][6]传给函数function1,为使function1的形参能与实参a类型匹配,请给出function1的参数原型;
(2) 将char *b[6]传给函数function2,为使function2的形参能与实参b类型匹配,请给出function2的参数原型;
(3) 将char (*c)[7]传给函数function3,为使function3的形参能与实参c类型匹配,请给出function3的参数原型。

以上问题部分是开放的,如问题1、问题8。能很好的回答上面问题的选手,我想他/她对C语言的理解已经有一定深度了^_^。

[注1]
本文的’C语言’如无特指均为ANSI C。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats