tableau de longueur fixe typedef

210

Je dois définir un type de données 24 bits que j'utilise char[3]pour représenter le type. Puis - je typedef char[3]à type24? Je l'ai essayé dans un exemple de code. J'ai mis typedef char[3] type24;mon fichier d'en-tête. Le compilateur ne s'en est pas plaint. Mais quand j'ai défini une fonction void foo(type24 val) {}dans mon fichier C, il s'est plaint. J'aimerais pouvoir définir des fonctions comme type24_to_int32(type24 val)au lieu de type24_to_int32(char value[3]).

341008
la source

Réponses:

320

Le typedef serait

typedef char type24[3];

Cependant, c'est probablement une très mauvaise idée, car le type résultant est un type de tableau, mais les utilisateurs de celui-ci ne verront pas qu'il s'agit d'un type de tableau. S'il est utilisé comme argument de fonction, il sera transmis par référence, pas par valeur, et le sizeoffor sera alors incorrect.

Une meilleure solution serait

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

Vous souhaiterez probablement également utiliser unsigned charau lieu de char, car ce dernier a une signature définie par l'implémentation.

R .. GitHub ARRÊTEZ D'AIDER LA GLACE
la source
10
Existe-t-il un bon document décrivant les cas d'angle impliqués dans le passage de tableaux typés comme paramètres? Par exemple, si une fonction prend un paramètre type24 foo, quelles seraient les tailles, les types et les significations foo, *foo, **foo, &fooet &&foo? La signification et la légalité de telles expressions ont-elles changé au fil des ans?
supercat
4
Il vaut probablement la peine de mentionner la mise en garde de la compression de la structure, car un type de données 24 bits pourrait être destiné à correspondre à quelque chose avec une sémantique de compression définie différemment comme les données d'image RVB.
sh1
2
@ sh1: Sur tous les ABI du monde réel modernes que je connais - même ceux où un accès mal aligné est très cher - les structures n'ont pas des exigences d'alignement plus fortes que leurs membres n'auraient sans la structure. Bien sûr, OP ou toute autre personne utilisant cette approche doit vérifier ma réclamation si cela va avoir une incidence sur le comportement et la portabilité de leur programme.
R .. GitHub STOP HELPING ICE
4
@R .. Une partie de cela est trompeuse - en C, les tableaux sont toujours passés par référence, c'est-à-dire que si vous modifiez le tableau passé en argument à une fonction, vous le faites globalement, pas seulement dans le contexte de la fonction. Cela étant dit, on pourrait également faire valoir que dans les tableaux C sont toujours passés par valeur puisque nous passons simplement l'adresse du premier élément, qui est copiée sur la pile sur la pile appelée. Dans les deux cas, cependant, la réponse est trompeuse.
baibo
1
@bobbogo: Ce n'est pas de l'emballage, c'est juste de la non-insertion de rembourrage gratuit. L'alignement de la structure est simplement l'alignement maximum de l'un de ses membres, qui ont tous l'alignement 1.
R .. GitHub STOP HELPING ICE
49

Tu veux

typedef char type24[3];

Les déclarations de type C sont étranges de cette façon. Vous placez le type exactement où irait le nom de la variable si vous déclariez une variable de ce type.

ysth
la source
33

De la réponse de R .. :

Cependant, c'est probablement une très mauvaise idée, car le type résultant est un type de tableau, mais les utilisateurs de celui-ci ne verront pas qu'il s'agit d'un type de tableau. S'il est utilisé comme argument de fonction, il sera transmis par référence, et non par valeur, et sa taille sera alors incorrecte.

Les utilisateurs qui ne voient pas qu'il s'agit d'un tableau écriront très probablement quelque chose comme ça (qui échoue):

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

Il se compilera avec les avertissements suivants:

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

Et produit la sortie suivante:

0
1
-453308976
32767
Gerhard Burger
la source
13

Les tableaux ne peuvent pas être passés en tant que paramètres de fonction par valeur en C.

Vous pouvez mettre le tableau dans une structure:

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

puis passez cela par valeur, mais bien sûr, c'est moins pratique à utiliser: x.byte[0]au lieu de x[0].

Votre fonction type24_to_int32(char value[3])passe en fait par un pointeur et non par une valeur. Il est exactement équivalent à type24_to_int32(char *value)et 3est ignoré.

Si vous êtes heureux de passer par un pointeur, vous pouvez vous en tenir au tableau et faire:

type24_to_int32(const type24 *value);

Cela passera un pointeur vers un tableau, pas un pointeur vers le premier élément, vous l'utilisez donc comme:

(*value)[0]

Je ne suis pas sûr que ce soit vraiment un gain, car si vous écrivez accidentellement, value[1]quelque chose de stupide se produit.

Steve Jessop
la source
2
Je pense que cette réponse pourrait être améliorée en mentionnant le terme decayquelque part (et peut-être en soulignant que la situation est pire pour les tableaux de retour - ce qui ne fonctionne pas du tout).
Frerich Raabe
9

Pour utiliser correctement le type de tableau en tant qu'argument de fonction ou paramètre de modèle, créez une structure au lieu d'un typedef, puis ajoutez un operator[]à la structure afin de pouvoir conserver la fonctionnalité de type tableau comme ceci:

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

type24 x;
x[2] = 'r';
char c = x[2];
Geronimo
la source
14
C'est une question C, pas C ++. Ni char&ne operator[]sont des choses qui existent dans C.
Michael Morris
3

Voici un bref exemple des raisons pour lesquelles le tableau typedef peut être source de confusion. Les autres réponses fournissent une solution de contournement.

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

Cela produit la sortie

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

car type24 comme paramètre est un pointeur. (En C, les tableaux sont toujours transmis comme pointeurs.) Heureusement, le compilateur gcc8 émet un avertissement par défaut.

Daniel
la source