2015-09-01 40 views
5

mam następujący prosty program, który korzysta z unii do konwersji pomiędzy całkowitej 64 bitowej oraz odpowiadającego mu tablicy bajtów:C/C++ Konwersja 64 bitową liczbę całkowitą do tablicy char

union u 
{ 
    uint64_t ui; 
    char c[sizeof(uint64_t)]; 
}; 

int main(int argc, char *argv[]) 
{ 
    u test; 
    test.ui = 0xabcdefLL; 
    for(unsigned int idx = 0; idx < sizeof(uint64_t); idx++) 
    { 
     cout << "test.c[" << idx << "] = 0x" << hex << +test.c[idx] << endl; 
    } 
    return 0; 
} 

co by się spodziewać jako wyjście jest:

test.c[0] = 0xef 
test.c[1] = 0xcd 
test.c[2] = 0xab 
test.c[3] = 0x89 
test.c[4] = 0x67 
test.c[5] = 0x45 
test.c[6] = 0x23 
test.c[7] = 0x1 

Ale to, co rzeczywiście się znajduje:

test.c[0] = 0xffffffef 
test.c[1] = 0xffffffcd 
test.c[2] = 0xffffffab 
test.c[3] = 0xffffff89 
test.c[4] = 0x67 
test.c[5] = 0x45 
test.c[6] = 0x23 
test.c[7] = 0x1 

widzę to na Ubuntu LTS 14.04 z GCC.

Od pewnego czasu próbuję to zrozumieć. Dlaczego pierwsze 4 elementy tablicy znaków wyświetlane są jako 32-bitowe liczby całkowite, z dodaniem do nich 0xffffff? I dlaczego tylko pierwsze 4, dlaczego nie wszystkie?
Co ciekawe, kiedy używam tablicy do zapisu w strumieniu (co było pierwotnym celem całego obiektu), poprawne wartości są zapisywane. Porównywanie tablicy char przez char oczywiście prowadzi do problemów, ponieważ pierwsze 4 znaki nie są równe 0xef, 0xcd i tak dalej.

+0

rzutować jako '(char *)', a następnie odczytać 4 bajty ...? – SteJ

+0

Bez zmian. Poza tym zawsze mogę maskować znak za pomocą 0x000000ff, aby uzyskać oczekiwane wartości. Po prostu interesuje mnie przyczyna tego zachowania. – tickferno

+2

Najwyraźniej twoja implementacja podpisała znaki. Zwykła promocja z liczbami całkowitymi spowoduje podpisanie rozszerzenia. – ewd

Odpowiedz

3

Używanie char nie jest właściwym rozwiązaniem, ponieważ może to być signed lub unsigned. Użyj unsigned char.

union u 
{ 
    uint64_t ui; 
    unsigned char c[sizeof(uint64_t)]; 
}; 
+0

To rozwiązuje. Problem polega na tym, że zapis tablicy do strumienia daje błędy, ponieważ strumienie akceptują tylko 'char *', a nie 'unsigned char *' plus naprawdę nie lubię używać 'reinterpret_cast' przez cały czas ... – tickferno

+1

@tickferno, jeśli potrzebujesz pomocy, opublikuj kolejne pytanie, które w konkretny sposób odnosi się do problemu z transmisją strumieniową. –

0

jest unsigned char vs podpisanego odbarwiającego i jego odlewania całkowitych

1

użyć jednej unsigned char, lub test.c[idx] & 0xff uniknąć przedłużenie gdy char value > 0x7f znak jest przekształcany na int.

0

jednoargumentowych oraz powoduje char być podniesione na int (Integralna). Ponieważ podpisałeś znaki, wartość zostanie użyta jako taka, a pozostałe bajty odzwierciedlą to.

Nie jest prawdą, że tylko cztery są intami, wszystkie są. Po prostu nie widzisz tego z reprezentacji, ponieważ początkowe zera nie są pokazane.

Aby uzyskać pożądany wynik, użyj promocji unsigned char lub & 0xff.

2

char zostaje awansowany na int z powodu wcześniejszego jednokonnego operatora +. . Ponieważ twój chars to signed, każdy element o najwyższym ustawieniu na 1 jest interpretowany jako liczba ujemna i promowany do liczby całkowitej o tej samej wartości ujemnej. Istnieje kilka różnych sposobów, aby rozwiązać ten problem:

  1. upuść +: ... << test.c[idx] << .... To może wydrukować znak jako znak, a nie numer, więc prawdopodobnie nie jest dobrym rozwiązaniem.
  2. Zadeklaruj c jako unsigned char. Spowoduje to promowanie go pod numerem unsigned int.
  3. Jawnie cast +test.c[idx] zanim zostanie przekazany: ... << (unsigned char)(+test.c[idx]) << ...
  4. Ustaw górne bajty liczby całkowitej do zera przy użyciu binarnego &: ... << +test.c[idx] & 0xFF << ....Wyświetli się tylko bajt najniższego rzędu, niezależnie od tego, w jaki sposób promowany jest kod char.
+0

char nie jest promowany w <<, jest to spowodowane operatorem jednoargumentowym +. Można to łatwo zauważyć, jeśli upuścisz unary + z kodu. –

+0

Upuszczenie operatora unarnego + wypisuje znak "bezpośrednio", ale interesuje mnie ich faktyczna wartość szesnastkowa (operator heksadecymalny nie pomaga inaczej). Również następujące nie powiedzie się: 'test.c [0] == 0xef' – tickferno

+0

Dobre połączenie. Edytowane odpowiednio. –

Powiązane problemy