Konwersja int -> uint8_t

0

Wie ktoś jak przeprowadzić taką konwersję w C?

1

Jeśli masz pewność, że wartość zapisana w tym int nie przekroczy "pojemności" uint8_t i nie będzie ujemna (czyli 0-255), to:

int		a = 42;
uint8_t	b = (uint8_t)a;

W innych przypadkach będziesz po konwersji miał nieprawidłowe wartości.
Pamiętaj, że definicja uint8_t siedzi w stdint.h

1

Dla ścisłości dodam, że to się nazywa rzutowanie, nie zaś konwersja.

0

A co jeśli int zajmuje więcej niż 1 bajt? Bo uint8_t chyba właśnie 1 bajt?

0

int zajmuje zazwyczaj 4 bajty w dzisiejszych czasach. Generalnie po rzutowaniu otrzymujesz najmłodszy bajt z inta, chociaż nie wiem czy to jest wymuszone w specyfikacji C++.

0

Chodziło mi o to, że int się nie zmieści na jednym bajcie.
A jak odjąć najmłodszy bajt?

0

a & 255 = najmłodszy bajt
a & -256 = liczba z odjętym najmłodszym bajtem

0

Jakoś tego nie rozumiem. Mógłbyś dać przykład jak zamienić np. liczbę 300?

0

300 & -256 = 256 - to wynik po odjęciu wartości najmłodszego bajta

Binarnie mamy

...00000100101100 & ...1111111000000000 = ....0100000000

Gdzie te kropki oznaczają powtórzenie pierwszej cyfry. Jak widać ostatnie 8 bitów się nam wyzerowało.

300 & 255 = 44 - to wartość ostatniego bajta

Binarnie 44 to

....00000101100

Jak widać zostały wyzerowane wszystkie bity oprócz ostatnich ośmiu (nota bene tylko jeden oprócz nich był ustawiony na 1).

0
SoyeR napisał(a)

Jakoś tego nie rozumiem. Mógłbyś dać przykład jak zamienić np. liczbę 300?

Sorry za głupie pytanie, ale czy Ty przypadkiem nie chcesz upchnąć wartości 300 w jednym bajcie? Jeśli tak, to se ne da. Następny typ, który jest w stanie pomieścić 300 to uint16_t (2 bajty), ale to nadal jest mniej niż int (4 lub 8 bajtów).

0

Nie, ja chcę upchnąć int w czterech uint8_t. Tylko nie za bardzo wiem jak dzielić bajty i je przesuwać.

0
#include <stdio.h>

// typedef unsigned char uint8_t;

int main(){
  int a = 42424242;
  uint8_t *b = (uint8_t*)&a;

  printf("   a = %i\n",a);
  printf("b[0] = %i\n",b[0]);
  printf("b[1] = %i\n",b[1]);
  printf("b[2] = %i\n",b[2]);
  printf("b[3] = %i\n",b[3]);
}

tylko pamiętaj, że b w tym momencie wskazuje na a, więc jest prawidłowe tak długo, jak istnieje a.

0

Tyle, że powyższy zapis jest zależny od kolejności bajtów w incie, np x86 jest little-endian, a PPC z reguły big-endian, więc ten kod może dać różne wyniki na różnych procesorach.

0

Ewentualnie:

#include <stdio.h>
#include <stdint.h>
#include <string.h>



int main(void) {
  int           a = 42424242;
  uint8_t    b[4];

  memcpy(b, &a, 4);

  printf("   a = %i\n",a);
  printf("b[0] = %i\n",b[0]);
  printf("b[1] = %i\n",b[1]);
  printf("b[2] = %i\n",b[2]);
  printf("b[3] = %i\n",b[3]);

  return 0;
}

(wykorzystałem fragment kodu kolegi wyżej, sorki)

0

Brawo, to może zarzućcie coś jeszcze z reinterpret_castem. Tak czy siak wszystkie wasze metody są nieprzenośne.

0

Zakładamy, że twórca wątku ma świadomość kolejności bajtów na różnych platformach. Zauważ, że nie zaznaczyliśmy też explicite, że na 64bit int ma 8 bajtów i odpowiednio musi sobie pod to zmodyfikować kod. Nie będziemy mu przecież tutaj wykładać teorii i konstruować funkcji byte-swap (które nawiasem mówiąc na różnych platformach różnie wyglądają). A nawet jeśli, to czy na format przenośny wybrać LE, BE czy hosta? Dużo tego i wątek by się rozrósł wojnami między sympatykami jednego i drugiego. Szkoda takiego pięknego dnia, a kolega wątkotwórca może sobie conieco poczyta w tym temacie i wyjdzie lepiej dla niego ;)

0

Ale po co byte swap?

u_int32_t x;

u_int8_t a = (x >> 0) & 0xFF;
u_int8_t b = (x >> 8) & 0xFF;
u_int8_t c = (x >> 16) & 0xFF;
u_int8_t d = (x >> 24) & 0xFF;

To jest najpopularniejsza metoda, nie widziałem nigdy wcześniej waszych metod, nikt ich nie stosuje bo są nieprzenośne.

0

O, fajna metoda. Trzeba było tak od razu :)
A teraz korzystajmy z pięknego dnia.

0

Tyle, że powyższy zapis jest zależny od kolejności bajtów w incie, np x86 jest little-endian, a PPC z reguły big-endian, więc ten kod może dać różne wyniki na różnych procesorach.
Żadna metoda „rozbijania inta” nie jest wolna od konieczności podjęcia jakiejś decyzji w tym względzie.

0
Azarien_ napisał(a)

Tyle, że powyższy zapis jest zależny od kolejności bajtów w incie, np x86 jest little-endian, a PPC z reguły big-endian, więc ten kod może dać różne wyniki na różnych procesorach.
Żadna metoda „rozbijania inta” nie jest wolna od konieczności podjęcia jakiejś decyzji w tym względzie.

Tak, tyle że mi chodzi o przenośność - pewność że na każdej platformie kod zadziała tak samo. Mogę sobie nawet permutować bajty w dowolnej ale ustalonej kolejności, ważne jest tylko to żebym wiedział, co oznacza dana zmienna.

0

No tak, zaproponowanie przez ciebie metody są przenośne, tylko że wolniejsze. Nie czepiam się akurat tej metody, ale ogólnie podejścia, że w C++ należy programować jak najbardziej wielosystemowo. C++ jest naprawdę bliski językowi maszynowemu (mimo paru dodatkowych, bardzo przydatnych, poziomów abstrakcji), tym samym nadaję się głównie do pisania programów, w których głównie liczy się wydajność.
Jeżeli wydajność nie jest czynnikiem decydującym, to istnieje wiele innnych języków, w których można napisać daną aplikacje łatwiej, szybciej oraz bezpieczniej (patrz np. Java, pozbawiona min. wskaźników, typów unsigned, czy goto, ale z to z garbage collectorem i większą objektownością (moja opinia może być kompletnie niezgodna z rzeczywistością, bo Javy dobrze nie znam)), ale mniej wydajnie (co w 90% przypadków nie robi różnicy).

0

(patrz np. Java, pozbawiona min. wskaźników, typów unsigned, czy goto, ale z to z garbage collectorem i większą objektownością
C# ma GC i „większą obiektowość” zachowujący przy tym wskaźniki, typy unsigned i goto ;-)

0
Zjarek napisał(a)

No tak, zaproponowanie przez ciebie metody są przenośne, tylko że wolniejsze. Nie czepiam się akurat tej metody, ale ogólnie podejścia, że w C++ należy programować jak najbardziej wielosystemowo. C++ jest naprawdę bliski językowi maszynowemu (mimo paru dodatkowych, bardzo przydatnych, poziomów abstrakcji), tym samym nadaję się głównie do pisania programów, w których głównie liczy się wydajność.
Jeżeli wydajność nie jest czynnikiem decydującym, to istnieje wiele innnych języków, w których można napisać daną aplikacje łatwiej, szybciej oraz bezpieczniej (patrz np. Java, pozbawiona min. wskaźników, typów unsigned, czy goto, ale z to z garbage collectorem i większą objektownością (moja opinia może być kompletnie niezgodna z rzeczywistością, bo Javy dobrze nie znam)), ale mniej wydajnie (co w 90% przypadków nie robi różnicy).

Jest naprawę niewiele różnic pomiędzy platformami na poziomie programowania w C++. W zasadzie to kolejność bajtów jest chyba jedynym problemem napotykanym przy pisaniu zwykłych aplikacji, a konstrukcję napisaną przeze mnie kompilator x86 powinien sobie zoptymalizować do jakiegoś mova czy bswapa (mnemoniki x86).

ARM zyskuje na sile, nie wiadomo czy w ciągu kilku lat nie będzie popularną platformą, konkurującą z x86 w segmencie laptopów na przykład. A tam kolejność bajtów jest raczej inna niż w x86.

0

@Wibowit - kompilator nawet bswapa nie potrzebował.

No tak, zaproponowanie przez ciebie metody są przenośne, tylko że wolniejsze.

Ech, takich pseudooptymalizacji to nie wykonywano chyba nawet w czasach kiedy jeszcze liczyli każdy cykl procesora...
Tutaj kilka na szybko wykonanych przeze mnie testów (gcc na -O3):

uwaga, sporo bezsensownych listingów : ;)

Na początek 'wolny' program Wibowita:

#include <stdio.h>

typedef unsigned long int u_int32_t;
typedef unsigned char u_int8_t;

int main()
{
	u_int32_t x;

	scanf("%ld", x);

	u_int8_t a = (x >> 0) & 0xFF;
	u_int8_t b = (x >> 8) & 0xFF;
	u_int8_t c = (x >> 16) & 0xFF;
	u_int8_t d = (x >> 24) & 0xFF;

	printf("%d", a);
	printf("%d", b);
	printf("%d", c);
	printf("%d", d);
}

Co daje nam w wyniku (wrzucam kody od znaczących parametrów do scanf do ostatniego printf):

.text:004012EA mov     [esp+38h+var_34], ebx
.text:004012EE shr     ebx, 8
.text:004012F1 mov     [esp+38h+var_38], offset unk_403000
.text:004012F8 call    scanf
.text:004012FD mov     [esp+38h+var_38], offset dword_403004
.text:00401304 mov     edx, esi
.text:00401306 mov     eax, esi
.text:00401308 shr     edx, 10h
.text:0040130B xor     esi, esi
.text:0040130D shr     eax, 18h
.text:00401310 mov     [ebp+var_18], dl
.text:00401313 mov     [esp+38h+var_34], esi
.text:00401317 mov     [ebp+var_28], al
.text:0040131A call    printf
.text:0040131F mov     [esp+38h+var_38], offset dword_403004
.text:00401326 movzx   ecx, bl
.text:00401329 mov     [esp+38h+var_34], ecx
.text:0040132D call    printf
.text:00401332 movzx   edx, [ebp+var_18]
.text:00401336 mov     [esp+38h+var_38], offset dword_403004
.text:0040133D mov     [esp+38h+var_34], edx
.text:00401341 call    printf
.text:00401346 movzx   eax, [ebp+var_28]
.text:0040134A mov     [esp+38h+var_38], offset dword_403004
.text:00401351 mov     [esp+38h+var_34], eax
.text:00401355 call    printf

Rezultat? Mniej-więcej 5 opkodów operacji wykonujących te całe "powolne" działania...

Ok, zmienne były lokalne, zobaczymy co będzie jeśli użyjemy globalnych (kompilator mie może zrobić pewnych optymalizacji)

#include <stdio.h>

typedef unsigned long int u_int32_t;
typedef unsigned char u_int8_t;

u_int8_t a;
u_int8_t b;
u_int8_t c;
u_int8_t d;

int main()
{
	u_int32_t x;

	scanf("%ld", x);

	a = (x >> 0) & 0xFF;
	b = (x >> 8) & 0xFF;
	c = (x >> 16) & 0xFF;
	d = (x >> 24) & 0xFF;

	printf("%d", a);
	printf("%d", b);
	printf("%d", c);
	printf("%d", d);
}
.text:004012FB                 mov     [esp+18h+var_18], offset unk_403000
.text:00401302                 shr     edi, 18h
.text:00401305                 call    scanf
.text:0040130A                 mov     ds:byte_40400A, bl
.text:00401310                 mov     edx, esi
.text:00401312                 mov     eax, edi
.text:00401314                 mov     ds:byte_404009, dl
.text:0040131A                 xor     ebx, ebx
.text:0040131C                 mov     [esp+18h+var_14], ebx
.text:00401320                 mov     [esp+18h+var_18], offset unk_403004
.text:00401327                 mov     ds:byte_40400B, 0
.text:0040132E                 mov     ds:byte_404008, al
.text:00401333                 call    printf
.text:00401338                 movzx   ecx, ds:byte_40400A
.text:0040133F                 mov     [esp+18h+var_18], offset unk_403004
.text:00401346                 mov     [esp+18h+var_14], ecx
.text:0040134A                 call    printf
.text:0040134F                 movzx   edx, ds:byte_404009
.text:00401356                 mov     [esp+18h+var_18], offset unk_403004
.text:0040135D                 mov     [esp+18h+var_14], edx
.text:00401361                 call    printf
.text:00401366                 movzx   eax, ds:byte_404008
.text:0040136D                 mov     [esp+18h+var_18], offset unk_403004
.text:00401374                 mov     [esp+18h+var_14], eax
.text:00401378                 call    printf

Wynik? Koło 6 opkodów narzutu.

No i na deser "zoptymalizowana", nieprzenośna wersja:

#include <stdio.h>

typedef unsigned long int u_int32_t;
typedef unsigned char u_int8_t;

int main()
{
	u_int32_t a;
	char *b = (char*)&a;

	scanf("%ld", a);

	 printf("   a = %i\n",a);
 	 printf("b[0] = %i\n",b[0]);
	 printf("b[1] = %i\n",b[1]);
	 printf("b[2] = %i\n",b[2]);
	 printf("b[3] = %i\n",b[3]);
}
.text:004012E8                 mov     [esp+18h+var_18], offset unk_403000
.text:004012EF                 mov     ecx, [ebp+var_4]
.text:004012F2                 mov     [esp+18h+var_14], ecx
.text:004012F6                 call    scanf
.text:004012FB                 mov     [esp+18h+var_18], offset aAI ; "   a = %i\n"
.text:00401302                 mov     edx, [ebp+var_4]
.text:00401305                 mov     [esp+18h+var_14], edx
.text:00401309                 call    printf
.text:0040130E                 movsx   eax, byte ptr [ebp+var_4]
.text:00401312                 mov     [esp+18h+var_18], offset aB0I ; "b[0] = %i\n"
.text:00401319                 mov     [esp+18h+var_14], eax
.text:0040131D                 call    printf
.text:00401322                 movsx   ecx, byte ptr [ebp+var_4+1]
.text:00401326                 mov     [esp+18h+var_18], offset aB1I ; "b[1] = %i\n"
.text:0040132D                 mov     [esp+18h+var_14], ecx
.text:00401331                 call    printf
.text:00401336                 movsx   edx, byte ptr [ebp+var_4+2]
.text:0040133A                 mov     [esp+18h+var_18], offset aB2I ; "b[2] = %i\n"
.text:00401341                 mov     [esp+18h+var_14], edx
.text:00401345                 call    printf
.text:0040134A                 movsx   eax, byte ptr [ebp+var_4+3]
.text:0040134E                 mov     [esp+18h+var_18], offset aB3I ; "b[3] = %i\n"
.text:00401355                 mov     [esp+18h+var_14], eax
.text:00401359                 call    printf

Kwestię 'czy warto' pozostawiam do rozważenia.

0

bswap nie jest dostępny pod 386, pewnie dlatego kompilator nie użył tej instrukcji. Dodaj -march=pentium do flag i zobacz czy kompilator użyje bswapa.

1 użytkowników online, w tym zalogowanych: 0, gości: 1