piątek, 16 kwietnia 2021

C - Tablice

W tym poście chciałbym opisać sposób inicjalizacji tablic oraz struktur. 



Tablice:


Podstawowym sposobem na inicjalizację tablic jest następujący zapis:

  1. uint8_t val[rozmiar] = {0x00};

Przy czym należy tutaj pamiętać, że tablicę w taki sposób można zainicjalizować jedynie przez wpisanie do niej wartości zero. Nie można do wszystkich komórek wpisać innej wartości. Przykład poniżej:

  1. int main(void)
  2. {
  3.     uint8_t val[100] = {0x01};
  4.    
  5.     for(uint8_t i=0; i<5; i++)
  6.     {
  7.         printf("%u ", val[i]);
  8.     }
  9.  
  10.     return 0;
  11. }

Ten prosty program zwróci takie wartości:

1 0 0 0 0

Wartość 0x01 dotyczy tylko i wyłączenie pierwszego elementu. Pozostałe elementy domyślnie przyjmą wartość 0.

Wobec tego aby zainicjalizować tablicę wartościami innymi niż 0 pozostaje jedna z kilku opcji:

Wpisywanie ręczne:


W ten sposób dane wprowadzamy ręcznie jak poniżej:

  1. uint8_t tablica[rozmiar] = {100, 85, 73, 84, 5, 8};

Można w takim przypadku nie podawać wielkości tablicy:

  1. uint8_t tablica[] = {100, 85, 73, 84, 5, 8};    //Rozmiar 6

Wtedy jej rozmiar należy pobrać wykorzystując operator sizeof(). W przypadku typu uint8_t wystarczy wywołać go następująco:

  1. sizeof(tablica);

Jeśli tablica jest innego typu np. uint16_t, int (typ danych większy niż jeden bajt). To pobranie danych o rozmiarze należy zmodyfikować:

  1. (sizeof(tablica)/sizeof(typ_danych_tablicy))
  2. np. (sizeof(tablica)/sizeof(uint16_t))

W przypadku niepełnego zdeklarowania wartości, jak w przypadku drugiego przykładu, dane zostaną uzupełnione zerami. Tym razem rozmiar tablicy należy wprowadzić do funkcji 

  1. uint8_t tablica[10] = {100, 85, 73, 84, 5, 8};

Wartości od [6] do [9] przyjmą wartość 0 automatycznie.

Pętla:


Każda iteracja w pętli wpisuje kolejny element do tablicy:

  1. for(uint8_t i=0; i<100; i++)
  2. {
  3.     val[i] = i;
  4. }

Memset:



  1. memset(val, 25, 100);

Memset ustawia zadaną ilość bajtów od wskazanego miejsca, więc w przypadku typu składającego się z większej ilości bajtów należy zmodyfikować funkcję następująco:

  1. memset(val, 0x00, sizeof(val)/sizeof(val[0]));

Jako trzeci parametr należy podać ilość bajtów w tablicy.

Tutaj pojawia się natomiast problem wprowadzania danych do komórek. Jak wspomniałem wcześniej dane wpisywane są do każdego bajtu. Co oznacza, że po wprowadzaniu wartości np. 0x01 dla uint16_t czy int wartość elementu nie będzie odpowiadała prawdopodobnej wartości oczekiwanej.

Typy danych inne niż jednobajtowe powinny mieć wpisywane wartości raczej z użyciem pętli for/while niż memset. Chyba, że chcemy tylko wyzerować tablicę. W innym przypadku należy się upewnić, że funkcja zadziała tak jak należy. 

GCC:


W poniższym przykładzie wartości od 0 do 5 przyjmą wartość 1. Pozostałe elementy zostaną ustawione na zero.

  1. uint8_t val[100] = {[0 ... 5] = 0x01};

Można także inicjalizować pojedyncze wartości:

  1. uint8_t tablica[100] = {[4] = 0x01, [7] = 0x30};

Tablica powyżej będzie zawierała wartości niezerowe dla pola [4] oraz [7]. 

Można też rozpocząć inicjalizację od zadanej pozycji:

  1. uint8_t tablica[100] = {[4] = 0x01, 30, 40, 80, 120};

Dla powyższej deklaracji wartości od pozycji [4] do [8] zostaną ustawione wartości niezerowe.

Rozmiar tablicy:


Najczęściej tablice, zwłaszcza globalne, muszą być wykorzystując stałą liczbową. 

  1. #define ROZMIAR 10
  2. uint8_t val[ROZMIAR] = {0x00};

W przypadku przekazywania rozmiaru tablicy do funkcji, nie można tego dokonać w następujący sposób.

  1. void initArray(uint8_t rozmiar)
  2. {
  3.     uint8_t val[rozmiar] = {0x00};
  4.    
  5.     for(uint8_t i=0; i<rozmiar; i++)
  6.     {
  7.         printf("%u\r\n", val[i]);
  8.     }
  9. }

Tablica musi najpierw musi zostać zdeklarowana, po czym można do niej wprowadzać wartości. 

  1. void initArray(uint8_t rozmiar)
  2. {
  3.     uint8_t val[rozmiar];
  4.    
  5.     for(uint8_t i=0; i<rozmiar; i++) {
  6.         val[i] = i;
  7.     }
  8.  
  9.     for(uint8_t i=0; i<rozmiar; i++) {
  10.         printf("%u\r\n", val[i]);
  11.     }
  12. }

Dla powyższego przypadku należy pamiętać, że tablica val jest to zmienną tymczasowa i zostanie ona alokowana na stosie. Jeśli tablica val zostanie zdeklarowana jako static to taka operacja jak w funkcji powyżej się nie uda. Ponieważ tablica globalna musi być zainicjalizowana jako tablica o stałym rozmiarze.