Странные результаты из-за продвижения аргумента при отсутствии объявления прототипа в C - PullRequest
0 голосов
/ 18 сентября 2011

Я столкнулся с проблемой, которую можно суммировать следующим образом:

#include <stdio.h>
int main()
{
        float f=23.45;
        printf("main: %f\n", f);
        t1(f);
/*    the result would be 
      main:23.450001 
      t1:2.000000    */          
}
void t1(float f)
{
        printf("t1: %f\n", f);
}

Теперь я знаю, что странное поведение связано с отсутствием объявления прототипа и, следовательно, приводятся аргументы (float-> double?), я до сих пор не могу понять, почему результат равен 2,000000, поэтому кто-нибудь может дать более подробное объяснение?Я использую ubuntu10.04 gcc4.4.3

Ответы [ 3 ]

6 голосов
/ 18 сентября 2011

Поведение, которое вы наблюдаете, характерно для передачи параметров на основе стека.Для людей, которые по умолчанию компилируют 64-битный код x86 и не могут его воспроизвести, вы можете попробовать использовать "gcc -m32" вместо простого "gcc".

При передаче параметров на основе стека t1()читать 32 бита из стека, и эти 32 бита образуют значение с плавающей запятой 2.0.На сайте вызова, поскольку у t1 не было прототипа, аргумент f был преобразован в double, и это было double, которое было записано в стек (C99 6.5.2.2:6 «Если выражениеэто означает, что вызываемая функция имеет тип, который не включает в себя прототип, целочисленные преобразования выполняются для каждого аргумента, а аргументы, имеющие тип float, повышаются до двойного. Это называется продвижением аргументов по умолчанию »).

У t1 нет оснований для восстановления нужного значения из стека, поскольку он не читает его должным образом с тем же типом и шириной, как было записано.

1 голос
/ 18 сентября 2011

Поскольку компилятор не знает, что t1 принимает число с плавающей запятой, он увеличивает число с плавающей запятой до двойного. На вашей платформе t1 интерпретирует первые четыре байта этого двойного числа как число с плавающей запятой.

2.0 представляется как 1 x 2 ^ 0, что в двоичном виде - три нулевых байта, за которыми следует 64 (на x86-64). Как это бывает, 23,45 как двойной имеет те же первые четыре байта, 0, 0, 0, 64 (за которыми следуют 51, 115, 55, 64). Так что если вы возьмете двойное 23,45 и интерпретируете первые четыре байта его как число с плавающей запятой, вы получите 2,0.

0 голосов
/ 18 сентября 2011

Помните, что отсутствующий прототип подразумевает, что функция будет позже объявлена ​​с аргументами int, и что произойдут соответствующие преобразования.Итак, я предполагаю, что если вы замените float на int в объявлении t1, вы получите результат 23.

...