typedef Array mit fester Länge

209

Ich muss einen 24-Bit-Datentyp definieren. Ich verwende char[3], um den Typ darzustellen. Kann ich typedef char[3]zu type24? Ich habe es in einem Codebeispiel versucht. Ich habe typedef char[3] type24;meine Header-Datei eingefügt. Der Compiler hat sich nicht darüber beschwert. Aber als ich eine Funktion void foo(type24 val) {}in meiner C-Datei definiert habe, hat sie sich beschwert. Ich möchte in der Lage sein, Funktionen wie type24_to_int32(type24 val)anstelle von zu definieren type24_to_int32(char value[3]).

341008
quelle

Antworten:

319

Das typedef wäre

typedef char type24[3];

Dies ist jedoch wahrscheinlich eine sehr schlechte Idee, da der resultierende Typ ein Array-Typ ist, Benutzer jedoch nicht erkennen, dass es sich um einen Array-Typ handelt. Wenn es als Funktionsargument verwendet wird, wird es als Referenz und nicht als Wert übergeben, und das sizeofdafür ist dann falsch.

Eine bessere Lösung wäre

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

Sie möchten wahrscheinlich auch unsigned charanstelle von verwenden char, da letzteres eine implementierungsdefinierte Signatur aufweist.

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle
10
Gibt es ein schönes Dokument, das die Eckfälle beschreibt, die mit der Übergabe typisierter Arrays als Parameter verbunden sind? Zum Beispiel, wenn eine Funktion eines Parameter annimmt type24 foo, was würde die Größen, Typen und Bedeutungen von foo, *foo, **foo, &foo, und &&foo? Haben sich die Bedeutung und Rechtmäßigkeit solcher Ausdrücke im Laufe der Jahre geändert?
Supercat
4
Erwähnenswert ist wahrscheinlich die Einschränkung beim Packen von Strukturen, da ein 24-Bit-Datentyp möglicherweise dazu gedacht ist, etwas mit unterschiedlich definierter Packungssemantik wie RGB-Bilddaten abzubilden.
Sh1
2
@ sh1: Bei allen modernen ABIs in der realen Welt ist mir bekannt, dass Strukturen - auch bei solchen, bei denen ein falsch ausgerichteter Zugriff sehr teuer ist - keine höheren Ausrichtungsanforderungen haben als ihre Mitglieder ohne die Struktur. Natürlich sollte OP oder jeder andere, der diesen Ansatz verwendet, meine Behauptung überprüfen, ob dies für das Verhalten und die Portabilität seines Programms von Bedeutung ist.
R .. GitHub STOP HELPING ICE
4
@R .. Ein Teil davon ist irreführend - in C werden Arrays immer als Referenz übergeben. Wenn Sie also das als Argument an eine Funktion übergebene Array ändern, geschieht dies global, nicht nur im Kontext der Funktion. Vor diesem Hintergrund könnte man auch argumentieren, dass in C-Arrays immer Werte übergeben werden, da wir einfach die Adresse des ersten Elements übergeben, das auf den Stapel auf dem Angerufenen-Stapel kopiert wird. In beiden Fällen ist die Antwort jedoch irreführend.
Baibo
1
@bobbogo: Das ist kein Packen, es ist nur das Nichteinfügen von unentgeltlichen Polstern. Die Ausrichtung der Struktur ist nur die maximale Ausrichtung eines ihrer Mitglieder, die alle die Ausrichtung 1 haben.
R .. GitHub STOP HELPING ICE
49

Sie wollen

typedef char type24[3];

C-Typ-Deklarationen sind auf diese Weise seltsam. Sie setzen den Typ genau dort ein, wo der Variablenname hingehen würde, wenn Sie eine Variable dieses Typs deklarieren würden.

ysth
quelle
32

Aus der Antwort von R .. :

Dies ist jedoch wahrscheinlich eine sehr schlechte Idee, da der resultierende Typ ein Array-Typ ist, Benutzer jedoch nicht erkennen, dass es sich um einen Array-Typ handelt. Wenn es als Funktionsargument verwendet wird, wird es als Referenz und nicht als Wert übergeben, und die Größe dafür ist dann falsch.

Benutzer, die nicht sehen, dass es sich um ein Array handelt, schreiben höchstwahrscheinlich Folgendes (was fehlschlägt):

#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]);
}

Es wird mit den folgenden Warnungen kompiliert:

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);
         ^

Und erzeugt die folgende Ausgabe:

0
1
-453308976
32767
Gerhard Burger
quelle
13

Arrays können nicht als Funktionsparameter nach Wert in C übergeben werden.

Sie können das Array in eine Struktur einfügen:

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

und dann als Wert übergeben, aber dann ist es natürlich weniger bequem zu verwenden: x.byte[0]statt x[0].

Ihre Funktion wird type24_to_int32(char value[3])tatsächlich nach Zeiger und nicht nach Wert übergeben. Es ist genau gleichbedeutend mit type24_to_int32(char *value)und 3wird ignoriert.

Wenn Sie gerne am Zeiger vorbeikommen, können Sie sich an das Array halten und Folgendes tun:

type24_to_int32(const type24 *value);

Dadurch wird ein Zeiger auf ein Array übergeben, kein Zeiger auf das erste Element. Sie verwenden ihn also wie folgt:

(*value)[0]

Ich bin mir nicht sicher, ob das wirklich ein Gewinn ist, denn wenn Sie versehentlich schreiben value[1], passiert etwas Dummes.

Steve Jessop
quelle
2
Ich denke, diese Antwort könnte verbessert werden, indem man den Begriff decayirgendwo erwähnt (und vielleicht darauf hinweist, dass die Situation für die Rückgabe von Arrays schlimmer ist - was überhaupt nicht funktioniert).
Frerich Raabe
9

Um den Array-Typ ordnungsgemäß als Funktionsargument oder Vorlagenparameter zu verwenden, erstellen Sie eine Struktur anstelle eines typedef und fügen Sie operator[]der Struktur eine hinzu, damit Sie die Array-ähnliche Funktionalität wie folgt beibehalten können:

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

type24 x;
x[2] = 'r';
char c = x[2];
Geronimo
quelle
14
Dies ist eine C-Frage, nicht C ++. Weder char&noch operator[]sind Dinge, die in C.
Michael Morris
3

Hier ist ein kurzes Beispiel dafür, warum typedef-Arrays verwirrend inkonsistent sein können. Die anderen Antworten bieten eine Problemumgehung.

#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);
}

Dies erzeugt die Ausgabe

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

weil type24 als Parameter ein Zeiger ist. (In C werden Arrays immer als Zeiger übergeben.) Der gcc8-Compiler gibt zum Glück standardmäßig eine Warnung aus.

Daniel
quelle