2017-03-02 239 views
-4

我已阅读Garbage value when passed float values to the function accepting integer parameters答案。我的问题更深入一点。我本来也可以问我有50多个声望点。我加入我的代码更多的澄清:将float传递给带int参数的函数(未事先声明)

#include <stdio.h> 
#include <string.h> 

void p2(unsigned int tmp) 
{ 
    printf("From p2: \n"); 
    printf("tmp = %d ,In hex tmp = %x\n", tmp, tmp); 
} 

int main() 
{ 
    float fvar = 45.65; 

    p1(fvar); 
    p2(fvar); 
    printf("From main:\n"); 
    printf("sizeof(int) = %lu, sizeof(float) = %lu\n", sizeof(int), 
      sizeof(float)); 
    unsigned int ui; 
    memcpy(&ui, &fvar, sizeof(fvar)); 
    printf("fvar = %x\n", ui); 
    return 0; 
} 

void p1(unsigned int tmp) 
{ 
    printf("From p1: \n"); 
    printf("tmp = %d ,In hex tmp = %x\n", tmp, tmp); 
} 

输出是:

From p1: 
tmp = 1 ,In hex tmp = 1 
From p2: 
tmp = 45 ,In hex tmp = 2d 
From main: 
sizeof(int) = 4, sizeof(float) = 4 
fvar = 4236999a8 

传递一个float值到与int参数事先(即P2)声明的函数给出正确的结果。如果对未事先声明的函数(即p1)进行相同尝试时出现错误值。我知道编译器不会为之前未声明的函数假设任何类型或参数的原因。这就是为什么在p2的情况下,float的值没有被标注为int

我的困惑是,在p2的情况下,float值究竟如何被复制到本地int变量tmp。

如果它是'一点一点复制'比读取这些位置至少应该产生一些东西(除了1)在十六进制(如果不是整数)。但是,输出结果并非如此。我知道浮动表示是不同的。

以及p2如何读寄存器/堆栈位置浮动未复制到?正如simonc在链接问题中所建议的那样?

我已经包括大小intfloat都和我的编译器是gcc,如果有帮助。

+7

这是未定义的行为。不要试图将其推断出来。根据定义,它没有定义发生了什么。 – bolov

+1

如果你想在调用者函数**之后定义一个函数,你必须在调用函数之前添加一个原型。这就是全部 – LPs

+1

为了详细说明@LP所说的,没有这些,你的代码不再是_valid_ C代码了。 –

回答

3

C编程语言本质上是一个单一的扫描语言 - 一个编译器不需要重新读取的代码,但它可以通过线线组装起来,只保留对如何标识符的信息被宣布。

C89标准具有隐式声明的概念。在没有声明的情况下,函数p1被隐式声明为int p1();即返回int的函数,并且采用经过默认参数提升的未指定参数。当你调用一个函数float作为参数时,float参数会被提升为double,这是默认参数促销所要求的。如果函数是int p1(double arg),那就好了;但预期的参数类型为unsigned int,并且返回值也不兼容(voidint)。这种不匹配会导致该程序有未定义的行为 - 推理当时发生的事情没有意义。但是,如果编译器不支持陈旧的隐式声明,那么很多旧的C程序将无法编译,因此您只需将所有这些警告视为错误即可。

请注意,如果你改变了返回值的p1int,你会得到的警告信息:

% gcc implicit.c 
implicit.c:14:5: warning: implicit declaration of function ‘p1’ [-Wimplicit-function-declaration] 
p1(fvar); 
^~ 

但在我的编译器观察到的行为将大致相同。

因此单纯警告的存在:函数隐式声明的“x”很可能在新编写的代码严重错误

是其使用前声明的功能,是区分与p2,那么编译器知道它期望的unsigned long作为参数,并返回void,因此它会知道生成正确的转换代码floatunsigned long为争论。


的C99和C11不允许隐函数声明在严格符合要求的程序 - 但他们也并不需要一个符合标准的编译要么拒绝它们。 C11表示:

标识符是以一次表达式,只要它已被宣布为指定的对象(在这种情况下,它是一个左值)或函数(在这种情况下,它是一个函数指示符)。

和一个脚注指出的是

因此,未声明的标识符是违反语法。

但是,它不需要编译器来拒绝它们。

+0

我明白了。这是未定义的行为。我喜欢你的解释。我有一个问题:为什么C标准允许它?不应该这种未定义的行为首先给出错误(即使没有使用 - 错误)?或者它在任何情况下都有用? – UserXYZ

+0

“但是,如果编译器不支持陈旧的隐式声明,那么很多旧的C程序将无法编译,因此您只需要将所有这些警告视为错误。” –

+0

隐式函数声明就是它在70年代完成的情况:D阅读有关隐式函数声明的更多信息[这里](http://stackoverflow.com/questions/9182763/implicit-function-declarations-in- C)。请注意,该问题具有双重未定义的行为,因为它正在重新定义也具有未定义行为的内置函数。 –

2

void p1(unsigned int tmp); 

implicitly declared

int p1(); 

由编译器。

尽管编译器不会引发错误,但应该将其视为一种,因为您可以在链接的文章中阅读它。

无论如何,这是未定义的行为,您不能指望可预测的输出。

+1

它会被暗示为'int p1()' –

0

在二进制级别,floatint根本不相像。

当尝试一个float复制到int,有一个隐式转换,这就是为什么当你调用一个函数,int的说法,但您提供float你得到它的整数部分,但在最后的测试,你去看看它真的看起来有多丑。这不是垃圾,如果你想以十六进制打印它,float在内存中看起来很像。详情请参阅IEEE 754

然而,p1()的问题在于,您尝试调用尚未声明的函数,因此它会自动声明为int p1()。即使您稍后将其定义为void p1(unsigned int tmp),它已被声明为int p1()(不接受任何参数),因此它不起作用(行为未定义)。我非常确定编译器尖叫着有关这个的警告和错误,这些错误并不意味着被忽略。

注意声明和定义函数有很大的区别。稍后定义一个函数是完全合法的,但是如果您希望它能够正常工作,则必须在尝试使用它之前声明它。

实施例:

// declare functions 
void p1(unsigned int tmp); 
void p2(unsigned int tmp); 

// use functions 
int main() 
{ 
    p1(1); 
    p2(1); 
} 

// define functions 
void p1(unsigned int tmp) 
{ 
    // do stuff 
} 
void p2(unsigned int tmp) 
{ 
    // do stuff 
}