Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4022153
  • 博文数量: 272
  • 博客积分: 7846
  • 博客等级: 少将
  • 技术积分: 6476
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-25 16:27
文章分类

全部博文(272)

分类: C/C++

2014-05-08 09:39:10


   CU博主西方失败2913312169,在博文《声明数组参数》中抛出了C语言指针的函数参数传递问题,原文链接如下:
    http://blog.chinaunix.net/uid-29455636-id-4214932.html

   这是中国学生的老问题了,怪我们老师没教好,所以现在特地详细解释一下。首先要说明几个基本原则:
   1、数组名是地址常量,也即指针常量。但通常省略了一个限定范围,即函数体内。函数体内,如main或自定义函数等体内定义的数组名都是地址常量。不能用++或--这类的自增或自减运算符,当然也不能用a+=1,毕竟常量不能作为左值。这是理解指针的第一个前提。
   2、*与&为互逆的两个运算符,分为是取值和取址。对于int a = 0;那么&a就是a变量的地址,而*(&a)就是按变量类型int取出a变量存放值的空间中的值。
   3、C语言里指针有两级,一级指针(指向地址的指针)和二级指针(指向地址指针的指针),都是指向地址但层次不同,一级和二级指针可以指向同一地址,但方向不同。例如:a[3][3]。a是二级指针,a+1向下跳一行,(*a)是一级指针,(*a)+1向后跳一列。对二级指针取值,就把指针指向也即移动单位进行了变换(实质上是完成了一种特殊类型的转换),*a就等于&(a[0][0])。这是理解指针的第二个前提。需要注意的是,所谓的两级指针并非链表那种你连我,我连他,千万别误以为链表中的第一个是一级指针,第二个是二级指针,第三个指针是三级指针,其实链表里的链接指向指针都属于一级指针。
    4、好了开始说关键之处吧,在函数定义时,参数的作用是接收其它传入值,不管这个值是否为指针。当然了,形参可以是变量,或缺省常量,如果是缺省常量,只能定义在参数列表的后面。你这里的定义并非缺省常量,所以该形参是一个变量。这就是为什么说在作为参数时, 数组和指针是等价的,因为发生拷贝出现了值传递,const直接转为了non-const。于是,可以认为函数形参定义的括号内并非严格属于函数体内。

   我专门写了一个小程序,如下:



   请你思考下并自测一下,为什么是下面这个答案?


   呵呵,虽然这个例子有点难度,但希望本贴对你真正有用。有什么想法,咱们一起讨论。如有不正确之处,也请指正。
阅读(6035) | 评论(4) | 转发(3) |
给主人留下些什么吧!~~

五岳之巅2014-05-13 08:47:07

CUTianrui007:数组作为函数参数时,转化为指针,是有其原因的,我们都知道,函数参数是要入栈的,如果是数据直接传送的话,那么数组如果很大的话,那么函数孤栈显然是不够用,不同的数组容量其所占用栈空间也不同,这就充满不确定性。
所以,C语言简单起见,统一将数组当成指针来用。

这个反映到C++中,那就是引用,当使用一个对象作为函数参数时,要调用拷贝构造函数,所以引入引用,那就不用再拷贝了。

没错,就是这样。C语言也是人设计出来的,可行性也非常关键。谢谢补充。

回复 | 举报

CUTianrui0072014-05-12 21:35:52

数组作为函数参数时,转化为指针,是有其原因的,我们都知道,函数参数是要入栈的,如果是数据直接传送的话,那么数组如果很大的话,那么函数孤栈显然是不够用,不同的数组容量其所占用栈空间也不同,这就充满不确定性。
所以,C语言简单起见,统一将数组当成指针来用。

这个反映到C++中,那就是引用,当使用一个对象作为函数参数时,要调用拷贝构造函数,所以引入引用,那就不用再拷贝了。

五岳之巅2014-05-12 13:10:47

dididimeme01:其实,这个就是(*s)++和*s++,按你的例子研究了一下,*s++是列移动,(*s)++行移动.不过深入研究了思考了一下,觉得更多的我们用的是char s[3][20]这样的静态数组比较多,如果需要指针的话,还需要连接上才能使用,而且对指针的超界问题需要更多思考,一不留神就会出现越界问题。值得一说的是,你的foo函数有边界溢出问题,即第二次*s++的时候越界了,因此你定义的是一个2个指针。

感谢你认真的态度,赞。工具都有两面性,电脑可以帮人工作,也可以误人子弟。指针也是一样,溢出往往会形成隐含的Bug,但其更加灵活。但不论那种方式,能解决问题就好。

回复 | 举报

dididimeme012014-05-12 09:55:43

其实,这个就是(*s)++和*s++,按你的例子研究了一下,*s++是列移动,(*s)++行移动.不过深入研究了思考了一下,觉得更多的我们用的是char s[3][20]这样的静态数组比较多,如果需要指针的话,还需要连接上才能使用,而且对指针的超界问题需要更多思考,一不留神就会出现越界问题。值得一说的是,你的foo函数有边界溢出问题,即第二次*s++的时候越界了,因此你定义的是一个2个指针。