Printf işlevi C dilinde nasıl çalışır?

Printf işlevini test ederken bir sorunla karşılaştım:

Önce böyle bir kod yazarım:

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

Çıktı

enter image description here

Ve sonra kod yazarım:

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

Çıktı

enter image description here

Ve sonra kod yazarım:

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

Çıktı

enter image description here

Bu yüzden neden ilk programda a = 0.000000 ve üçüncü programda a = 97.000000 ? < br> printf() işlevi nasıl çalışır?
% f , % d sembolü nasıl çalışır?

5
RTFM, örn. printf (3) ve/veya printf
katma yazar Basile Starynkevitch, kaynak
Printf hakkında kafanız karışık ama float b = 'a'; sizin için uygun mu? ;)
katma yazar sebastian, kaynak
@sebastian üzgünüm, ne demek istediğini anlamadım.
katma yazar Mr.CodeMonkey, kaynak

6 cevap

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.

Üçüncü programın nesne koduna baktım ve garip davranışın nedenini buldum: kayan nokta argümanları tamsayılardan başka bir kayıt defterine/yığına gönderilir. Ve printf buna güvenir ve onları printf (diğer bir deyişle main yönteminin) argümanları dışındaki yerlerden arar.

İşte üçüncü programın ilgili demontajı (x86_64 mimarisi için):

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

Ve printf buna güvenir, arayanın % f bağımsız değişkenlerini xmm0 / xmm1 /etc içine yerleştirdiğini varsayar. Kayıtlar ve üç programın davranışı şöyle:

  1. ilk programda printf , xmm0 registerında % f argümanını arar, ancak programın başındayken kayıt temiz ve main , a 'u eax ' a yerleştirdi, bu nedenle xmm0 değeri sıfırdır ve bu printf budur yazdırır
  2. ikinci programda main doğru şekilde b öğesini xmm0 içine yerleştirir ve printf bunu oradan alır, yazdırır doğru değer
  3. üçüncü programda, önce b 'nın basılması nedeniyle, xmm0 kaydı bu değeri tutacak ve printf yapmadığından İlk printf çağrısından sonra bozulmadan kalan, xmm0 öğesinden tekrar aldığı ikinci kez çağrıldığında kayıt defterini karıştırmayın.

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
katma
Bence bu, format dizgisine ve printf'e iletilen argümanların listesiyle eşleşmediğinde elde ettiğiniz tanımsız davranışlardan biri. Bir kaza yapmadığına sevin :)
katma yazar Cristik, kaynak
Cevabınız için teşekkür ederim. Fakat üçüncü programı nasıl açıklayabilir?
katma yazar Mr.CodeMonkey, kaynak
Üçüncü programdaki çıktıların çıktı tamponunun çıktı olduğunu düşünüyorum. Çünkü iki printf cümlesini değiştirdiğimde, çıktı a = 0.000000 b = 97.000000 çıktı.
katma yazar Mr.CodeMonkey, kaynak

Burada % f , bir kayan nokta için değiştirilecek bir belirteçtir.

Bir karakteri değiştirmek için % c gerekir.

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

5
katma
@ Mr.CodeMonkey, yaptığınız şey tanımsız davranış, bu nedenle her şey , evrenin toplam imhası dahil olmak üzere geçerli bir sonuçtur. Başka bir deyişle, bunu yapma :-)
katma yazar paxdiablo, kaynak
Bunu biliyorum Fakat% c yerine% f kullandığımı bilmek istiyorum, printf işlevi ne yapacak? Kaydı nasıl okuyor?
katma yazar Mr.CodeMonkey, kaynak

En son 'C' standardına göre tanımsız bir davranış. 7.21.6.1 pt 9'u c standart taslaktan kontrol edin.

Bir dönüşüm özelliği geçersizse, davranış   undefined.282) Herhangi bir argüman   karşılık gelen dönüşüm belirtimi, davranış tanımsız.

Dolayısıyla, şeylerin tanımsız bir davranışa sahip olduğu söylendiğinde her şey mümkündür ve davranış bir derleyiciden diğerine değişebilir. 'C', parmağını bir baltayla kesmene izin ver, ama bu yapmamalısın.

1
katma

% f Float içindir  Karakterler için% c

The 97 which you have got is the ASCII value for 'a'
1
katma

Arasındaki fark

printf("%f\n, 97.0);

ve

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

is that the printf function reads its parameters from the stack based on the %X you give, ve 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) ve displays it (it displays the less significant byte if an int is provided).

% f için printf, (bayt cinsinden sizeof (float) , genellikle gcc/Intel işlemcilerde 4 bayt olan) bir float bekler.

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

0
katma
Neden bu durumda 0 aldığınızı açıklayan Cristik'ten (yukarıda sanırım) çıkan mükemmel cevabı okuyun.
katma yazar Ring Ø, kaynak
Ne dediğini anlıyorum. Ama programımı açıklayamaz. Char a = 'a' gibi tanımladığımda; ve printf ("a =% f \ n", a); printf, a adresinden başlayan 4 bayt veri okuyor mu? Eğer öyleyse, çıkış 0 olamaz, değil mi?
katma yazar Mr.CodeMonkey, kaynak

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

Eğer kullanırsan

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

Karakteri alacaksın.

Yani, ilk kodunuzu olarak değiştirirseniz,

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

Olarak çıktı alacaksınız

a
0
katma
@ Mr.CodeMonkey, aldığım çıktıyı kontrol etmek isteyebilirsiniz burayı
katma yazar Arun A S, kaynak
@ Mr.CodeMonkey, çıktıdaki fark muhtemelen derleyicinin nasıl yorumladığından kaynaklanmaktadır.
katma yazar Arun A S, kaynak
Biliyorum. Fakat% f ile% c arasındaki farkın ne olduğunu bilmek istiyorum.
katma yazar Mr.CodeMonkey, kaynak
AA neden? Clodeblocks13.12 kullanıyorum. Benim işletim sistemim Windows7 64bit. Çıktımızın neden farklı olduğunu biliyor musunuz?
katma yazar Mr.CodeMonkey, kaynak
Başlatılmamış bellekteki verilere bağlı olduğunu düşünüyorum.
katma yazar Mr.CodeMonkey, kaynak