2013-04-25 15 views
8

Oto makro uzyskania rozmiar tablicyC++ sens coraz wielkości tablicy

#define array_size(array) \ 
(sizeof(array)/(sizeof(array[0]) * (sizeof(array) != sizeof(void*) || sizeof(array[0]) <= sizeof(void*))) 

myślę normalnie (sizeof (array)/(sizeof (array [0])) jest wystarczająco dobry, aby dostać wielkość tablicy.

Chyba part

(sizeof(array[0]) * (sizeof(array) != sizeof(void*) || sizeof(array[0]) <= sizeof(void*)) 

jest uniknięcie całość podzieloną przez zero, ktoś może pomóc wyjaśnić?

Z góry dziękuję.

zdrowie,

+0

przypuszczam, że jest to, aby zapobiec przypadkowemu nadużyciom ze zbutwiałych wskaźnik do pierwszego elementu w tej sprawie, a platformy -zależnie, miałoby to wartość '0' ... –

+0

sizeof nigdy nie wróci ze ro. – john

+6

"należy unikać całej rzeczy podzielonej przez zero" Przeciwnie, wymusić będzie dzielenie przez zero w czasie kompilacji, gdy 'sizeof array == sizeof (void *)' i 'sizeof array [0]> sizeof (void *) '. –

Odpowiedz

12

Mnożenie sizeof array[0] w dzielnik przez

(sizeof(array) != sizeof(void*) || sizeof(array[0]) <= sizeof(void*)) 

sprawia, że ​​dzielnik zerem

sizeof array == sizeof(void*) 

i

sizeof array[0] > sizeof(void*) 

W tych przypadkach podczas kompilacji uzyskuje się dzielenie przez zero, co może spowodować niepowodzenie kompilacji.

Te sprawdzenia są próbą wykrycia argumentów będących wskaźnikami (niezależnie od tego, czy są wynikiem konwersji między tablicami, czy nie), ponieważ nie można się dowiedzieć, jak dużą "tablicę" wskazuje wskaźnik za pomocą tej iloraz.

Nie powiedzie się, jeśli inne typy wskaźników mają różne rozmiary niż void* i nie wykrywa wskaźników do rzeczy, które nie są większe niż void* s. Prawdopodobnie wyrządza więcej szkody niż pożytku, kołysając autora w fałszywym poczuciu bezpieczeństwa.

+0

I oczywiście posiadanie tablicy wskaźnika jest całkowicie legalne i to makro zawiedzie na nim ... –

1

Przypuszczam, ta część jest jasna: sizeof(array)/sizeof(array[0])

Ta część (sizeof(array) != sizeof(void*) || sizeof(array[0]) <= sizeof(void*)) to wyrażenie logiczne, tak uzyskując prawdziwe lub fałszywe. Przy obliczaniu cały wyraz, więc mnożąc sizeof(array[0]) z logicznej wypowiedzi, kompilator konwertuje wyrażenie logiczne 0 lub 1. Tak więc skończyć z
sizeof(array)/(sizeof(array[0]) * 1) lub
sizeof(array)/(sizeof(array[0]) * 0).

Pierwszy przypadek to normalny i pożądany przypadek. Drugi przypadek spowoduje błąd kompilatora z powodu dzielenia przez zero. Więc kod nie będzie kompilować jeśli zadzwonisz na przykład:

long *p; // assuming a platform with sizeof(long)==sizeof(void*) 
array_size(p); 

Ale to nie złapie błędy jak:

char *p; 
array_size(p); 

I to nie skompilować dla przypadku I'ld chcesz go skompilować dla:

long x[1]; // assuming a platform with sizeof(long)==sizeof(void*) 
array_size(x); 

BTW, jeśli deklarują tę funkcję jako makro (w C++ I'ld naprawdę wolą rozwiązanie szablon stylu), należy zmienić wszystkie array w makro do (array).

6

myślę normalnie (sizeof (array)/(sizeof (array [0])) jest wystarczająco dobry, aby uzyskać rozmiaru tablicy.

Chociaż nie jest to podstawowa kwestia, ale wspomniałeś to poprawny sposób, aby określić rozmiar tablicy w C++ jest przy użyciu szablonów..

template<typename T, size_t size> 
constexpr size_t array_size(T(&)[size]){ 
    return size; 
} 
+0

Dodatkowa premia: znacznie wyraźniejszy komunikat o błędzie, gdy argument nie jest tablicą. Jednak powinno to być 'constexpr'. – MSalters

+0

Tak, najlepiej powinien to być 'constexpr'. Niestety wiele obecnie używanych kompilatorów nie obsługuje tego, dlatego go pominąłem. –

Powiązane problemy