Wskazniki w C

0

Witam. Mam problem, moim zadaniem jest przeanalizowanie, wyniku tego programu: 256 4

#include <stdio.h>
#include <conio.h>



int main()
{

int i;
unsigned char tab[20]={0,1,2,3,4,0,1,2,3,4,0,1,2,3,4,0,1,2,3,4};
unsigned short *wsk=tab;

printf("%d\n",*wsk);
wsk+=2;
printf("%d\n",*wsk);
getche();
}

Lecz troche tego nie rozumiem, nie wiem skąd ten wynik 256 4. Czy ktoś mógłby mi to normalnie wytłumaczyć?

2

Masz tu UB, dowolny wynik jest możliwy.

0
kq napisał(a):

Masz tu UB, dowolny wynik jest możliwy.

No tak, ale dlaczego akurat taki wynik? Wykładowca coś wspominał że jak mamy int to 'skaczemy' po tablicy o 4 pozycje, czy coś w tym stylu, oraz o dodawaniu potęg 256^0, 256^1 itp nie rozumiem tego

0

włącz w kompilatorze -Wall to wtedy się dowiesz.

4

No dobra, trochę lakonicznie się wypowiedziałem wcześniej - ale poprawnie. Ten kod to UB, i de facto w tym momencie powinna kończyć się jego analiza.


Niemniej jednak można poczynić pewne założenia co się dzieje w Twoim przypadku.

Założenia:

  1. tab jest poprawnie zalignowane z short
  2. Masz system oparty na little-endian (wedle niektórych "uporządkowanie grubokońcówkowe" :​D)
  3. Kompilator nie korzysta ze strict aliasingu (-fno-strict-aliasing)
  4. Ten kod nie zawiera UB ;​)
  5. Bajt = 8 bitów
  6. sizeof(short) = sizeof(char) * 2

W tym momencie weźmy pod uwagę pierwsze sześć wartości z tablicy tab (pozostałe nie mają znaczenia):

0,1,2,3,4,0
tab jest wskaźnikiem (w uproszczeniu) na początek tablicy. Wskazuje więc na pierwsze zero:

0,1,2,3,4,0
^^

Rzutujesz teraz ten wskaźnik na short (btw, to jest nielegalne rzutowanie w C++ - musisz rzutować jawnie), a on dalej wskazuje na tę samą lokację w pamięci, ale jako short:

0,1,2,3,4,0
^^^^

Wskazujesz więc na liczby 0, 1. Teraz, wiedząc, że bajt ma 8 bitów, są to kolejno 000000002 i 000000012, a wiedząc, że używamy kolejności cyfr little endian (czyli odwrotnie niż ludzie), dwubajtowa wartość short jest rozumiana jako 00000001000000002, czyli 25610

Teraz wróćmy do arytmetyki wskaźników: w C jest to jedna z nielicznych rzeczy, w których język idzie "na rękę" programiście. Inkrementacja lub dodanie wartości do wskaźnika zwiększa bezwzględną wartość komórki pamięci o wielkość typu, na który wskazuje. Więc tab+1 wskaże na adres o 1 bajt dalej, a wsk+1 wskaże na adres o 2 bajty dalej (pod warunkiem, że założenia wyżej są prawdziwe):
tab+1:

0,1,2,3,4,0
  ^^

wsk+1:

0,1,2,3,4,0
    ^^^^

Wobec tego, wsk += 2 oznacza, że wsk wskazuje na piąty i szósty bajt tablicy tab:

0,1,2,3,4,0
        ^^^^

Analogicznie do poprzedniego przypadku, 4 i 0 to kolejno 000001002 i 000000002, czyli jako short 00000000000001002, czyli 410

0

Po pierwsze zobacz sobie co da Ci taki wiersz: printf("%d %d\n", sizeof(unsigned char), sizeof(unsigned short));.
Od razu dowiesz się, że typy danych nie są równe. Wskaźnik masz na unsigned short, którego rozmiar to dwa bajty. Rozmiar unsigned char to jeden bajt.

Żeby same tylko wyniki były prawidłowe to trzeba by zrobić tak:

#include <stdio.h>
 
int main()
{
	int i;
	unsigned char tab[20]={0,1,2,3,4,0,1,2,3,4,0,1,2,3,4,0,1,2,3,4};
	unsigned short *wsk=tab;
	
	printf("%d\n",(unsigned char)*wsk);
	wsk+=2;
	wsk-=1;
	printf("%d\n",(unsigned char)*wsk);
}

Jeżeli chcesz pokazać dwójkę w tablicy to trzeba cofnąć się o tę różnicę ale należy brać pod uwagę to, że powyższy kod jest błędny.

https://ideone.com/htAO4q

0

Bardzo dziękuje, na wykładzie nie mogłem tego zrozumieć ponieważ wykładowca podał przykład i wynik działania a teraz już wiem o co chodzi :) A jeszcze jedno pytanie, gdybym miał tablice jako int, a wskaźnik jako char? Ponieważ w tym przypadku int jest większy od chara, co w takim przypadku?

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