2016-07-18 2 views
1

У меня есть вопрос о спецификаторе преобразования в С.О спецификаторе преобразования в C

В 5-м предложения, если я использую %lf или %Lf вместо %f, ошибки не возникнет. Но почему ошибка возникает, если я использую %f?

#include <stdio.h> 

int main(void) 
{ 
    long double num; 
    printf("value: "); 
    scanf("%f",&num); // If I use %lf or %Lf instead of %f, no error occurs. 
    printf("value: %f \n",num); 
} 
+0

Итак, вы спрашиваете, почему вы получаете предупреждение о компиляторе, если используете неправильный спецификатор? Пожалуйста, откиньтесь назад и снова прочитайте свой вопрос. Затем получите C-книгу и начните учиться с самой первой страницы. – Olaf

+1

@Olaf Это на самом деле запутанная тема для изучения, потому что '% f' означает разные вещи в' printf' vs 'scanf'. – dasblinkenlight

+0

@dasblinkenlight: И в моих собственных функциях это может означать нечто большее. Во всяком случае, речь идет о 'long double', где нет разницы. 'printf' acception' double' для '% f' является просто наследием и просто потому, что вы не можете передать параметр' float' в переменную функцию. Имо это прямо. Просто используйте '% f' для аргументов' float', '% lf' для' double' в обеих функциях ('logn double' уже ясно). Мне это не очень смущает. – Olaf

ответ

6

%f предназначается, чтобы быть использованы для чтения float с, не double с или long double с.

%lf предназначен для чтения double.

%Lf предназначен для считывания long double.

Если ваша программа работает с %lf, когда тип переменной long double, это только совпадение. Он работает, вероятно, потому, что sizeof(double) такой же, как sizeof(long double) на вашей платформе. Теоретически это неопределенное поведение.

+0

@R Sahu Спасибо! – Jin

+0

@jwqwerty, добро пожаловать. Счастливое программирование! –

0

Глядя на странице человека для printf(3) в системе FreeBSD (которая является POSIX, кстати), я получаю следующее:

Следующая Модификатор длина действительна для a, A, e , E, f, F, g, или G преобразование:

 Modifier a, A, e, E, f, F, g, G 
    l (ell)  double (ignored, same behavior as without it) 
    L   long double 

Я использовал преобразования с 32-битным типом данных с плавающей запятой. Но проблема здесь в том, что причина в том, что разные форматы float имеют разные размеры, а функция printf должна знать, какая из них есть, поэтому она может правильно выполнить это преобразование. Использование %Lf в поплавке может привести к ошибке сегментации, потому что преобразование осуществляет доступ к данным за пределами переменной, поэтому вы получаете неопределенное поведение.

  • поплавок: 32-битный
  • дважды: 64-битный
  • длинный двойной: 80-разрядное

Теперь для long double, фактический размер определяется платформой и реализации , 80 бит - 10 байтов, но это точно не подходит для 32-битного выравнивания без заполнения. Таким образом, большинство реализаций используют либо 96-битные, либо 128-битные (12 байт или 16 байт соответственно) для установки выравнивания.

Будьте осторожны, однако, потому что это может занять 128 бит, это не означает, что это __float128 (при использовании gcc или clang). Существует хотя бы одна платформа, где указание long double означает __float128 (SunOS, я думаю), но оно реализовано в программном обеспечении и работает медленно. Кроме того, некоторые компиляторы (Microsoft и Intel приходят на ум) = double, если вы не указали переключатель в командной строке.

Смежные вопросы