标题 | 浅谈C语言中函数形参为地址类型的定义形式和类型自动转换 |
范文 | 田媛 摘要:当函数参数为地址类型时,可以有多种定义形式,因而读者易产生迷惑,通过对各种实例的分析和证明,介绍了按各种形式定义的参数的本质;同时研究了当地址类型实参和形参类型不同时的自动转换。 关键词:地址;形式参数;实际参数;类型转换 中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2017)29-0118-02 当函数形式参数(简称形参)类型为普通类型的数值类型时,参数的定义形式以及不同当形参和实参类型不符时发生的自动转换,各类教科书已进行了详尽讲解。而当函数形参类型为地址类型时,它的定义形式多种多样,而且当地址类型实参和形参发生类型不匹配时的数据转换,还很少有资料专门研究总结。 本文针对这些问题,通过举合适的例做了证明和研究,对各种地址类型的形参的形式及本质做了总结,同时研究了地址类型数据的自动转换。 1 函数形参为一级指针 当函数形参类型为一级指针(即普通变量的地址)时,它的形式通常有一级指针和普通一维数组两种形式。 void fun1(char *a) //此处等价于(char a[ ]),数组的维数无需指出 { int i; for(i=0;i<=5;i++) { putchar(*(a+i)); //此处等价于putchar(a[i]); } } main( ) { char b[]="a1b2c3"; char *p; p=b; // 等价于 p=&b[0]; fun1(b); // 等价于fun1(p); } 此处形参的两种定义形式char *a与 char a[ ]是等价的,形参a的本质就是一个一级指针(指向普通变量的指针)。当我们需要对数组变量操作时,形参通常写成数组的形式,更加便于读者的理解,而当对普通变量操作时,形参通常写成一级指针形式。程序执行结果如图1。 2 函数形参为数组指针 当函数形参类型为数组指针(指向一维数组的指针)时,其形式通常为数组指针(很多资料中也称为行指针)和二维数组。 void fun1(char (*a)[4]) //此处等价于(char a[ ][4]),数组的第一维无需指出 //若参数说明为char a[ ][ ],则编译就会发生错误,二维数组必须指定列维数 { int i; for(i=0;i<3;i++) puts(*(a+i)); //此处等价于 puts(a[i]) } main() { char b[3][4]={"abc","xyz","aaa"}; char (*p)[4]; p=b; // 此处等价于 p=&b[0]; fun1(p); //fun1(b);运行结果都一样 } 程序运行结果为: 了解了数组指针形式的本质后,我们可以把数组指针扩展到多维数组,比如对于三维数组形式,void fun( char p[ ][4][3])和void fun( char (*p)[4][3])是等价的。 3 函数形参为二级指针 当函数形参类型为二级指针(指向一级指针的指针)时,它的形式通常有二级指针形式和指针数组形式。 void fun(char **a) //此处等价于(char *a[ ]) { int i; for(i=0;i<3;i++) puts(*(a+i)); //或puts(a[i]) } main( ) { char *p1[3],**p2; char b[3][7]={"abccde","deawff","gaaahi"}; p1[0]=b[0]; p1[1]=b[1]; p1[2]=b[2]; p2=&p1[0]; /p1[0]存放的是地址 ,&p1[0]则是p1[0]的地址,也即指针的指针 fun(p2);//等价于fun(p1) } 程序运行结果为: 对于此处指针形式的研究,可以参照在1中分析的 char *a与char a[ ]作为参数形式时本质是一致的,那么char **a与char *a[ ]也是一致的,我们已经做实验验证。 在实际编程中,读者可依据自己的喜好选择参数的定义形式,通常情况下,指针数组的形式更加易于理解,但当我们探究清楚参数类型的本质后,就可以灵活自如使用。 在1、2和3中我们总结了有关形式参数类型为地址类型的各种定义形式,并且在实际调用中,实参的类型和形参都一致,那么当函数调用時,实参并不是和形参同样类型的地址数值时,又会发生什么样的情况? 4 指针类型数据的转换 有关数据类型的自动转换,对于非地址类型的数据已有很多例题和资料讲述,在指针中只强调了强制类型转换,而地址类型的自动转换却很少提及。 经常在各类计算机考试中或教科书中有这样的题目: Char str[10];下列调用正确的是: A)gets(str) B)gets(&str) C)gets(str[0]) 给出的正确答案往往是A,其实B也是正确的。 在函数调用及赋值中,地址类型的数据间也会发生自动转换。 系统函数puts的函数原型是int puts(const char *s),我们利用1中分析,此处等价于int puts(const char s[ ]),显然,参数s要求传递的是一个列地址,而在4中所举的例子,实参str、str+1、str+2是行地址,但我们可以看到程序运行的结果依然正确。这里就是因为实参和形参不一致时,当它们的类型是地址类型也会发生自动转换。 main() { int (*ptr1)[2]; //此处ptr1是一个数组指针,指向长度为2的数组 int a[6]={0,1,2,3,4,5}; int *ptr=&a; printf("(1)%d,%d \n",*(ptr),*(ptr+2)); /*此处的ptr指针应该指向一个普通的int变量,所以应该存放a数组成员的地址,而此处&a并不代表a[0]的地址,它是一个行地址,在编译时,系统会有警告出现。但系统会进行数据的自动转换,将这个行地址转换成一个列地址,因而 printf("%d,%d",*(ptr),*(ptr+2))会输出a[0]和a[2]的值。*/ ptr1=a+1; /*常规的赋值,ptr1应该存放一个行地址,而此处a+1显然是一个列地址,是成员a[1]的地址。但此处系统会进行自动转换,将列地址转换为了行地址。*/ printf("(2)%d,%d \n",(*ptr)[0],(*ptr)[1]); //输出a[1],a[2]的值 printf("(3)%d,%d \n ",(*(ptr+1))[0], (*(ptr+1))[1]);//输出a[3],a[4]的值 } 地址类型的自动数据转换,通常发生在行地址和列地址之间。弄懂了这种赋值的自动转换,那么在函数调用中出现了类型不一致时也是如此。如下例: main() { char str[3][4]={"abc","123","x8y"}; puts(str+1); // 系統自动转化为puts(str[1]),输出串123 } 5 结束语 本文通过实例说明如下两点: 1) 总结了函数形参类型为地址类型数据时的各种不同定义形式及本质。 2) 当地址类型数据之间进行赋值和参数传递时,存在数据的自动转换。 参考文献: [1] 丁留海. C语言指针的底层原理[J]. 电子技术与软件工程, 2016(21):257-258. [2] 谭浩强. C语言程序设计[M]. 3版.北京: 清华大学出版社, 2005. [3] 蒋清明. C语言程序设计[M]. 北京: 人民邮电出版社, 2005. |
随便看 |
|
科学优质学术资源、百科知识分享平台,免费提供知识科普、生活经验分享、中外学术论文、各类范文、学术文献、教学资料、学术期刊、会议、报纸、杂志、工具书等各类资源检索、在线阅读和软件app下载服务。