typedef массив фиксированной длины


210

Я должен определить 24-битный тип данных. Я использую char[3]для представления типа. Могу ли я ввести char[3]определение type24? Я попробовал это в примере кода. Я положил typedef char[3] type24;в мой файл заголовка. Компилятор не жаловался на это. Но когда я определил функцию void foo(type24 val) {}в моем C-файле, он пожаловался. Я хотел бы иметь возможность определять функции, как type24_to_int32(type24 val)вместо type24_to_int32(char value[3]).

Ответы:


320

Typedef будет

typedef char type24[3];

Однако, это, вероятно, очень плохая идея, потому что результирующий тип является типом массива, но пользователи этого не увидят, что это тип массива. Если он используется в качестве аргумента функции, он будет передан по ссылке, а не по значению, и тогда sizeoffor будет неверным.

Лучшее решение было бы

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

Вы, вероятно, также хотите использовать unsigned charвместо char, так как последний имеет подпись, определяемую реализацией.


10
Есть ли хороший документ, в котором описываются угловые случаи, связанные с передачей массивов типа в качестве параметров? Например, если функция принимает параметр type24 foo, что бы размеры, типы и значения foo, *foo, **foo, &foo, и &&foo? Изменились ли значения и законность любых таких выражений с годами?
суперкат

4
Вероятно, стоит упомянуть предостережение о структуре упаковки, поскольку 24-битный тип данных может быть предназначен для отображения на что-то с по-разному определенной семантикой упаковки, такой как данные изображения RGB.
sh1

2
@ sh1: На всех современных реальных ABI, о которых я знаю, - даже в тех, где смещенный доступ очень дорогой, - структуры не получают более строгих требований к выравниванию, чем их участники без структуры. Конечно, OP или кто-либо еще, использующий этот подход, должен проверить мое заявление, если оно будет иметь значение для поведения и переносимости их программы.
R .. GitHub ОСТАНОВИТЬ ЛЬДА

4
@R .. Одна часть этого вводит в заблуждение - в C массивы всегда передаются по ссылке, т. Е. Если вы модифицируете массив, переданный в качестве аргумента функции, вы делаете это глобально, а не только в контексте функции. При этом можно также утверждать, что в C массивы всегда передаются по значению, поскольку мы просто передаем адрес первого элемента, который копируется в стек в стеке вызываемого. Однако в обоих случаях ответ вводит в заблуждение.
baibo

1
@bobbogo: Это не упаковка, а просто не вставка необязательных отступов. Выравнивание структуры - это просто максимальное выравнивание любого из ее членов, у всех из которых есть выравнивание 1.
R .. GitHub ОСТАНОВИТЬ ПОМОЩЬ ICE

49

Вы хотите

typedef char type24[3];

Объявления типа C странны таким образом. Вы помещаете тип точно в то место, куда будет идти имя переменной, если вы объявляли переменную этого типа.


33

Из ответа Р .. :

Тем не менее, это, вероятно, очень плохая идея, потому что результирующий тип является типом массива, но пользователи этого не увидят, что это тип массива. Если он используется в качестве аргумента функции, он будет передан по ссылке, а не по значению, и тогда sizeof для него будет неправильным.

Пользователи, которые не видят, что это массив, скорее всего напишут что-то вроде этого (что не получается):

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

Он скомпилирует следующие предупреждения:

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

И выдает следующий вывод:

0
1
-453308976
32767

13

Массивы не могут быть переданы как параметры функции по значению в C.

Вы можете поместить массив в структуру:

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

и затем передать это по значению, но, конечно, тогда это менее удобно использовать: x.byte[0]вместо x[0].

Ваша функция type24_to_int32(char value[3])фактически передается по указателю, а не по значению. Это в точности эквивалентно type24_to_int32(char *value), и 3игнорируется.

Если вы счастливы, передавая указатель, вы можете придерживаться массива и сделать:

type24_to_int32(const type24 *value);

Это передаст указатель на массив, а не указатель на первый элемент, поэтому вы используете его как:

(*value)[0]

Я не уверен, что это действительно выигрыш, так как если вы случайно напишите, value[1]произойдет что-то глупое.


2
Я думаю, что этот ответ можно улучшить, упомянув decayгде-то термин (и, возможно, указав, что ситуация с возвратом массивов хуже - что не работает вообще).
Фрерих Раабе

9

Чтобы правильно использовать тип массива в качестве аргумента функции или параметра шаблона, создайте структуру вместо typedef, затем добавьте operator[]в структуру, чтобы вы могли сохранить массив как функциональность следующим образом:

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

type24 x;
x[2] = 'r';
char c = x[2];

14
Это вопрос C, а не C ++. Ни то, char&ни другое operator[], что существует в C.
Майкл Моррис

3

Вот короткий пример того, почему массив typedef может быть противоречивым. Другие ответы обеспечивают обходной путь.

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

Это производит вывод

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

потому что type24 в качестве параметра является указателем. (В C массивы всегда передаются как указатели.) К счастью, компилятор gcc8 выдаст предупреждение по умолчанию.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.