有没有一种方法可以修改这个C++结构体赋值块,使其在纯C中工作?

6
以下代码位于设备中,在通过IrDA sockets连接枚举时将发出deviceId(LXdeviceInfo)。这只是为了解释为什么我想尽可能保持数据类型相似,但能够使用ansi C编译。
在包含了windows.h和af_irda.h的情况下,以下代码在C++编译器中可以编译而没有错误,但是在C编译器中在结构赋值下面就会报错(见ERROR)。理想情况下,我想初始化结构成员“ID”为字符数组,同时保持它的类型与原始代码完全相同,以便我可以在从PC socket连接调用设备进行查询时测试LXdeviceInfo的值。
“是否有一些方法可以修改这个赋值块,使之在纯C中工作?”
#include <windows.h>
#include <af_irda.h>

#define IR_HINT_COMPUTER  0x04
#define IR_HINT_EXT       0x80
#define IR_HINT_OBEX      0x20
#define IR_HINT_IRCOMM    0x04
#define IR_CHAR_ASCII       0
#define PROD_FAMILY_NAME ("product name goes here")

#define uint8_t unsigned char

const struct {
    uint8_t hint1;
    uint8_t hint2;
    uint8_t charset;
    uint8_t ID[sizeof(PROD_FAMILY_NAME)];
} devInfoStorage = 
{
    IR_HINT_COMPUTER | IR_HINT_EXT,   // hint1
    IR_HINT_OBEX | IR_HINT_IRCOMM,    // hint2
    IR_CHAR_ASCII,                    // charset
    PROD_FAMILY_NAME                  // Prod ID string
}; // ERROR here: Innvalid initialization type: found 'pointer to char' expected 'unsigned char'

const uint8_t *LXdeviceInfo = (uint8_t *) &devInfoStorage;

/* The size of the device info */
const uint8_t LXdeviceInfoLen = sizeof(devInfoStorage);



void main(void)
{

    #define DEVICE_LIST_LEN    10

    unsigned char DevListBuff[sizeof (DEVICELIST) -
                              sizeof (IRDA_DEVICE_INFO) +
                              (sizeof (IRDA_DEVICE_INFO) * DEVICE_LIST_LEN)];

    int DevListLen = sizeof (DevListBuff);
    PDEVICELIST pDevList;

    pDevList = (PDEVICELIST) & DevListBuff;
         //code continues.

}

2
尝试从PROD_FAMILY_NAME中删除括号。 - Vaughn Cato
2
据我所知,这应该也是有效的 C 代码。可能是字符串周围的括号搞乱了它?或者数组赋值没有成功,因为无符号字符和字符不太匹配。 - Zan Lynx
@ryyker,将这些定义替换为它们的consttypedef等效项怎么样? - greatwolf
2
这是int main(void)不是 void main(void)。如果你有一本教材告诉你void main(void)是正确的,那么你需要找一本更好的教材。 - Keith Thompson
@KeithThompson - C99标准中的 "返回类型应定义为int,不带参数...或带两个参数...或以其他实现定义的方式"(第5.1.2.2.1节)。听起来好像可以按需要定义它。 - ryyker
@ryyker:除了标准定义的两种形式之外,其他定义仅在您使用的实现文档另一种形式时才有效,并且仅适用于该实现。否则,行为是未定义的。(而C++根本不允许main具有除int以外的返回类型。)对于独立实现,程序的入口点完全由实现定义,并且甚至可能不被称为“main”。 - Keith Thompson
2个回答

11

移除字符串字面量周围的括号。这些括号会使宏展开为一个表达式,该表达式将衰减为指针类型,在您的C编译器中无法编译。指针类型不能用于初始化数组。没有括号,字符串字面量将用于初始化数组。

#define PROD_FAMILY_NAME "product name goes here"

根据C标准,圆括号表达式与未加括号的表达式具有相同的类型,C.99 §6.5.1 ¶5:

圆括号表达式是一种基本表达式。如果未加括号的表达式是左值、函数指针或无类型表达式,则其类型和值与未加括号的表达式相同。

然而,字符串字面量虽然是一个表达式,但反之并不成立。具体来说,字符串字面量本身不是一种类型,而是一个定义的实体。数组的初始化对字符串字面量做出了特别的允许,C.99 §6.7.8 ¶14:

字符类型的数组可以用字符字符串字面量进行初始化,可以选择用大括号括起来。

其他允许的数组初始值在C.99 §6.7.8 ¶16中描述:

否则,具有聚合或联合类型的对象的初始值应为元素或命名成员的大括号括起来的初始化器列表。

圆括号表达式既不是字符串字面量,也不是用花括号括起来的初始化器列表。


3
看起来是 C 编译器的一个 bug -- 在初始化器中使用括号是完全合法的,因此不应该迫使编译器将其视为表达式。相比之下,在 gcc(mingw)中它可以正常工作。 - Chris Dodd
也许 #define S ("hello") 会衰变成一个指针? - user2249683
3
C99 §6.7.8/14-16指出,“字符类型的数组可以通过字符字符串字面值进行初始化,可以选择用大括号括起来。[...]否则,具有聚合或联合类型的对象的初始化器必须是一个元素或命名成员的初始化器的大括号括起来的列表。” 这似乎意味着 (“blah”) 不是合法的初始化器,因为它既不是字符串字面值,也不是括在大括号内的字符串字面值,也不是大括号括起来的初始化器列表。 - Adam Rosenfield
1
gcc 4.7.2对此有一个特定的警告:“warning: array initialized from parenthesized string constant [-pedantic]”; 它会在使用gcc -pedantic -std=cXX时出现,其中XX可以是909911中的任何一个。 - Keith Thompson
字符字面量的类型为'char数组',只有在不允许使用数组的情况下才会发生指针衰减。例如sizeof((("foobar")))的结果是7而不是sizeof(char *),无论添加多少额外的括号。6.7.8.14中的语言不够精确--在这个上下文中,“字符字符串字面量”是什么?像char foo[] = "foo" "bar";这样的代码是否合法(利用字符串字面量连接)? - Chris Dodd
显示剩余3条评论

0

由于PROD_FAMILY_NAME是一个(常量)字符串,编译器期望ID被声明为类似uint8_t *ID;的东西,因为在C语言中,字符串通常表示为指向内存中字符的指针。请参见我的示例:http://ideone.com/m5ZZl0

如果你使用字符数组,你需要使用strcpymemcpy将产品系列名称复制到你的ID字节数组中,因为C语言不支持直接数组赋值。如果产品名称是一个变量但不会改变且不会超出作用域,我建议使用uint8_t*类型的ID,但如果你需要一个名称的副本并且不想动态分配内存,你可能需要使用一个数组(但请注意,旧版本的C标准也不支持非静态长度的数组[虽然有一些方法可以通过在结构体末尾使用可变长度数组来解决这个问题,但在这种情况下你通常必须手动跟踪结构体的大小,因为sizeof()对于这样的数组将返回0;像strlen()这样的函数仍然可以正常工作)。

看起来我忘记了,虽然不允许数组赋值,但是像char array[] = "literal string";这样的东西仍然可以。(http://ideone.com/vilDOa vs. http://ideone.com/gEC2k2) 所以我的上面有点无意义,但是我认为如果你想要一个这样的结构体数组,动态调整大小的结构体并不是一个好主意,此时char*方法可能是一个更好的主意(即使它只是指向其他地方的数组的指针)。


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