matriz de comprimento fixo typedef

210

Eu tenho que definir um tipo de dados de 24 bits. Estou usando char[3]para representar o tipo. Posso typedef char[3]para type24? Eu tentei em um exemplo de código. Eu coloquei typedef char[3] type24;no meu arquivo de cabeçalho. O compilador não reclamou. Mas quando defini uma função void foo(type24 val) {}no meu arquivo C, ela se queixou. Eu gostaria de poder definir funções como em type24_to_int32(type24 val)vez de type24_to_int32(char value[3]).

341008
fonte

Respostas:

320

O typedef seria

typedef char type24[3];

No entanto, essa é provavelmente uma péssima idéia, porque o tipo resultante é um tipo de matriz, mas os usuários não verão que é um tipo de matriz. Se usado como argumento de função, ele será passado por referência, não por valor, e o valor sizeofestará errado.

Uma solução melhor seria

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

Você provavelmente também deseja usar em unsigned charvez de char, pois o último possui assinatura definida pela implementação.

R .. GitHub PARE DE AJUDAR O GELO
fonte
10
Existe algum documento legal que descreva os casos de canto envolvidos na passagem de matrizes digitadas como parâmetros? Por exemplo, se uma função tem um parâmetro type24 foo, quais seriam os tamanhos, tipos e significados de foo, *foo, **foo, &foo, e &&foo? Os significados e a legalidade de tais expressões mudaram ao longo dos anos?
Supercat 14/09
4
Provavelmente vale a pena mencionar a ressalva de empacotamento da estrutura, pois um tipo de dados de 24 bits pode ser mapeado para algo com semântica de empacotamento definida de maneira diferente, como dados de imagem RGB.
sh1 27/06
2
@ sh1: Em todas as ABIs modernas do mundo real que eu conheço - mesmo naquelas em que o acesso desalinhado é muito caro - as estruturas não recebem requisitos de alinhamento mais fortes do que seus membros teriam sem a estrutura. É claro que o OP ou qualquer outra pessoa que use essa abordagem deve verificar minha reivindicação se isso importa para o comportamento e a portabilidade do programa.
R .. GitHub Pare de ajudar o gelo
4
@R .. Uma parte disso é enganosa - em C, as matrizes sempre são passadas por referência, ou seja, se você modificar a matriz passada como argumento para uma função, fá-lo-á globalmente, não apenas no contexto da função. Dito isto, também se pode argumentar que em matrizes C sempre são passadas por valor, uma vez que simplesmente passamos o endereço do primeiro elemento, que é copiado na pilha da pilha chamada. Nos dois casos, no entanto, a resposta é enganosa.
Baibo
1
@bobbogo: Isso não é empacotar, é apenas não inserção de preenchimento gratuito. O alinhamento da estrutura é apenas o alinhamento máximo de qualquer um dos seus membros, todos os quais têm alinhamento 1.
R .. GitHub parar de ajudar ICE
49

Você quer

typedef char type24[3];

As declarações do tipo C são estranhas dessa maneira. Você coloca o tipo exatamente para onde o nome da variável iria se estivesse declarando uma variável desse tipo.

ysth
fonte
33

Da resposta de R .. :

No entanto, essa é provavelmente uma péssima idéia, porque o tipo resultante é um tipo de matriz, mas os usuários não verão que é um tipo de matriz. Se usado como argumento de função, ele será passado por referência, não por valor, e o tamanho dele será incorreto.

Os usuários que não vêem que é uma matriz provavelmente escreverão algo assim (que falhará):

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

Ele será compilado com os seguintes avisos:

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

E produz a seguinte saída:

0
1
-453308976
32767
Gerhard Burger
fonte
13

Matrizes não podem ser passadas como parâmetros de função por valor em C.

Você pode colocar a matriz em uma estrutura:

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

e depois passar isso por valor, mas é claro que é menos conveniente usar: em x.byte[0]vez de x[0].

Sua função type24_to_int32(char value[3])realmente passa pelo ponteiro, não pelo valor. É exatamente equivalente a type24_to_int32(char *value), e o 3é ignorado.

Se você está feliz passando pelo ponteiro, você pode ficar com a matriz e fazer:

type24_to_int32(const type24 *value);

Isso passará um ponteiro para o array, não um ponteiro para o primeiro elemento; portanto, você o usa como:

(*value)[0]

Não sei se isso é realmente um ganho, pois se você escrever acidentalmente value[1], algo estúpido acontece.

Steve Jessop
fonte
2
Acho que essa resposta poderia ser melhorada mencionando o termo em decayalgum lugar (e talvez apontando que a situação é pior para o retorno de matrizes - o que não funciona de todo).
Frerich Raabe
9

Para usar o tipo de matriz corretamente como argumento de função ou parâmetro de modelo, faça uma struct em vez de um typedef e adicione uma operator[]à estrutura para que você possa manter a matriz como uma funcionalidade da seguinte maneira:

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

type24 x;
x[2] = 'r';
char c = x[2];
Geronimo
fonte
14
Esta é uma pergunta C, não C ++. Nem char&nem operator[]são coisas que existem em C.
Michael Morris
3

Aqui está um pequeno exemplo de por que o array typedef pode ser confusamente inconsistente. As outras respostas fornecem uma solução alternativa.

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

Isso produz a saída

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

porque type24 como parâmetro é um ponteiro. (Em C, as matrizes sempre são passadas como ponteiros.) Felizmente, o compilador gcc8 emitirá um aviso por padrão.

Daniel
fonte