wtorek, 2 maja 2017

C - Wskaźniki zastosowanie

W tym poście chciałbym przedstawić kilka krótki zastosowań wskaźników.

Poruszone zostaną następujące zagadnienia: przesłanie łańcucha znakowego do funkcji, przekazywanie wskaźnika do funkcji, przekazywanie tablicy oraz wskaźnika do tablicy do funkcji, zwracanie wskaźnika przez funkcje.

Łańcuch znaków do funkcji:


Przekazywanie łańcucha znakowego do funkcji, przepisywanie do do nowej tablicy oraz odwracanie podanego ciągu:

  1. #include <stdio.h>
  2. #include <stdint.h>
  3. int main(void)
  4. {
  5.     void copyTwoStrings(uint8_t *toCopyTo, uint8_t *toCopyFrom);
  6.     void reverseTwoStrings(uint8_t *stringToReverse, uint8_t *reversedString);
  7.     uint8_t charTable[] = "abcdefghijklmnoprst";
  8.     uint8_t charTable2[50];
  9.     copyTwoStrings(charTable2, charTable);
  10.     printf("Kopiowanie: %s\n", charTable2);
  11.     copyTwoStrings(charTable2, (uint8_t*)"Znaki do skopiowania");
  12.     printf("Kopiowanie: %s\n", charTable2);
  13.     reverseTwoStrings(charTable2, charTable);
  14.     printf("Odwrocenie: %s\n", charTable2);
  15.     return 0;
  16. }
  17. void copyTwoStrings(uint8_t *toCopyTo, uint8_t *toCopyFrom)
  18. {
  19.     for ( ; *toCopyFrom != '\0'; toCopyFrom++, toCopyTo++ )
  20.     {
  21.             *toCopyTo = *toCopyFrom;
  22.     }
  23.     *toCopyTo = '\0';
  24. }
  25. void reverseTwoStrings(uint8_t *stringToReverse, uint8_t *reversedString)
  26. {
  27.     uint8_t length = strlen(stringToReverse)-1;
  28.     for ( ; *stringToReverse != '\0'; length--, stringToReverse++)
  29.     {
  30.             *stringToReverse = *(reversedString+length);
  31.     }
  32.     *reversedString = '\0';
  33. }

Funkcje zdeklarowane w programie posiadają dwa argumenty zdeklarowane jako wskaźniki. W pętlach for obu funkcji następuje przechodzenie po poszczególnych elementach. W pierwszej funkcji znaki są kopiowane do nowej tablicy, w drugiej następuje przechodzenie pomiędzy elementami najniższymi jednego ciągu, do których następuje przypisanie wartości od największej ciągu drugiego. Wyjście z pętli następuje po napotkaniu znaku null. Do nowego ciągu znaków jest on następnie dodawany po wyjściu z pętli.

W języku c deklarując łańcuch znaków w wywołaniu funkcji nie otrzymuje się konkretnie tego łańcucha tylko wskaźnik na niego.

Przekazywanie wskaźników do funkcji:


Poniżej krótki program, który będzie przekazywał wskaźnik do zdefiniowanej wartości do funkcji. W niej nastąpi modyfikacja tej wartości:

  1. #include <stdio.h>
  2. #include <stdint.h>
  3. void passPointerToFunction(uint8_t *pointerToValue)
  4. {
  5.     *pointerToValue +=50;   //Modyfikacja przekazanej wartości
  6. }
  7. int main (void)
  8. {
  9.     void test (uint8_t *pointerToValue);
  10.     uint8_t  value = 50;
  11.     uint8_t *pointer = &value;
  12.     printf("Przed funckja value = %u\n", value);
  13.     passPointerToFunction(pointer);
  14.     printf("Po pierwszym wywołaniu value = %u\n", value);
  15.     passPointerToFunction(&value);
  16.     printf("Po drugim wywołaniu value = %u\n", value);
  17.     return 0;
  18. }

Do funkcji przekazywany jest wskaźnik przez jego osobne zdeklarowanie bądź przez przekazanie adresu bezpośrednio do funkcji.

Przekazywanie tablicy do funkcji:


Poniżej przedstawię dwie funkcje których zadaniem będzie sumowanie elementów zdeklarowanych w tablicy. Jedna z nich będzie przekazywała tablicę, druga natomiast wskaźnik na tą tablicę. Obie będzie operowały na wskaźnikach:

  1. #include <stdio.h>
  2. #include <stdint.h>
  3. uint8_t sumPassArray(uint8_t arrayOfNumbers[], const uint8_t endNumber)
  4. {
  5.     uint8_t  sum = 0;        //Suma
  6.     uint8_t  *tmpPointer;    //Wskaznik
  7.     uint8_t  * const endOfArray = arrayOfNumbers + endNumber;  //Długość tablicy
  8.     for(tmpPointer=arrayOfNumbers; tmpPointer<endOfArray; tmpPointer++) //Przejście po każdym elemencie, zwiększanie wskaźnika
  9.     {
  10.         sum += *tmpPointer;
  11.     }
  12.     return sum;
  13. }
  14. uint8_t sumPassArrayPointer(uint8_t *arrayOfNumbers, const uint8_t endNumber)
  15. {
  16.     uint8_t  sum = 0;
  17.     uint8_t  * const endOfArray = arrayOfNumbers + endNumber;
  18.     for (; arrayOfNumbers<endOfArray; ++arrayOfNumbers)
  19.     {
  20.         sum += *arrayOfNumbers;
  21.     }
  22.     return sum;
  23. }
  24. int main (void)
  25. {
  26.     uint8_t  sumPassArray(uint8_t arrayOfNumbers[], const uint8_t n);
  27.     uint8_t  sumPassArrayPointer(uint8_t *array, const uint8_t n);
  28.     uint8_t  valuesToPass[15] = {3, 7, 5, 3, 6, 1, 7, 9, 1, 0, 20, 3, 94, 56, 22};
  29.     uint8_t len = sizeof(valuesToPass) / sizeof(valuesToPass[0]);
  30.     printf("Wielkość tablicy: %u\n", len);
  31.     printf("Suma valuesToPass: %u\n", sumPassArray(valuesToPass, len));
  32.     printf("Suma sumPassArrayPointer: %u\n", sumPassArrayPointer(valuesToPass, len));
  33.     return 0;
  34. }

Powyżej przygotowane są dwie funkcje, których zadaniem jest zsumowanie wartości podanych w tablicy. Do funkcji przekazywana jest jej długość oraz sama tablica. W pierwszej funkcji tablica jest przypisana do wskaźnika, po czym na tym wskaźniku wykonywane są operacje, a dokładniej przechodzenie do następnego elementu poprzez zwiększenie wskaźnika. Jest to rozwiązanie szybsze niż przechodzenie po poszczególnych elementach tablicy bezpośrednio.

Druga funkcja też otrzymuje tablice, tylko tym razem rozpatrywane jest to jako sam wskaźnik, dzięki temu pomija się deklarację dodatkowego wskaźnika.

Zwracanie wskaźnika przez funkcje:


Zwracanie wskaźnika następuje w normalny sposób tak jak przy zwracaniu normalnej wartości:

  1. #include <stdio.h>
  2. #include <stdint.h>
  3. uint8_t passPointerToFunction(uint8_t *pointerToValue)
  4. {
  5.     *pointerToValue +=50;   //Modyfikacja przekazanej wartości
  6.     return *pointerToValue;
  7. }
  8. int main (void)
  9. {
  10.     void test (uint8_t *pointerToValue);
  11.     uint8_t  value = 50;
  12.     uint8_t returnValue = 0;
  13.     uint8_t *pointer = &value;
  14.     printf("Przed funckja value = %u\n", value);
  15.     returnValue = passPointerToFunction(pointer);
  16.     printf("returnValue = %u, value %u\n", returnValue, value);
  17.     return 0;
  18. }

Można to wykonać także przez deklarację wskaźnika lokalnie w funkcji. Należy przy tym pamiętać, że ta zmienna jest tracona, chyba, że zostanie zdefiniowana jako statyczna