远离旧式风格的 C 函数

例一

test.c中的代码

#include <stdio.h>
#include <stdlib.h>

int* test()
{
    int *new = malloc(sizeof(int));
    printf("test.c输出:%p\n",new);
    return new;
}

main.c中的代码

#include <stdio.h>

int main(void)
{    
    int*p = test(); 
    printf("main.c输出:%p\n",p);
}

输出结果

test.c输出:0x1005042a0
main.c输出:0x5042a0

为什么同一个地址,子函数和主函数里输出结果会不同?

例二

a.c中的代码

#include <stdio.h> 

int main(int argc,char * argv[]){
    fadd(3.0f,3.0f); 
    return 0;
}

b.c中的代码

#include <stdio.h> 

void fadd(float a,float b){
    float c;
    c = a+b;
    printf("%f\n",c); 
}

结果是6吗?上机运行后的结果竟然是2.125,这一定会让我们大吃一惊。

新旧风格

在最古老的 C 语言中,函数的参数列表并不是函数接口的一部分,C 编译器对旧式风格的函数甚至连实参的个数都不进行检查。下面是旧式风格的函数:

int f1(){
    return 3;
}

int f2(a,b,c)
    int a,b;
    char c;
{ 
    return a+b; 
}

之后随着C语言的发展,对函数的参数进行了更严格的类型检查,函数的参数列表也就成了函数接口的一部分。下面的属于新式风格的函数

int f1(void){
    return 3;
}

int f2(int a, int b, char c)
{ 
    return a+b; 
}

问题所在

在 C 语言中,如果有一个函数未经声明就直接使用,则C编译器会把这个函数当作旧式风格的函数,并且会使用缺省类型作为函数的类型。比如例一中的test()会缺省类型为:

int test();

函数缺省类型为int,若你的函数返回值不是int,就会出现一些莫名其妙的错误。

例二与返回值无关,为什么也错了?原来C编译器对旧式风格的函数会进行一个被称为“实参提升”的动作。凡是低于 int 型的其他整型,包括 char 和 short 都会被提升为 int,而单精度 float 则会被提升为 double;其他类型保持不变。

于是,C 编译器面对未声明就使用的函数调用“fadd(3.0f,3.0f);”时,默默地进行了实参提升的操作。真正执行的函数调用是“fadd(3.0,3.0);”,压入栈的是两个 double 类型的浮点数 3.0,共占了 16 字节。在小端机器上,浮点数 3.0 的存放在低地址的 4 字节中存放 0x00000000,在高地址的 4 字节中存放 0x40080000。但在文件 b.c 中,函数 fadd 是新式风格的函数,其接口为“void fadd(float a,float b)”,按照函数声明,仍然把形参 a 和 b 当作 float 来处理,依照 C 调用约定,参数从右向左入栈,所以形参 a 对应的是 0x00000000, 形参 b 对应的是 0x40080000。

总之,远离旧式风格的 C 函数,同时记住,函数要先声明再使用,否则我们就不知不觉地在使用旧式风格的函数声明。

posted @ 2020-06-20 21:18:40
评论加载中...
发表评论