sizeof style: sizeof (typ) czy sizeof zmienna?

22

Widziałem dwa style używania sizeofdo operacji związanych z pamięcią (takie jak w memsetlub malloc):

  • sizeof(type), i
  • sizeof variable lub sizeof(variable)

Który wolisz, czy użyłbyś kombinacji dwóch stylów i kiedy używałbyś każdego z nich? Jakie są zalety i wady każdego stylu i kiedy go używasz?

Jako przykład widzę następującą parę sytuacji, w których jeden styl pomaga, a drugi nie:

Kiedy pomylisz wskaźnik pośredni wskaźnika:

type *var;
...
memset(var, 0, sizeof var);    /* oops */

Kiedy typ się zmienia:

new_type var;    /* changed from old_type to new_type */
...
memset(&var, 0, sizeof(old_type));    /* oops */
congusbongus
źródło

Odpowiedzi:

30

I perfer sizeof(variable)powyżej sizeof(type). Rozważać:

int a1;
float a2;
memset(&a1,0,sizeof(a1));
memset(&a2,0,sizeof(a2));

vs.

int a1;
float a2;
memset(&a1,0,sizeof(int));
memset(&a2,0,sizeof(float));

W pierwszym przypadku łatwo jest sprawdzić, czy przekazywane są odpowiednie rozmiary memset. W drugim przypadku musisz stale sprawdzać górną i dolną sekcję, aby upewnić się, że jesteś spójny.

vy32
źródło
4
Ponadto, jeśli zmienisz typ zmiennej, kod, który zakłada, że ​​typ będzie musiał się zmienić. Myślę, że lepiej jest poprawiać kod przy jak najmniejszej zależności od typu. (W przeszłości wpływał na mnie duży projekt konwersji 16 do 32 bitów.)
Gort the Robot
Jest to preferowane do pracy z tablicami C, w których tworzenie typedef nie jest powszechne, ale takie rzeczy jak tablica char [MAX_SIZE];
mattnz
@ vy32: Nieprawidłowy 1: „sizeof (tablica) NIE zwraca rozmiaru tablicy ... rozmiar wskaźnika do tablicy” - jeśli arrayrzeczywiście jest tablicą C, to sizeof(array)zwraca liczbę bajtów wymaganą do przechowywania elementów tablicy . Jeśli arrayjest wskaźnikiem do pierwszego elementu zepsutej tablicy C, oznacza to, że instrukcja jest przechowywana. Przekazanie tablicy C jako argumentu funkcji powoduje, że wygląda ona jak wskaźnik w funkcji - wszystkie informacje potwierdzające, że była to tablica, podobnie jak jej rozmiar, są tracone. Właśnie dlatego się rozkłada . Wrong 2: „Tablice w rzeczywistości nie istnieją w C; ... po prostu cukier składniowy dla wskaźników”.
Johann Gerell
9

Preferowane jest (jak zawsze) odzwierciedlenie twojego zamiaru tak bezpośrednio, jak to możliwe.

Czy zamiar działa w oparciu o pamięć istniejącej zmiennej? Jeśli tak, to użyj sizeof(variable), ponieważ pokazuje to tak dokładnie, jak to możliwe, że zależy ci na pamięci samej zmiennej.

Czy zamierzasz wykonać jakieś obliczenia na typie, na przykład w celu ustalenia, ile pamięci należy przydzielić dla nowej instancji? Jeśli tak, użyj sizeof(type).

To znaczy wolę

struct foo *bar;
bar = (struct foo *) malloc(sizeof(struct foo));

koniec

bar = (struct foo *) malloc(sizeof(*bar));

ponieważ ten drugi przypadek wygląda na to, że próbujesz uzyskać dostęp do zmiennej, która jeszcze nie istnieje.

Z drugiej strony wolę

char Buffer[256];
memset(Buffer, 0, sizeof(Buffer));

koniec

char Buffer[256];
memset(Buffer, 0, 256 * sizeof(char));

ponieważ intencją jest oczywiście wypełnienie zera zawartości zmiennej, więc jest to zmienna, przeciwko której powinniśmy działać. Próba użycia metadanych typu po prostu dezorientuje, tutaj.

Aidan Cully
źródło
Zawsze tak robię, ale to kwestia osobistego gustu. Uważam, że void *są one automatycznie rzutowane na inne typy wskaźników, co jest błędne.
Aidan Cully
3

Wolałbym znacznie sizeof(type)więcej sizeof variable. Mimo, że zmusza do bardziej martwić o rodzaju i dokonać więcej zmian, to pomoże Ci uniknąć błędów wskaźnik zadnie, które zaliczają się do najczęstszych przyczyn błędów w C .

Nie martwiłbym się tak bardzo błędami, w których sizeof(type)zmienia się typ ; za każdym razem, gdy zmieniasz typ zmiennej, powinieneś wykonać szybkie skanowanie, aby zobaczyć, gdzie ta zmienna jest używana i czy musisz zmienić dowolne sizeof(type)instrukcje. Mimo że zawsze istnieje szansa, że ​​to się nie powiedzie, ryzyko jest znacznie mniejsze niż w przypadku błędów pośrednich wskaźnika.

Jedną z zalet sizeof variablejest dla tablic, ale w praktyce odkryłem, że przekazywanie tablic jako par pierwsza / len jest znacznie bardziej powszechne (w takim przypadku wystarczy użyć długości), a także bardzo łatwo jest pomylić rozmiar z powodu zanik tablicy / wskaźnika, jak w tym przykładzie:

ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) {
  memcpy( mat, src, sizeof( src ) );    /* NOT the same as 3*3*sizeof(float) */
}
congusbongus
źródło
2
„Szybki skan, aby zobaczyć, gdzie ta zmienna jest używana… zawsze istnieje szansa, że ​​to źle” - dla mnie jest to największe niebezpieczeństwo wykonywania sizeof (type), dlatego zawsze robię sizeof (zmienny). Ponieważ efektem końcowym tego niebezpieczeństwa jest to, że wszystko może się jeszcze kompilować, a nawet jeśli popełnisz jeden błąd, może potrwać ponad rok lub losowe awarie i uszkodzenia bufora, zanim go złapiesz. I nie mogłem nawiązać połączenia (oczywiście moje okno czasowe na pytanie wynosi około 30 sekund) między tym pytaniem a „błędami pośredniej wskazówki”.
DXM
@DXM W przypadku argumentu sizeof, jeśli jest to wartość, to jest sizeof var; jeśli to wskaźnik, to sizeof *var. Jest to również coś, co ludzie mogą pomylić, a kompilator chętnie to zrobi bez narzekań. Twierdzę, że te błędy są znacznie trudniejsze do wykrycia i są częstsze niż błędy w sizeof(type)formularzu.
congusbongus
1
Twoje oba prawa - typy C i rozmiar operatora są zasadniczo zepsute i nigdy nie będą niezawodne, nic, co robisz jako programista, nie może tego naprawić.
mattnz
Gdy post jest oznaczony C, kod C jest bardziej pouczający niż kod C ++ mat3_t::mat3_t(...).
chux - Przywróć Monikę
0

Celem jest usunięcie nadmiarowości. Jeśli użyjesz sizeof do operacji związanej ze zmienną, to oczywiście odzwierciedlisz to w sizeof. Tak, możesz zepsuć się jak w pierwszym przykładzie, ale lekarstwem nie jest użycie typu, ale * var, poprawnie dopasowane do celu.

Aby złagodzić takie problemy, zwykle używa się makr, szablonów i podobnych narzędzi przeszkolonych w zakresie użycia, a nie nagich rozmiarów.

Zobacz moje makro CLEAR, które jest używane wyłącznie zamiast nagiego memsetu. Zapisałem mój tyłek kilka razy w przypadku literówki pośredniej lub gdy zawartość strukturalna pobierała wektor lub ciąg ...

Balog Pal
źródło
Makra takie jak te, które nadały programowaniu makra C złe imię ......
mattnz
@mattnz: pokaż lepszą alternatywę. O wiele łatwiej jest rozmawiać o abstrakcyjnych rzeczach niż tworzyć faktycznie działające programy
Balog Pal
1
Połączone makro to C ++, a ten post jest oznaczony jako „C” (są to różne języki), Ada byłaby moją sugestią.
mattnz
@mattnz: i ten sam temat pokazuje, jak egzekwować w podobny sposób dla C. Wygląda na to, że zupełnie nie rozumiesz, że nie jest to rzeczywista implementacja treści, ale bezpieczniejsze użycie w porównaniu do surowych rzeczy. Btw również czytelne.
Balog Pal
0

Logika biznesowa określa twój wybór.

  1. Jeśli twój kod odnosi się do konkretnej zmiennej i bez tej zmiennej twój kod nie ma sensu - wybierz sizeof(var)

  2. Jeśli kodujesz dotyczy zestawu zmiennych określonego typu - wybierz sizeof(type). Zwykle jest to potrzebne, jeśli masz typedefdefinicję wielu zmiennych, które przetwarzasz inaczej w zależności od ich typu (np. Serializacja). Możesz nie wiedzieć, która z tych zmiennych pozostanie w przyszłych wersjach kodu, więc wybór typu jako argumentu jest logicznie poprawny. Nawet ich zmiana typedefnie wpłynie na rozmiar linii.

Ryga
źródło
-1

W zależności od celu rozmiar (zmienny) może być najlepszym rozwiązaniem. W ten sposób możesz zmienić typ zmiennej, jeśli to konieczne, bez poprawiania wszystkich pozycji dla tego typu. Ale znowu zależy to od twojego celu.

Pedro Perez
źródło