2015-08-01 2 views
1

У меня есть небольшой вопрос. У меня есть этот кусок кода:c программирование printf формат ищет выражения

#include <stdio.h> 

int main(){ 
    printf("%d, %f, %d\n", 0.9, 10, 'C'); 
} 

И выход таков:

10, 0.900000, 67 

Но я бы ожидал:

0, 10.0, 67 

Но это похоже на поиски PRINTF для соответствующий тип в выражениях (int и float повернуты) Может ли кто-нибудь помочь мне с этой проблемой? Большое спасибо!

ответ

1

Единственное правильное ожидание становится 67 для печати символа с использованием формата %d Тендерный *. Другие две распечатки - это неопределенное поведение.

это выглядит как поиски PRINTF для соответствующего типа в выражениях

Это всего лишь совпадение. printf не имеет представления о типах фактических параметров, которые вы передаете. Он доверяет строке формата и последовательно интерпретирует данные. Вы можете рассказать, что происходит, поставляя разные цифры и наблюдая, как изменяется результат.

Числа, которые вы видите мусор - это double повторно интерпретируется как int, и int повторно интерпретируется как double. Более того, если размеры double и int различны, первые два параметра пересекают границы друг друга.

Для получения выходного сигнала, который вы хотите добавить оттенок в первых двух выражениях параметров:

printf("%d, %f, %d\n", (int)0.9, (double)10, 'C'); 

Обратите внимание, что вам не нужно, чтобы бросить последний параметр, поскольку char способствует к int в рамках список аргументов переменной переменной длины printf.

* Это приводит к правильному поведению только в том случае, если между другими параметрами и спецификаторами формата нет совпадений; ваша программа имеет UB даже для последнего параметра, который был бы правильным, если бы он использовался сам по себе.

2

Но я бы ожидал: ...

Когда строка формата не совпадают с типами аргументов для того, поведение не определено. Все может случиться. Вы ничего не можете ожидать (и я не понимаю, почему вы ожидаете 0. Возможно, вы ожидаете, что printf использует строку формата для преобразования аргументов между плавающей точкой и целым числом. Это просто так. Это вариационная функция, как вы могли бы написать ваш собственный, и тот факт, что строка формата кодирует типы возвращающих аргументов, не используется для их преобразования).

Современные соглашения об использовании используют регистры для первых нескольких аргументов и могут использовать регистры даже для вариативных функций.На x86-64 соглашение может быть, например, что первый аргумент с плавающей запятой переменной переменной всегда можно ожидать в регистре с плавающей запятой xmm0, тогда как целые аргументы передаются в регистры общего назначения %rdi, %rsi, %rdx, ... Это приводит к тому, что printf("%f %d", 1, 1.0) печатает аргумент с плавающей запятой, за которым следует целочисленный аргумент.

В качестве иллюстрации, вот короткая программа:

#include <stdio.h> 

int main(){ 
    printf("%d, %f, %d\n", 0.9, 10, 'C'); 

    printf("%d, %f, %d\n", 10, 0.9, 'C'); 
} 

Это как мой компилятор (Clang на Mac OS X 10.6) компилирует программу:

leaq <memory location of format string>, %rbx 

movq %rbx, %rdi 
movsd <memory location of 0.9 constant>, %xmm0 
movl $10, %esi 
movl $67, %edx 
movb $1, %al 
callq _printf 

movq %rbx, %rdi 
movl $10, %esi 
movsd <memory location of 0.9 constant>, %xmm0 
movl $67, %edx 
movb $1, %al 
callq _printf 

… 

Очевидно, что два вызова дают одинаковый результат. Но в случае одного он случайный и работает только для этой конкретной версии компилятора и ABI, тогда как другой относится к стандарту и должен работать где угодно.

Опять же, printf("%d %f", 0.9, 10) - это неопределенное поведение, и вы не должны его использовать ни при каких обстоятельствах.

1

Это неопределенное поведение, поэтому все может случиться.

%d требует int, но вы передаете double (не float), так printf занимает 4 байта значения double и интерпретирует его как int.

%f требует наличия double, но вы проходите мимо int. Поэтому он берет 4 байта int и 4 байта из следующей памяти и интерпретирует его как double.

Вам повезло передать 16 байт, в то время как printf ожидает 16 байт, поэтому последнее значение является правильным.

+0

Этот ответ предполагает, что аргументы 'printf' передаются в стеке, что очень маловероятно для получения результатов, указанных в вопросе. –

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