typedef固定长度数组

254

我需要定义一个24位数据类型。我使用char[3]来表示这种类型。我可以将char[3] typedef为type24吗? 我在代码示例中尝试过。我将typedef char[3] type24;放入我的头文件中,编译器没有抱怨。但是当我在C文件中定义一个函数void foo(type24 val) {}时,它会出现错误。我希望能够定义像type24_to_int32(type24 val)这样的函数,而不是type24_to_int32(char value[3])

7个回答

385

这个typedef将会是:

typedef char type24[3];

然而,这可能是一个非常糟糕的想法,因为结果类型是数组类型,但使用它的用户将看不到它是一个数组类型。如果作为函数参数使用,它将通过引用传递而不是按值传递,其sizeof 也将是错误的。

更好的解决方案是

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

你可能还想使用unsigned char而不是char,因为后者具有实现定义的有符号性。


13
有没有一个好的文件描述传递typedef数组作为参数所涉及的边界情况?例如,如果一个函数使用参数type24 foo,那么foo*foo**foo&foo&&foo的大小、类型和含义会是什么?这些表达式的含义和合法性是否随着时间而改变? - supercat
4
值得一提的是结构体对齐的注意事项,因为24位数据类型可能会被用于映射到具有不同打包语义的内容,比如RGB图像数据。 - sh1
2
@sh1:在我所知道的所有现代实际应用二进制接口(ABIs)中,即使是那些不支持未对齐访问非常昂贵的ABI,结构体也不会比其成员本身具有更强的对齐要求。当然,如果OP或其他人使用这种方法,如果这对他们程序的行为和可移植性很重要,他们应该验证我的说法。 - R.. GitHub STOP HELPING ICE
5
这段话中有一个误导性的部分 - 在C语言中,数组总是通过引用传递,也就是说,如果你修改作为参数传递给函数的数组,你会对它进行全局修改,而不仅仅是在函数上下文中。话虽如此,人们也可以认为在C语言中,数组总是按值传递,因为我们只是传递第一个元素的地址,这个地址被复制到调用者堆栈上。然而,在两种情况下,答案都是误导性的。 - baibo
2
@bobbogo:你的测试有缺陷。“3”是一个“int”,而`sizeof(int)!=3”。 - R.. GitHub STOP HELPING ICE
显示剩余9条评论

64
你想要什么。
typedef char type24[3];

C类型的声明方式有些奇怪。你需要把类型放在变量名的位置上,就好像你正在声明一个该类型的变量。


6
我本来期望的是 typedef char[3] type24,但看起来我错了 :))。为什么会这样,有解释吗? - Cătălina Sîrbu
7
@CătălinaSîrbu 请查看解密声明的“左右规则”:http://cseweb.ucsd.edu/~ricko/rt_lt.rule.html - Gerhard Burger
@CătălinaSîrbu 请访问https://cdecl.org/进行英汉翻译。 - undefined

38

来自R..的回答:

然而,这可能是一个非常糟糕的想法,因为结果类型是一个数组类型,但使用者不会看到它是一个数组类型。如果作为函数参数使用,它会被按引用传递,而不是按值传递,然后对于它的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

18

在 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
我认为在某处提到“衰减”这个术语可能会改善这个答案(也许还可以指出返回数组的情况更糟糕 - 它根本不起作用)。 - Frerich Raabe

8
为了正确地将数组类型用作函数参数或模板参数,请使用结构体而不是 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];

19
这是一个关于 C 语言的问题,不是 C++。在 C 语言中不存在 char&operator[] - Michael Morris

6
这是一个说明为什么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编译器会默认发出警告。


4

已接受的答案的基础上构建,一个多维数组类型,即由固定长度的固定长度数组组成的定长数组,不能使用以下方式声明:

typedef char[M] T[N];  // wrong!

相反,可以声明并使用中间的一维数组类型,就像被接受的答案中所示:

typedef char T_t[M];
typedef T_t T[N];

或者,T 可以在单个(可能令人困惑的)语句中声明:

typedef char T[N][M];

该代码定义了一种类型的N个数组,每个数组包含M个字符(注意这里的顺序)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接