2010-03-11 10 views
9

pisałem ten mały kod:printf z size_t zmiennej z LLD, LD i d identyfikatorów typu

#include <stdio.h> 
int main() { 
    size_t temp; 
    temp = 100; 

    printf("lld=%lld, ld=%ld, u=%u\n", temp, temp, temp); 

    return 0; 
} 

Używam tego na i386 GNU/Linux maszynie z wersji gcc 4.1.1 20070105 (Red Hat 4.1.1-52). Jest to wyjście, które mam:

lld=429496729700, ld=100, u=7993461 

mogę zrozumieć, że pierwsza (lld) została wydrukowana jako śmieci, ponieważ printf próbuje drukować 8 bajtów (dla signed long long jak oznacza lld), gdy tylko 4 bajty są dostępne od zmiennej temp. Ale nie rozumiem, dlaczego ostatni identyfikator, u jest drukowany jako śmieci - podczas gdy w moim rozumieniu jest to najbliższy stosowny identyfikator dla size_t.

Tutaj założyłem, że size_t jest unsigned int (który jest podpisany 4 bajty dla mojego i386).

Teraz zrobiłem trochę szczypanie z linią printf:

... 
printf("ld=%ld, u=%u, lld=%lld\n", temp, temp, temp); 
... 

i mam perfekcyjnie odpowiedź (z wyjątkiem lld części).

ld=100, u=100, lld=34331653576851556 

Czy ktoś może mi pomóc w zrozumieniu, czego dokładnie tu brakuje?

Wielkie dzięki za pomoc!

[dygresja: Próbowałem przełączania optymalizacji przy użyciu gcc -O[0,2] tag on/off bez jakiejkolwiek różnicy w obserwacji.]

+0

Opcja -Wformat prawdopodobnie wykryje tę niezgodność. – Clifford

+0

@Clifford Dzięki za wiadomość - ale celowo użyłem niewłaściwych specyfikatorów. Właściwie to chciałem wiedzieć, w jaki sposób 'printf' zużywa elementy na stosie. Właściwie myślę, że -Wall też miałby to samo. Niemniej jednak, dziękuję bardzo. – Shrey

+0

Zdaję sobie sprawę, że to było celowe; ale moim celem było to, że jeśli intencją było obserwowanie zachowania kompilatora, opcja -Wformat demonstruje alternatywne (i bezpieczniejsze) zachowanie, które mogło być interesujące dla kogoś, nawet jeśli byłeś już tego świadomy. -Wall nie uwzględnił -Wformat na starszych wersjach GCC. – Clifford

Odpowiedz

22

To dlatego, że to, co masz wciśnięty na stosie jest trzy wartości 32-bitowe i swój Łańcuch formatu próbuje użyć czterech z nich lub, dokładniej, jednej wartości 64-bitowej i dwóch wartości 32-bitowych.

W pierwszym przypadku lld zasysa dwie 32-bitowe wartości, ld zasysa trzecią, a u dostaje wszystko, co dzieje się na stosie, co może być cokolwiek.

Po zmianie kolejność specyfikatorów formatu w ciągu, to działa inaczej, ponieważ ld zasysa pierwszą wartość 32-bitowa, u zasysa drugi i lld zasysa trzeci plusa Cokolwiek się zdarzy być na stosie po tym. Właśnie dlatego otrzymujesz różne wartości, jest to problem wyrównania/dostępności danych.

Widać to w akcji z pierwszą wartością. 429496729700 jest równy (4294967296 + 1) * 100, tj. (2 +1) * 100.Twój kod urywek

printf("lld=%lld, ld=%ld, u=%u\n", temp, temp, temp); 

faktycznie ma następujący efekt:

What you pass  Stack  What printf() uses 
-------------  -----  ------------------ 
       +-----+ 
100    | 100 | \ 
       +-----+ = 64-bit value for %lld. 
100    | 100 |/
       +-----+ 
100    | 100 | 32-bit value for %ld. 
       +-----+ 
       | ? | 32-bit value for %u (could be anything). 
       +-----+ 

w drugim przypadku

printf("ld=%ld, u=%u, lld=%lld\n", temp, temp, temp); 

poniższych sytuacji:

What you pass  Stack  What printf() uses 
-------------  -----  ------------------ 
       +-----+ 
100    | 100 | 32-bit value for %ld. 
       +-----+ 
100    | 100 | 32-bit value for %u. 
       +-----+ 
100    | 100 | \ 
       +-----+ = 64-bit value for %lld (could be anything). 
       | ? |/
       +-----+ 
+0

Tak, brzmi idealnie, gdy szukałem odpowiedzi. Przeczytałem kod assemblera i on też robi to, o czym wspomniałeś (część wypychania 3 zmiennych z 32 bajtów na stosie). Po prostu nie mogłem potwierdzić, jak je zużywa printf. Dziękuję za potwierdzenie! – Shrey

0

są przechodzącą do printf zła liczba b ytes. % lld wymaga większej liczby całkowitej, w twoim przypadku metoda% lld taked argument jest całkowicie pomieszany, ponieważ oczekuje wartości 64-bitowej.

7

Twój kod trafnie pokazuje niezdefiniowane zachowanie. Zwróć uwagę, że w przypadku argumentów typu variadic nie wykonuje się sprawdzania typów dla parametrów. To wtedy staje się konieczny wyraźny rzut. W rzeczywistości powinno być stosowane następujące:

printf("lld=%lld, ld=%ld, u=%u\n", 
     (unsigned long long)temp, 
     (unsigned long)temp, 
     (unsigned int)temp); 

Tak na marginesie pamiętać specyfikator dla size_t jest z. A więc:

printf("zd=%zd\n", temp);