Jest tak jak _13th_Dragon napisał w komentarzu, czyli:
Jeśli masz wsk =& tab[0][0]; (zakładam że wsk to int*), i robisz wsk += 2, to trafisz nie na tab[2][0], a na tab[0][2].
Jeśli chcesz przesunąć wskaźnik na elementu [2][0], to musisz zrobić wsk += 2 * ilość_kolumn (np. dla int tab[3][5] będzie to wsk += 2 * 5).
Dodam, że standardy C/C++ gwarantują, że elementy w tablicy są po sobie (a więc że kolejne wiersze w tablicy dwówymiarowej również są po sobie), więc takie przeskakiwanie między wierszami jest jak najbardziej legalne.
(dla pewności, zazwyczaj się mówi, że jest tak: tab[numer_wiersza][numer_elementa_w_wierszu_ew_czasem_mowi_sie_na_to_numer_kolumny])
Ważny wzór do zapamiętania (baardzo się przydaje przy pracy na bitmapach / tablicach 2D i wskaźnikach na elementy będące w takich tablicach):
index_w_tablicy_1D = x + y * ilość_elementów_w_wierszu_tablicy_2D; // dla tab2d[y][x] oczywiśćie
Natomiast, jeszcze jedna uwaga - jeśli masz wsk który jest int*, to przy arytmetyce na pointerach nie używa się sizeof(typ) - C/C++ się same tym zajmują.
Jedynym wyjątkiem (w granicach standardu) jest jeśli wsk byłby pointerem na unsigned char (czyli de facto na bajt) - wtedy chcąc przeskoczyć na kolejne całkowite elementy pierwotnej tablicy (która ma inny typ - tak jak tutaj, gdzie pierwotna tablica to int[5][3]) musisz ofc pomnożyć numer elementu (lub przesunięcie) przez wielkość pojedynczego elementu faktycznej tablicy (wyrażoną w bajtach).
Czyli (na przykładzie):
int tab[3][5] = {0};
int *wsk = &tab[0][0];
unsigned char *wskb = (unsigned char*)&tab[0][0];
wsk += 2 * 5 + 1; // wskaż na element tab[2][1]; kompilator wie, że typem docelowym jest int, więc pomnoży tą wielkość przez sizeof(int)
ale
wskb += (2 * 5 + 1) * sizeof(int); // kompilator wie jedynie, że typem docelowym jest unsigned char, wiec sam musisz pomnożyć przez sizeof(int) - czyli ilość bajtów przypadającą na element faktycznego typu docelowego
I jeszcze jedna sprawa - to co Wojtek79 napisał - wsk[2][0] = ... - niestety nie zadziała dla wsk będącego int. Żeby coś takiego obliczyć kompilator musiałby znać ilość elementów w wierszu, a na podstawie typu tego nie wie.
Oczywiście, jeśli ilość elementów w wierszu jest stałą, możesz stworzyć wskaźnik na "tablicę z N intów w wierszu", np.:
#include <stdio.h>
int
main(void) {
int tab[3][5] = {
{ 0, 1, 2, 3, 4 },
{ 10,11,12,13,14 },
{ 20,21,22,23,24 }
};
int (*wsk)[5] = &tab[0];
printf("1,3 == %u", wsk[1][3]);
return 0;
}
W tym przykładzie typem wsk jest int(*)[5] (wskaźnik na tablicę intów z 5cioma elementami w wierszu), więc kompilator może sobie spokojnie wyliczyć docelowy adres, bo ma wszystkie potrzebne informacje.
Edit: trochę rozszerzyłem wyjaśnienie wyżej - _13th_Dragon zasugerował, że, mimo przykładu, nie wszystko może być dla wszystkich jasne ;)