Przechodzę do zainteresowania zmienionym systemem znaków C99. To pytanie zostało zainspirowane przez this one.Kompatybilność ze zmienionymi typami i ich implikacje związane z bezpieczeństwem
Sprawdzając kod z tego pytania, odkryłem coś interesującego. Rozważ ten kod:
int myFunc(int, int, int, int[][100]);
int myFunc(int a, int b, int c, int d[][200]) {
/* Some code here... */
}
To oczywiście nie będzie (i nie jest) kompilować. Jednak ten kod:
int myFunc(int, int, int, int[][100]);
int myFunc(int a, int b, int c, int d[][c]) {
/* Some code here... */
}
kompiluje bez ostrzeżenia (na gcc).
Wydaje się to sugerować, że zmiennie zmodyfikowany typ tablicy jest kompatybilny z dowolnym nie zmodyfikowanym typem tablicy!
Ale to nie wszystko. Można by się spodziewać, że zmieniony typ będzie przynajmniej kłopotał się z użyciem zmiennej do ustawienia jej rozmiaru. Ale tak się nie wydaje!
int myFunc(int, int b, int, int[][b]);
int myFunc(int a, int b, int c, int d[][c]) {
return 0;
}
Kompiluje również bez żadnego błędu.
Moje pytanie brzmi: czy to prawidłowe ustandaryzowane zachowanie?
Ponadto, jeśli zmiennie zmodyfikowany typ tablicy byłby rzeczywiście zgodny z dowolną tablicą o tych samych wymiarach, czy nie oznaczałoby to nieprzyjemnych problemów z bezpieczeństwem? Na przykład rozważ następujący kod:
int myFunc(int a, int b, int c, int d[][c]) {
printf("%d\n", sizeof(*d)/sizeof((*d)[0]));
return 0;
}
int main(){
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
myFunc(0, 0, 100, &arr);
return 0;
}
Kompiluje i przekazuje 100, bez błędów i ostrzeżeń, nic. Jak widzę, oznacza to łatwe zapisywanie poza tablicą, nawet jeśli dokładnie sprawdzasz rozmiar tablicy poprzez sizeof
, nie wykonując pojedynczego rzutowania, a nawet wszystkie ostrzeżenia są włączone! Czy może czegoś brakuje?
Jeśli jeszcze tego nie zrobiłeś, spróbuj dodać -std = c99 -pedantic-errors do linii kompilacji gcc i sprawdź, czy to ma znaczenie. – jschultz410
@ jschultz410: dobry pomysł, ale nie - nie robi żadnej różnicy = ( – Mints97
Istnieje wiele przypadków, w których kompilator nie mógłby statycznie wywnioskować wartości c (np. - c jest wejściem ze stdin). często niemożliwe byłoby wykonanie jakiegokolwiek sensownego sprawdzania typu statycznego przy takich parametrach definicji funkcji. Wydaje się, że jeśli to zrobisz, kompilator mówi "OK, pozwolę ci przekazać cokolwiek chcesz jako d, więc o ile jego typem jest podwójnie indeksowana tablica int. Powodzenia! " – jschultz410