typedef tablica o stałej długości

210

Muszę zdefiniować 24-bitowy typ danych. Używam char[3]do reprezentowania typu. Czy mogę napisać char[3]na maszynie type24? Próbowałem w próbce kodu. I umieścić typedef char[3] type24;w moim pliku nagłówka. Kompilator nie narzekał na to. Ale kiedy zdefiniowałem funkcję void foo(type24 val) {}w moim pliku C, narzekała. Chciałbym móc definiować funkcje takie jak type24_to_int32(type24 val)zamiast type24_to_int32(char value[3]).

341008
źródło

Odpowiedzi:

320

Typedef będzie

typedef char type24[3];

Jest to jednak prawdopodobnie bardzo zły pomysł, ponieważ typ wynikowy jest typem tablicy, ale użytkownicy tego nie zobaczą, że jest to typ tablicy. Jeśli zostanie użyty jako argument funkcji, zostanie przekazany przez odwołanie, a nie przez wartość, a wtedy sizeoffor będzie błędny.

Lepszym rozwiązaniem byłoby

typedef struct type24 { char x[3]; } type24;

Prawdopodobnie zechcesz również użyć unsigned charzamiast char, ponieważ ten ostatni ma podpisy zdefiniowane w implementacji.

R .. GitHub ZATRZYMAJ LOD
źródło
10
Czy jest jakiś fajny dokument, który opisuje parametry narożników związane z przekazywaniem tablic maszynowych jako parametrów? Na przykład, jeśli funkcja przyjmuje parametr type24 foo, co byłoby rozmiary, typy i znaczeń foo, *foo, **foo, &foo, i &&foo? Czy znaczenia i legalność takich wyrażeń zmieniły się na przestrzeni lat?
supercat
4
Prawdopodobnie warto wspomnieć o zastrzeżeniu dotyczącym pakowania struktury, ponieważ 24-bitowy typ danych może być przeznaczony do mapowania na coś z inaczej zdefiniowaną semantyką upakowania, np. Dane obrazu RGB.
sh1
2
@ sh1: Na wszystkich współczesnych rzeczywistych ABI, o których wiem - nawet tych, w których nierównomierny dostęp jest bardzo kosztowny - struktury nie mają większych wymagań wyrównania niż ich członkowie bez tej struktury. Oczywiście OP lub ktokolwiek inny stosujący to podejście powinien zweryfikować moje roszczenie, czy będzie miało to znaczenie dla zachowania i przenośności ich programu.
R .. GitHub ZATRZYMAJ LÓD
4
@R .. Jedna z części jest myląca - w C tablice są zawsze przekazywane przez odwołanie, tzn. Jeśli zmodyfikujesz tablicę przekazaną jako argument funkcji, robisz to globalnie, nie tylko w kontekście funkcji. To powiedziawszy, można również argumentować, że w C tablice są zawsze przekazywane przez wartość, ponieważ po prostu przekazujemy adres pierwszego elementu, który jest kopiowany na stos na stosie odbiorczym. Jednak w obu przypadkach odpowiedź jest myląca.
baibo
1
@bobbogo: To nie jest pakowanie, to po prostu nie wstawianie bezpłatnego wypełnienia. Wyrównanie struktury jest tylko maksymalnym wyrównaniem dowolnego z jej elementów, z których wszystkie mają wyrównanie 1.
R .. GitHub ZATRZYMAJ LODĘ
49

Chcesz

typedef char type24[3];

Deklaracje typu C są w ten sposób dziwne. Umieszczasz typ dokładnie tam, gdzie pójdzie nazwa zmiennej, jeśli deklarujesz zmienną tego typu.

ysth
źródło
33

Od R .. 's odpowiedź :

Jest to jednak prawdopodobnie bardzo zły pomysł, ponieważ typ wynikowy jest typem tablicy, ale użytkownicy tego nie zobaczą, że jest to typ tablicy. Jeśli zostanie użyty jako argument funkcji, zostanie przekazany przez odwołanie, a nie przez wartość, a rozmiarof będzie wtedy niepoprawny.

Użytkownicy, którzy nie widzą, że jest to tablica, najprawdopodobniej napiszą coś takiego (co się nie powiedzie):

#include <stdio.h>

typedef int twoInts[2];

void print(twoInts *twoIntsPtr);
void intermediate (twoInts twoIntsAppearsByValue);

int main () {
    twoInts a;
    a[0] = 0;
    a[1] = 1;
    print(&a);
    intermediate(a);
    return 0;
}
void intermediate(twoInts b) {
    print(&b);
}

void print(twoInts *c){
    printf("%d\n%d\n", (*c)[0], (*c)[1]);
}

Skompiluje się z następującymi ostrzeżeniami:

In function intermediate’:
warning: passing argument 1 of print from incompatible pointer type [enabled by default]
    print(&b);
     ^
note: expected int (*)[2]’ but argument is of type int **’
    void print(twoInts *twoIntsPtr);
         ^

I produkuje następujące dane wyjściowe:

0
1
-453308976
32767
Gerhard Burger
źródło
13

Tablice nie mogą być przekazywane jako parametry funkcji przez wartość w C.

Możesz umieścić tablicę w strukturze:

typedef struct type24 {
    char byte[3];
} type24;

a następnie przekazać to przez wartość, ale oczywiście jest to mniej wygodne w użyciu: x.byte[0]zamiast x[0].

Twoja funkcja type24_to_int32(char value[3])faktycznie przekazuje wskaźnik, a nie wartość. Jest dokładnie równoważny type24_to_int32(char *value)i 3jest ignorowany.

Jeśli jesteś zadowolony przechodzącej przez wskaźnik, to mógł pozostać przy tablicy i zrobić:

type24_to_int32(const type24 *value);

Spowoduje to przekazanie wskaźnika do tablicy, a nie wskaźnika do pierwszego elementu, więc używasz go jako:

(*value)[0]

Nie jestem pewien, czy to naprawdę zysk, ponieważ jeśli przypadkowo napiszesz, value[1]dzieje się coś głupiego.

Steve Jessop
źródło
2
Myślę, że odpowiedź ta mogłaby zostać poprawiona przez wspomnienie decaygdzieś tego terminu (i może przez wskazanie, że sytuacja jest gorsza w przypadku zwracania tablic - co wcale nie działa).
Frerich Raabe
9

Aby poprawnie użyć typu tablicy jako argumentu funkcji lub parametru szablonu, utwórz strukturę zamiast typedef, a następnie dodaj operator[]do struktury, aby zachować funkcjonalność podobną do tablicy:

typedef struct type24 {
  char& operator[](int i) { return byte[i]; }
  char byte[3];
} type24;

type24 x;
x[2] = 'r';
char c = x[2];
Geronimo
źródło
14
To jest pytanie C, a nie C ++. Ani char&też nie operator[]są rzeczy, które istnieją w C.
Michael Morris
3

Oto krótki przykład, dlaczego tablica typedef może być myląco niespójna. Pozostałe odpowiedzi stanowią obejście.

#include <stdio.h>
typedef char type24[3];

int func(type24 a) {
        type24 b;
        printf("sizeof(a) is %zu\n",sizeof(a));
        printf("sizeof(b) is %zu\n",sizeof(b));
        return 0;
}

int main(void) {
        type24 a;
        return func(a);
}

To daje wynik

sizeof(a) is 8
sizeof(b) is 3

ponieważ type24 jako parametr jest wskaźnikiem. (W C tablice są zawsze przekazywane jako wskaźniki). Kompilator gcc8 domyślnie wyświetli ostrzeżenie, na szczęście.

Daniel
źródło