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];

Однак це, мабуть, дуже погана ідея, оскільки отриманий тип - це тип масиву, але користувачі його не побачать, що це тип масиву. Якщо він використовується як аргумент функції, він передаватиметься посиланням, а не значенням, аsizeof для цього буде помилковим.

Кращим рішенням було б

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

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


10
Чи є якийсь приємний документ, який описує кутові випадки, пов’язані з передачею масивів typedef'ed як параметри? Наприклад, якщо функція приймає параметр type24 foo, що б розміри, типи і значення foo, *foo, **foo, &foo, і &&foo? Чи змінилися значення і законність будь-яких таких виразів за ці роки?
supercat

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

2
@ sh1: Про всі сучасні реальних ІТС, про які я знаю - навіть ті, де неправильний доступ дуже дорогий - структури не мають більш високих вимог до вирівнювання, ніж їх члени мали б без структури. Звичайно, ОП або хтось, хто використовує цей підхід, повинен перевірити мою заяву, якщо це буде мати значення для поведінки та переносимості їх програми.
R .. GitHub СТОП ДОПОМОГАТИ

4
@R .. Одна частина цього вводить в оману - в C масиви завжди передаються посиланням, тобто, якщо ви змінюєте масив, переданий як аргумент функції, ви робите це в усьому світі, а не лише в контексті функції. Це, можна сказати, також може стверджувати, що в C масиви завжди передаються за значенням, оскільки ми просто передаємо адресу першого елемента, який копіюється в стек на стеку callee. Однак в обох випадках відповідь є оманливою.
байбо

1
@bobbogo: Це не упаковка, це просто невстановлення безоплатної набивки. Вирівнювання структури - це лише максимальне вирівнювання будь-якого з її членів, усі вони мають вирівнювання 1.
R .. GitHub СТОП ДОПОМОГА ВІД

49

Ти хочеш

typedef char type24[3];

Декларації типу C дивні. Ви вводите тип саме там, куди піде ім'я змінної, якщо ви декларували змінну цього типу.


33

З відповіді Р .. :

Однак це, мабуть, дуже погана ідея, оскільки отриманий тип - це тип масиву, але користувачі його не побачать, що це тип масиву. Якщо його використовувати як аргумент функції, він передаватиметься за посиланням, а не за значенням, і розмірofof для нього буде помилковим.

Користувачі, які не бачать, що це масив, швидше за все, напишуть щось подібне (що не вдається):

#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

Масиви не можна передавати як функціональні параметри за значенням у С.

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

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.