Bu program nasıl işliyor?


88
#include <stdio.h>

int main() {
    float a = 1234.5f;
    printf("%d\n", a);
    return 0;
}

Bir 0!! Bu nasıl mümkün olabilir? Gerekçe nedir?


Ben kasten bir koyduk %diçinde printfdavranışını incelemek için deyimi printf.

Yanıtlar:


239

Bunun nedeni bir %dbeklemesidir, intancak siz bir şamandıra sağladınız.

Şamandırayı yazdırmak için %e/ %f/ öğesini kullanın %g.


Neden 0'ın yazdırıldığına dair: Kayan nokta numarası, doublegönderilmeden önce dönüştürülür printf. Little endian'da çift gösterimde 1234,5 sayısı

00 00 00 00  00 4A 93 40

A %d, 32 bitlik bir tamsayı kullanır, bu nedenle sıfır yazdırılır. (Bir test printf("%d, %d\n", 1234.5f);olarak çıktı alabilirsiniz 0, 1083394560.)


Neden 6.5.2.2 / 7'den prototip olan printf'e floatdönüştürüldüğüne gelince,doubleint printf(const char*, ...)

Bir işlev prototip tanımlayıcısındaki üç nokta gösterimi, argüman türü dönüşümünün bildirilen son parametreden sonra durmasına neden olur. Varsayılan bağımsız değişken yükseltmeleri, takip eden bağımsız değişkenler üzerinde gerçekleştirilir.

ve 6.5.2.2/6'dan itibaren,

Çağrılan işlevi belirten ifade, prototip içermeyen bir türe sahipse, her bağımsız değişkende tamsayı yükseltmeleri gerçekleştirilir ve türe sahip bağımsız değişkenler floatyükseltilir double. Bunlara varsayılan bağımsız değişken yükseltmeleri denir .

(Bunu öğrendiği için teşekkürler Alok.)


4
+1 En iyi cevap. Hem "standart teknik olarak doğru" neden hem de "olası uygulamanız" nedenini yanıtlar.
Chris Lutz 04

12
Sanırım onun aradığı cevabı veren 12 kişiden sadece sizsiniz.
Gabe 04

11
Çünkü printfdeğişken bir fonksiyondur ve standart, değişken fonksiyonlar için a'nın geçmeden önce floatdönüştürüldüğünü söyler double.
Alok Singhal

8
C standardından: "Bir fonksiyon prototip tanımlayıcısındaki üç nokta gösterimi, argüman türü dönüşümünün son bildirilen parametreden sonra durmasına neden olur. Varsayılan argüman yükseltmeleri, takip eden argümanlarda gerçekleştirilir." ve "... ve float türüne sahip bağımsız değişkenler iki katına çıkarılır. Bunlara varsayılan bağımsız değişken yükseltmeleri denir ."
Alok Singhal

2
Yanlış biçim belirticisinin kullanılması Tanımsız Davranışıprintf() çağırır .
Prasoon Saurav

45

Teknik olarak hiçbir orada konuşma , her kütüphane uygular kendi ve bu nedenle, çalışmanın çalışmakla yönteminiz sen çok kullanmak gitmiyor ne yaptığını yaparak davranış 'ın. Sisteminizin davranışını incelemeye çalışıyor olabilirsiniz ve eğer öyleyse, belgeleri okumalı ve kitaplığınız için mevcutsa kaynak koduna bakmalısınız . printfprintfprintfprintf

Örneğin, Macbook'umda çıktıyı 1606416304programınızla alıyorum .

Bir geçerken, söyledikten floatvariadic işleve, floatbir şekilde geçirilir double. Yani, programınız abir double.

Bir bayt incelemek için double, gördüğünüz bu cevabı burada SO üzerinde son soruya.

Hadi bunu yapalım:

#include <stdio.h>

int main(void)
{
    double a = 1234.5f;
    unsigned char *p = (unsigned char *)&a;
    size_t i;

    printf("size of double: %zu, int: %zu\n", sizeof(double), sizeof(int));
    for (i=0; i < sizeof a; ++i)
        printf("%02x ", p[i]);
    putchar('\n');
    return 0;
}

Yukarıdaki programı çalıştırdığımda şunu elde ederim:

size of double: 8, int: 4
00 00 00 00 00 4a 93 40 

Yani, ilk dört baytı double0 çıktı, bu yüzden aramanızın 0çıktısını almış olabilirsiniz printf.

Daha ilginç sonuçlar için programı biraz değiştirebiliriz:

#include <stdio.h>

int main(void)
{
    double a = 1234.5f;
    int b = 42;

    printf("%d %d\n", a, b);
    return 0;
}

Yukarıdaki programı Macbook'umda çalıştırdığımda şunu elde ederim:

42 1606416384

Bir Linux makinesindeki aynı programla şunu elde ederim:

0 1083394560

Neden geriye doğru yazdırmayı programladınız? Bir şey mi kaçırıyorum? Eğer b, bir int = 42 ise ve '% d' tamsayı biçimiyse, neden printf bağımsız değişkenlerindeki ikinci değişken olduğu için ikinci basılı değer değil? Bu bir yazım hatası mı?
Katastic Voyage

Muhtemelen intargümanların argümanlardan farklı kayıtlarda geçirilmesinden dolayı double. printfile 42 %dolan intargümanı alır ve %dikinci intargüman olmadığından muhtemelen ikinci önemsiz yazdırır .
Alok Singhal

20

%dbelirteci söylerprintf bir tamsayı beklemek. Dolayısıyla, float'ın ilk dört (veya platforma bağlı olarak iki) baytı bir tamsayı olarak yorumlanır. Sıfır olurlarsa, sıfır yazdırılır

1234.5'in ikili gösterimi şuna benzer

1.00110100101 * 2^10 (exponent is decimal ...)

floatAslında IEEE754 çift değerlerini temsil eden bir C derleyicisiyle , baytlar (hata yapmazsam)

01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000

Çok az dayanıklı (yani en önemsiz bayt önce gelen) bir Intel (x86) sisteminde, bu bayt dizisi ilk dört bayt sıfır olacak şekilde tersine çevrilir. Yani neprintf çıktı ...

IEEE754'e göre kayan nokta gösterimi için Bu Wikipedia makalesine bakın .


7

Bunun nedeni, ikili sayıdaki bir kayan noktaların temsilidir. Bir tam sayıya dönüştürme onu 0 olarak bırakır.


1
Görünüşe göre onun ne istediğini anlayan tek kişi sensin. Tabii ki kendimi yanılmıyorsam.
Mizipzor 04

Cevabın yanlış ve eksik olduğunu ancak yanlış olmadığını kabul ediyorum.
Shaihi

7

Tanımlanmamış davranışı çağırdığınız için: printf () yönteminin sözleşmesini parametre türleri hakkında yalan söyleyerek ihlal ettiniz, böylece derleyici ne isterse yapmakta özgürdür. Programın çıktısını "dksjalk is a ninnyhead !!!" yapabilir. ve teknik olarak yine de doğru olacaktır.


5

Nedeni, bu printf()oldukça aptalca bir işlev. Türleri hiç kontrol etmez. İlk argümanın bir olduğunu söylerseniz int(ve %dbununla birlikte söylediğiniz şey budur ), size inanır ve yalnızca bir için gereken baytları alır int. Bu durumda, makinenizin dört bayt intve sekiz bayt kullandığını varsayarsak double( içeriye floatdönüştürülür ), ilk dört baytı yalnızca sıfır olur ve bu yazdırılır.doubleprintf()a


3

Float'ı otomatik olarak tam sayıya dönüştürmez. Çünkü her ikisinin de farklı depolama biçimi vardır. Yani dönüştürmek istiyorsanız, (int) tipini kullanın.

#include <stdio.h>

int main() {
    float a = 1234.5f;
    printf("%d\n", (int)a);
    return 0;
}

2

Siz de onu C ++ ile etiketlediğiniz için, bu kod dönüşümü muhtemelen beklediğiniz gibi yapar:

#include <iostream.h>

int main() {
    float a = 1234.5f;
    std::cout << a << " " << (int)a << "\n";
    return 0;
}

Çıktı:

1234.5 1234

1

%d ondalık

%f yüzer

bunlardan daha fazlasını burada görün .

0 alıyorsunuz çünkü kayan sayılar ve tam sayılar farklı şekilde temsil ediliyor.


1

Yalnızca ilgili veri türüyle (int, float, string, vb.) Uygun biçim belirticisini (% d,% f,% s, vb.) Kullanmanız gerekir.


Soru, nasıl düzeltileceği değil, neden çalıştığı şekilde çalıştığıdır.
ya23


0

hey o bir şey basmak zorundaydı, bu yüzden 0 yazdırdı. Unutmayın, C 0 da her şeydir!


1
Nasıl böyle C'de her şey diye bir şey yoktur.
Vlad

eğer x bir
şeyse

eğer (iGot == "her şey") "her şeyi" yazdır ; aksi takdirde "hiçbir şey" yazdırmaz;
nik

0

Tam sayı değil. Kullanmayı deneyin %f.


Sanırım soru şu, neden float'ı int'e dönüştürüp "1234" göstermiyor?
Björn Pollex 04

c'nin mantıklı olmayan bir şeyi yapmanıza izin vermemesini beklemeyin. Evet, birçok dil beklediğiniz 1234'ü verir ve belki c'nin bazı uygulamaları bile bu davranışın tanımlı olduğunu düşünmezim. C kendini asmana izin verir, tıpkı cehennem için çatlamayı denemene izin veren ebeveyn gibi.
yeniden

Çünkü C esnek olacak şekilde tasarlanmıştır.
tangrs
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.