#c
#c
Вопрос:
Вот мой код.
// Fibonacci function in C
#include <stdio.h>
unsigned long long int fib(int n);
unsigned long long int main()
{
unsigned long long int n,resu<
printf("Enter n for a_n:");
scanf("%d",amp;n);
result=fib(n);
printf("a_%d is %d",n,result);
return 0;
}
unsigned long long int fib(int n)
{
if(n==1)
{
return 1;
}
else if(n==2)
{
return 1;
}
else
{
return fib(n-1) fib(n-2);
}
}
Когда я ввожу 48, на выходе получается 512 559 680.
Но в python это 4,807,526,976.
Я думаю, что unsigned long достаточно долго, чтобы обозначить результат.
Комментарии:
1. Компилятор должен выдавать некоторые предупреждения об использовании
%d
вами формата при печатиunsigned long long int
.2.
scanf("%d", amp;n);
— вы понимаетеn
этоunsigned long long
, верно? это неподходящий спецификатор формата дляscanf
извлечения такой вещи из stdin. Вы также должны получать предупреждение оfib(n)
том, откуда вы передаете что-то, чего неint
ожидает функцияint
. Короткая версия: увеличьте свои предупреждения до педантичного уровня и рассмотрите их ВСЕ как ошибки, а затем исправьте их; не игнорируйте и не скрывайте их.3. Кстати,
fib(48) == 4,807,526,976
, а не 4,294,967,2954. @ForceBru Спасибо. Извините за опечатку. Я исправил его до правильного значения.
Ответ №1:
Результат правильный, но вы печатаете только его часть из-за %d
формата здесь:
printf("a_%d is %d",n,result);
^^ here
Правильный результат — 4807526976 или 0x11e8d0a40 в шестнадцатеричном формате. Вы получили 512559680 или 0x1e8d0a40 в шестнадцатеричном формате. Сравните их бок о бок:
correct: 0x011e8d0a40
you got: 0x001e8d0a40
То есть вы напечатали последние 4 байта значения, потому %d
что формат интерпретировал число как обычное целое число, ширина которого на вашем компьютере составляет 4 байта.
Чтобы исправить это, включите все предупреждения компилятора и посмотрите, какой формат он предлагает вместо %d
(вероятно %llu
).
Пользовательский ввод по своей сути динамичен, а строки формата (например "a_%d is %d"
) — это просто строки, поэтому статически типизированный язык (например, C или OCaml) не может определить, является ли какой-либо спецификатор формата (например %d
) неправильным — как указано в спецификаторе signed int
, но (статический!) Тип переменной, которую выпечать есть unsigned long long int
. В результате я думаю, что форматирование с использованием строк формата обречено с самого начала, потому что статическая проверка типов автоматически становится динамической, обеспечиваемой внутренним механизмом printf
, и вы теряете все гарантии статической проверки типов.
Похоже, что единственный способ решить эту проблему — вставить «хак» в компилятор, который будет проверять форматирование строк. Вот как ваш компилятор C со всеми включенными предупреждениями может обнаруживать ошибки:
test.c:8:16: warning: format specifies type 'int *' but the argument has type
'unsigned long long *' [-Wformat]
scanf("%d",amp;n);
~~ ^~
%lld
test.c:9:25: warning: format specifies type 'int' but the argument has type
'unsigned long long' [-Wformat]
printf("a_%d is %d",n,result);
~~ ^
%llu
test.c:9:27: warning: format specifies type 'int' but the argument has type
'unsigned long long' [-Wformat]
printf("a_%d is %d",n,result);
~~ ^~~~~~
%llu
Аналогично, в OCaml Printf.printf
имеет этот странный тип:
val printf : ('a, out_channel, unit) format -> 'a
… даже если в вашем коде оно вызывается со строкой формата:
Printf.printf "a_%d is %d" 4 (fib 4)
Аналогично, в Rust есть этот странный тип std::fmt::Arguments
, который представляет строку формата и не может быть создан во время выполнения. Итак, этот тип фактически используется для форматирования, но в коде вы пишете строки формата:
println!("a_{} = {}", 48, 0x11e8d0a40u64)
Комментарии:
1. мой компилятор
mingw64
. Как я могу взломать, чтобы получать предупреждения?2. @kile, «хак» уже есть. Вы должны просто включить все предупреждения с
-Wall
помощью опции или что-то в этом роде. На самом деле это не хак, это скорее особенность компилятора — я называю это так, потому что, насколько я понимаю, обычный C не должен даже улавливать такие ошибки в строках формата, потому что это просто строки, и их содержимое не проверяется по типу.