Преобразования типов, прототипы и т.д. по списку...
От: maximum28  
Дата: 13.11.04 19:25
Оценка:
Намедни попался мне под руку следующий, на первый взгляд, ничем не примечательный код:

#include <stdio.h>
#include <math.h>

static int vklad=2000, rost=7;
int main()
{
float schet(void);
printf("schet=%.3f\n",schet()); 
}

float schet(void)
{
return vklad*powf((100.+rost)/100., 20.);
}


Все компилится. Работает. Выводит 7739.376.
Если вспомнить теорию Си, то вообще-то, определение прототипа функции не обязятельно.
Допустим. Удаляем определение прототипа ф-ции float schet(void) из проги.
Компилируем. Слушаем надоедливые ворчания компилятора.
Запускаем исплоняемый файл и получаем ... -1.999!!!
Почему?!
... << RSDN@Home 1.1.4 beta 3 rev. 207>>
Re: Преобразования типов, прототипы и т.д. по списку...
От: Phoenixx  
Дата: 13.11.04 20:06
Оценка:
Здравствуйте, maximum28, Вы писали:

M>Намедни попался мне под руку следующий, на первый взгляд, ничем не примечательный код:


M>
M>#include <stdio.h>
M>#include <math.h>

M>static int vklad=2000, rost=7;
M>int main()
M>{
M>float schet(void);
M>printf("schet=%.3f\n",schet()); 
M>}

M>float schet(void)
M>{
M>return vklad*powf((100.+rost)/100., 20.);
M>}
M>


M>Все компилится. Работает. Выводит 7739.376.

M>Если вспомнить теорию Си, то вообще-то, определение прототипа функции не обязятельно.
M>Допустим. Удаляем определение прототипа ф-ции float schet(void) из проги.
M>Компилируем. Слушаем надоедливые ворчания компилятора.
M>Запускаем исплоняемый файл и получаем ... -1.999!!!
M>Почему?!

Наверно фишка тут в том, что если функция описана после main, то ее нужно объявить
Re: Преобразования типов, прототипы и т.д. по списку...
От: migel  
Дата: 13.11.04 20:46
Оценка:
Здравствуйте, maximum28, Вы писали:

M>Все компилится. Работает. Выводит 7739.376.

M>Если вспомнить теорию Си, то вообще-то, определение прототипа функции не обязятельно.
Но ооочень рекомендуется
M>Допустим. Удаляем определение прототипа ф-ции float schet(void) из проги.
M>Компилируем. Слушаем надоедливые ворчания компилятора.
M>Запускаем исплоняемый файл и получаем ... -1.999!!!
M>Почему?!
Потому что функции в C по умолчанию считаются как возвращающие int ну а дальше тебе printf наколбасил
... << RSDN@Home 1.1.4 beta 3 rev. 190>>
Re[2]: Преобразования типов, прототипы и т.д. по списку...
От: Macr0s Россия  
Дата: 13.11.04 22:01
Оценка: -1
Здравствуйте, migel, Вы писали:

M>Здравствуйте, maximum28, Вы писали:


M>>Все компилится. Работает. Выводит 7739.376.

M>>Если вспомнить теорию Си, то вообще-то, определение прототипа функции не обязятельно.

Странный термин, не натыкался раньше на такой... определение прототипа — то есть объявление функции?
Если да, то я в шоке.
Как это не обязательно??? А как линкер узнает, что типа "есть такая функция..."?

M>Но ооочень рекомендуется


не просто рекомендуется, а строго обязательно.

Иначе, (если я неправильно понимаю термин "определение прототипа") просьб ане бить ногами в живот =)
Перед тем, как улучшиться, ситуация ухудшается. (из законов Мерфи)
Re[2]: Преобразования типов, прототипы и т.д. по списку...
От: Кодт Россия  
Дата: 14.11.04 13:12
Оценка:
Здравствуйте, migel, Вы писали:

M>Потому что функции в C по умолчанию считаются как возвращающие int ну а дальше тебе printf наколбасил


Точнее, там происходит следующее:

1) если прототип объявлен float schet(void)
printf("%f", schet()) здесь 4-байтовый float расширяется до 8-байтового double
"%f" печатает double в фиксированном формате

2) если не объявлен
прототип измышляется как int schet(void)
то, что sizeof(int)==sizeof(float), это нам крупно повезло сейчас...
но чуть позже — крупно не везёт
printf("%f", schet()) здесь 4-байтовый int (содержащий, на самом деле, float) оставляется как есть
"%f" печатает double, у которого 4 байта из того самого int'а, а ещё 4 байта — мусор на стеке.

3) наконец, если извратиться и написать
printf("%f", (double)schet())
то мы возьмём битовое представление float'а и это здоровенное целое число переведём в double.
Перекуём баги на фичи!
Re: Преобразования типов, прототипы и т.д. по списку...
От: Павел Кузнецов  
Дата: 15.11.04 02:19
Оценка:
maximum28,

> Если вспомнить теорию Си, то вообще-то, определение прототипа функции не обязятельно.


Но начиная с 99-го, по крайней мере, объявление функции должно быть в области видимости в точке вызова. Это означает, что компиляция приведенного примера с удалением предварительного объявления функции schet() компилятором "нового" C должна завершаться выдачей сообщения об ошибке. Хотя прототип функции, действительно, может быть опущен (с точки зрения C это означает возможность не указывать список параметров в объявлении функции).
Posted via RSDN NNTP Server 1.9 gamma
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[3]: Преобразования типов, прототипы и т.д. по списку...
От: vdimas Россия  
Дата: 15.11.04 11:39
Оценка:
Здравствуйте, Кодт, Вы писали:

К>3) наконец, если извратиться и написать

К>printf("%f", (double)schet())
К>то мы возьмём битовое представление float'а и это здоровенное целое число переведём в double.

без объявления прототипа компилятор сгенерирует перевод int в double, что так же не спасет
Re[4]: Преобразования типов, прототипы и т.д. по списку...
От: Кодт Россия  
Дата: 15.11.04 11:58
Оценка:
Здравствуйте, vdimas, Вы писали:

V>без объявления прототипа компилятор сгенерирует перевод int в double, что так же не спасет


Ничего он не сгенерирует.

printf это функция с эллипсом (...), а компилятор придерживается такого правила:
— целочисленные (в т.ч. bool и enum) расширяет до ближайшего подходящего из {int, long, __int64}
— плавающие — до double
— указатели — как есть, т.е. void*
— структуры — по идее, это ill-formed, хотя пёс его знает (под рукой Стандарта нет). VC пихает их по значению, выравнивая на 4.

Если функция без прототипа, то её результат трактуется как int. Соответственно, на стеке окажутся 4 байта (если 32-битная платформа).
Перекуём баги на фичи!
Re[5]: Преобразования типов, прототипы и т.д. по списку...
От: Кодт Россия  
Дата: 15.11.04 12:00
Оценка:
V>>без объявления прототипа компилятор сгенерирует перевод int в double, что так же не спасет

К>Ничего он не сгенерирует.


Извини, не понял суть вопроса.
Конечно же, он переведёт int (а не упакованный в нём float) в double. О том и речь.
Перекуём баги на фичи!
Re[3]: Преобразования типов, прототипы и т.д. по списку...
От: LaptevVV Россия  
Дата: 15.11.04 17:05
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, migel, Вы писали:


M>>Потому что функции в C по умолчанию считаются как возвращающие int ну а дальше тебе printf наколбасил


К>Точнее, там происходит следующее:


К>1) если прототип объявлен float schet(void)

К>printf("%f", schet()) здесь 4-байтовый float расширяется до 8-байтового double
К>"%f" печатает double в фиксированном формате
Неправда ваша! Для вывода double надо буковку "лы"-латинскую прописывать.

К>2) если не объявлен

К>прототип измышляется как int schet(void)
К>то, что sizeof(int)==sizeof(float), это нам крупно повезло сейчас...
К>но чуть позже — крупно не везёт
К>printf("%f", schet()) здесь 4-байтовый int (содержащий, на самом деле, float) оставляется как есть
К>"%f" печатает double, у которого 4 байта из того самого int'а, а ещё 4 байта — мусор на стеке.
int выводится как float — полная фигня выводится!
К>3) наконец, если извратиться и написать
К>printf("%f", (double)schet())
К>то мы возьмём битовое представление float'а и это здоровенное целое число переведём в double.
Не — 'f' — это float.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[4]: Преобразования типов, прототипы и т.д. по списку...
От: Кодт Россия  
Дата: 15.11.04 17:28
Оценка:
Здравствуйте, LaptevVV, Вы писали:

К>>"%f" печатает double в фиксированном формате

LVV>Неправда ваша! Для вывода double надо буковку "лы"-латинскую прописывать.
LVV>Не — 'f' — это float.

Увы, увы. %f — это именно double, а %Lf — long double.
Легко убедиться, что ... принимает вещественные числа именно как double.
#include <stdarg.h>
#include <stdio.h>

void foo(int b, ...)
{
  double x,y,z;
  va_list va;
  va_start(va,b);
  x = va_arg(va,double);
  y = va_arg(va,float);
  z = va_arg(va,double);
  printf("%f\n%f\n%f\n", x, y, z);
}

main()
{
  foo(1, 12.34, (float)12.34, 12.34);
  /* результат выглядит так:
   *
   * 12.340000
   * 0.000000
   * какой-то немыслимый крендильон
   *
   * это означает, что, начиная со второго параметра, мы считали из стека не то, что записали
   * но мы же писали float? ан нет, мы записали double
   */
}


Впрочем, не могу сказать — прописано ли это Стандартом или 32-битными компиляторами. Во всяком случае, здравому смыслу не противоречит.
В том, что это не MS-specific — уверен.

Про здравый смысл:
1) Дело в том, что вещественные литералы вида 123.45, 123.45e67 — имеют в С/С++ тип double. Чтобы написать float литерал, надо справа дописать букву F: 123.45F. А зачем нам лишняя работа?
2) Расширение целых чисел до int, а вещественных — до double — позволяет наименее геморройно передавать произвольные числа через эллипс.
Перекуём баги на фичи!
Re[5]: Преобразования типов, прототипы и т.д. по списку...
От: LaptevVV Россия  
Дата: 15.11.04 17:35
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, LaptevVV, Вы писали:


К>>>"%f" печатает double в фиксированном формате

LVV>>Неправда ваша! Для вывода double надо буковку "лы"-латинскую прописывать.
LVV>>Не — 'f' — это float.
К>Увы, увы. %f — это именно double, а %Lf — long double.
Это большая. А там еще маленькая есть — я все это буквально вчера проверял, поэтому все еще помню

К>Легко убедиться, что ... принимает вещественные числа именно как double.

К>1) Дело в том, что вещественные литералы вида 123.45, 123.45e67 — имеют в С/С++ тип double. Чтобы написать float литерал, надо справа дописать букву F: 123.45F. А зачем нам лишняя работа?
Ну, это по умолчанию — это да. Но там же явный возврат float из функции.
К>2) Расширение целых чисел до int, а вещественных — до double — позволяет наименее геморройно передавать произвольные числа через эллипс.
Ну, это тож понятно.
Но посмотри всеж printf — вывод double — это lf (не Lf)
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[6]: Преобразования типов, прототипы и т.д. по списку...
От: achp  
Дата: 15.11.04 18:05
Оценка:
Здравствуйте, LaptevVV, Вы писали:

К>>Увы, увы. %f — это именно double, а %Lf — long double.

LVV>Это большая. А там еще маленькая есть — я все это буквально вчера проверял, поэтому все еще помню

Нет. Стандарт Си не определяет применение l совместно с f.
Я кончил, джентльмены, мне остается только поблагодарить вас за внимание.
Re[3]: Преобразования типов, прототипы и т.д. по списку...
От: achp  
Дата: 15.11.04 18:11
Оценка: 23 (2)
Здравствуйте, Кодт, Вы писали:

К>2) если не объявлен

К>прототип измышляется как int schet(void)
К>то, что sizeof(int)==sizeof(float), это нам крупно повезло сейчас...
К>но чуть позже — крупно не везёт
К>printf("%f", schet()) здесь 4-байтовый int (содержащий, на самом деле, float) оставляется как есть
К>"%f" печатает double, у которого 4 байта из того самого int'а, а ещё 4 байта — мусор на стеке.

Кстати, еще не факт, что в данном случае произойдет переинтерпретация float в int. Во многих архитектурах для возвращаемых из функций скаляров используются регистры, а сами регистры могут быть разделены для целых чисел и для вещественных. Соответственно, вызываемая функция, зная, что ей полагается возвратить float или double, оставляет результат в "вещественном" регистре (например, ST(0)), а вызывающая, не зная объявления, предполагает, что тип возврата — int, и забирает результат из "целого" регистра (например, AX), где лежит "мусор".
Я кончил, джентльмены, мне остается только поблагодарить вас за внимание.
Re[5]: Преобразования типов, прототипы и т.д. по списку...
От: achp  
Дата: 15.11.04 18:25
Оценка: 20 (1)
Здравствуйте, Кодт, Вы писали:

К>Если функция без прототипа, то её результат трактуется как int.


Здесь есть терминологическая путаница. В Си (до стандарта 1999 года) можно было использовать необъявленную функцию, и тогда компилятор предполагал, что она не имеет прототипа и возвращает результат типа int. Прототип — это перечень формальных параметров функции. Функция может быть объявлена, как не имеющая прототипа, но при этом иметь тип возвращаемого значения, отличный от int.

// Функции, объявленные без прототипа:

int f();
float g();
void* h();

// Функции, объявленные с прототипом:

int i(void);
float j(int, double);
double* k(void const*);
Я кончил, джентльмены, мне остается только поблагодарить вас за внимание.
Re[6]: Преобразования типов, прототипы и т.д. по списку...
От: Кодт Россия  
Дата: 15.11.04 18:35
Оценка:
Здравствуйте, LaptevVV, Вы писали:

К>>Увы, увы. %f — это именно double, а %Lf — long double.

LVV>Это большая. А там еще маленькая есть — я все это буквально вчера проверял, поэтому все еще помню

На каком компиляторе? Для 16-битных приложений, вполне возможно, расширять до double является роскошью, поэтому там могут быть отступления от Стандарта.

LVV>Но посмотри всеж printf — вывод double — это lf (не Lf)


Может быть, long double ?
Перекуём баги на фичи!
Re[2]: Преобразования типов, прототипы и т.д. по списку...
От: maximum28  
Дата: 20.11.04 11:51
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>maximum28,


>> Если вспомнить теорию Си, то вообще-то, определение прототипа функции не обязятельно.


ПК>Но начиная с 99-го, по крайней мере, объявление функции должно быть в области видимости в точке вызова. Это означает, что компиляция приведенного примера с удалением предварительного объявления функции schet() компилятором "нового" C должна завершаться выдачей сообщения об ошибке. Хотя прототип функции, действительно, может быть опущен (с точки зрения C это означает возможность не указывать список параметров в объявлении функции).


Компилятор GCC одной из последних версий
Re[3]: Преобразования типов, прототипы и т.д. по списку...
От: Alexey_VL  
Дата: 21.11.04 09:51
Оценка:
Если говорить о стандартах, то еще надо смотреть как компилируется файл — как С или как С++. Потому как С++ включает в себя С89, а С99 это уже отдельная ветвь. По крайней мере так года три назад было.
А насчет модификаторов, из Шилдта (который заявлен как один из членов ANSI/ISO принимавших стандарт):
scanf : %f == float, %Lf == long double, %lf == double
printf: %f == double, %Lf == long double, %lf == не указано (сказано, что "l" применяется для d,i,o,u,x,n,c,s) но видимо либо double, либо long double
Мафиозная диктатура это нестабильность. Если не мафиозная диктатура, то Конституция и демократия.
Re[4]: Преобразования типов, прототипы и т.д. по списку...
От: Кодт Россия  
Дата: 22.11.04 13:43
Оценка:
Здравствуйте, Alexey_VL, Вы писали:

A_V>scanf : %f == float, %Lf == long double, %lf == double


И это закономерно: scanf принимает в ... указатели на переменные. Поэтому есть возможность и потребность различать float и double.
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.