Як функцыя Printf працуе ў C?

Я сустрэўся з праблемай, калі я праверыць функцыю Printf:

Спачатку я пішу такі код:

int main(void)
{
    char a = 'a';
    printf("a = %f\n", a);
    return 0;
}

выхад

enter image description here

А потым я пішу код:

int main(void)
{
    float b = 'a';
    printf("b = %f\n", b);
    return 0;
}

выхад

enter image description here

А потым я пішу код:

int main(void)
{
    char a = 'a';
    float b = 'a';
    printf("b = %f\n", b);
    printf("a = %f\n", a);
    return 0;
}

выхад

enter image description here

Таму я заблытаўся, чаму ў першай праграме а = 0.000000 і ў трэцяй праграме а = 97.000000 <? ш> Як працуе функцыя Е() праца?
Як сімвал % е , % d працы?

5
RTFM, напрыклад, прачытаць Е (3) і/або Printf
дададзена аўтар Basile Starynkevitch, крыніца
Вы заблыталіся Printf але флоат б = "а"; гэта добра для вас? ;)
дададзена аўтар sebastian, крыніца
@sebastian прабачце, я не ведаю, што вы маеце на ўвазе.
дададзена аўтар Mr.CodeMonkey, крыніца

6 адказы

Update: after doing some more research on this, it seems that the differences between the float and int memory representations are not the ones responsible for the behaviour of the three programs.

Я паглядзеў на код аб'екта для трэцяй праграмы, і я знайшоў прычыну дзіўных паводзін: аргументы з якая плавае кропкай перадаюцца ў іншы рэестр/стэк, чым цэлалікавых. І Printf заснаваны на тым, што і выглядае іх у іншых месцах, чым тыя, які выклікаецца з PRINTF (г.зн. Асноўны метад) змяшчае аргументы.

Вось адпаведны дэмантаж трэцяй праграмы (для архітэктуры x86_64):

0000000100000f18    leaq    0x71(%rip), %rdi        ## literal pool for: "b = %f\n"
0000000100000f1f    movsd   0x61(%rip), %xmm0      ## b variable gets sent to xmm0
0000000100000f27    movl    $0x0, -0x4(%rbp)
0000000100000f2e    movb    $0x61, -0x5(%rbp)      ## a variable gets placed on the callee stack
0000000100000f32    movsd   %xmm0, -0x10(%rbp)
0000000100000f37    movsd   -0x10(%rbp), %xmm0
0000000100000f3c    movb    $0x1, %al
0000000100000f3e    callq   0x100000f66             ## symbol stub for: _printf
0000000100000f43    leaq    0x4e(%rip), %rdi        ## literal pool for: "a = %f\n"
0000000100000f4a    movsbl  -0x5(%rbp), %esi
0000000100000f4e    movl    %eax, -0x14(%rbp)
0000000100000f51    movb    $0x0, %al
0000000100000f53    callq   0x100000f66             ## symbol stub for: _printf

І Printf належыць на гэта, мяркуецца, што выклікаецца размясціла % е аргументы на XMM0 / XMM1 /і г.д. рэгіструе і паводзіны трох праграм, як гэта:

  1. у першай праграме Printf шукае % е аргумент на XMM0 рэгістры, аднак, як мы ў пачатку праграмы, рэестр чысты і Асноўны размясціў а у еах , такім чынам, XMM0 мае нулявое значэнне, і гэта тое, што Printf друк
  2. У другой праграме Асноўны правільна размяшчае B у XMM0 і Printf бярэ яго адтуль, друк правільнае значэнне
  3. у трэцяй праграме ў сувязі з тым, што B друкуецца першым, то XMM0 Рэгістр будзе утрымліваць гэтае значэнне, і так як Printf Абыякава «т звязвацца з рэгістрам, калі яна называецца другі раз, калі ён здабывае зноў з XMM0 , якія засталіся некранутымі пасля першага Printf выкліку.

So it's all about caller/callee conventions on where integers and floats are being send by the caller and from where the callee tries to pick them up.


Original response: In the first program you are trying to print a float, but you pass an int (char is a smaller int). Due to the fact that ints and floats have different binary representations, the int 97 (corresponding to the character 'a') corresponds to a very small float: 1.36E-43, that gets printed as zero.

Here is the binary representation of 97 (the compiler expands any 1-byte char to a 4-byte argument when calling a function)
00000000 00000000 00000000 01100001

IEEE 754 is the standard format for binary representations of float/double numbers, you can play with an online converter here, and you can see how the same binary number has different values when its interpreted as an int or as a float.

6
дададзена
Ну, я думаю, што гэта адзін з тых нявызначанага паводзінаў вы атрымліваеце, калі вы не соответствуете фарматнай радку і спіс аргументаў, перададзеных PRINTF. Радуйцеся, што вы не мелі аварыі :)
дададзена аўтар Cristik, крыніца
Дзякуй за ваш адказ. Але як яна можа растлумачыць трэцюю праграму?
дададзена аўтар Mr.CodeMonkey, крыніца
Я думаю, што гэта выхадны буфер прыводзіць да выхаду ў трэцяй праграме. Таму што, калі я памяняўшы два PRINTF прапановы выхад выйшаў = 0,000000 б = 97.000000.
дададзена аўтар Mr.CodeMonkey, крыніца

<�Код>% е тут уяўляе сабой маркер для замены паплаўка.

Для таго, каб замяніць сімвал вам трэба % C .

Here is a list that tells you what is the appropriate replacement token for each type.

5
дададзена
@ Mr.CodeMonkey, што вы робіце гэта нявызначаны паводзіны, так <�я> што-небудзь з'яўляецца дапушчальным вынікам, аж да поўнага знішчэння сусвету. Іншымі словамі, не рабіце гэтага :-)
дададзена аўтар paxdiablo, крыніца
Я ведаю, што гэта. Але я хачу ведаць, калі я выкарыстоўваю% е замест% з, што будзе функцыя Printf рабіць? Як гэта прачытаць рэгістр?
дададзена аўтар Mr.CodeMonkey, крыніца

У адпаведнасці з апошнім стандартам «C» гэта нявызначаны паводзіны. Праверце 7.21.6.1 пт 9 з ад гр стандартнага праекта.

<�Р> Калі спецыфікацыя пераўтварэння з'яўляецца несапраўдным, паводзіны   undefined.282) Калі які-небудзь аргумент не правільны тып для   адпаведную спецыфікацыю пераўтварэнні, паводзіны не вызначана. </р>

Таму, калі ўсе кажуць, няпэўны паводзіны ўсё магчыма і паводзіны можа вар'іравацца ад аднаго кампілятара на іншы. «C» давайце вы парэзалі палец з сякерай, але гэта не мне, вы павінны гэта зрабіць.

1
дададзена

% F для Float  % З для сімвалаў

The 97 which you have got is the ASCII value for 'a'
1
дададзена

розніца паміж

printf("%f\n, 97.0);

і

printf("%c\n, 'a');

is that the printf function reads its parameters from the stack based on the %X you give, і interprets them (for display) as such.

For %c printf expects a char as parameter, so it will read a char (a byte, but often actually a int, it's implementation dependant) і displays it (it displays the less significant byte if an int is provided).

Для % е Printf чакае паплаўка (памер якіх у байтах SizeOf (флоат) , як правіла, 4 байта на GCC/працэсары Intel).

If you compile with gcc use the -Wall option that would give a warning when the %X format і the type of the parameter do not match.

0
дададзена
Чытайце выдатны адказ ад Cristik (вышэй я думаю), што тлумачыць, чаму вы атрымліваеце 0 у гэтым выпадку.
дададзена аўтар Ring Ø, крыніца
Я разумею, што вы сказалі. Але ён не можа растлумачыць сваю праграму. Калі я вызначаю як паўкокс а = "а"; і Е ( "а =% е \ п», а); Ці Printf счытваць дадзеныя 4 байта, пачынаючы з адрасу? Калі так, то выхад не можа быць 0, ці не так?
дададзена аўтар Mr.CodeMonkey, крыніца

%f is for float. You must use %c for characters.

Калі вы карыстаецеся

    printf("a = %c\n", a);

Вы атрымаеце характар.

Такім чынам, калі вы зменіце свой першы код

int main(void)
{
    char a = 'a';
    printf("a = %c\n", a);
    return 0;
}

Вы атрымаеце выхад у

a
0
дададзена
@ Mr.CodeMonkey, вы можаце праверыць выснову я атрымаў тут
дададзена аўтар Arun A S, крыніца
@ Mr.CodeMonkey, розніца ў выхадзе, верагодна, з-за таго, як кампілятар інтэрпрэтуе яго.
дададзена аўтар Arun A S, крыніца
Я ведаю. Але я хачу ведаць, у чым розніца паміж% е і% з?
дададзена аўтар Mr.CodeMonkey, крыніца
Ну чаму? Я выкарыстоўваю clodeblocks13.12. Мая АС windows7 64bit. Вы ведаеце, чаму наш выхад адрозніваецца?
дададзена аўтар Mr.CodeMonkey, крыніца
Я мяркую, што гэта залежыць ад дадзеных у неинициализированной памяці.
дададзена аўтар Mr.CodeMonkey, крыніца