网站首页  词典首页

请输入您要查询的论文:

 

标题 浅谈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下载服务。

 

Copyright © 2004-2023 puapp.net All Rights Reserved
更新时间:2025/3/13 17:33:46