灵活阵列成员的替代方案,无需动态内存分配

时间:2017-08-08 18:42:24

标签: c arrays flexible-array-member

我使用灵活的数组成员来定义USB-String描述符:

IF(COUNT(DISTINCT ID) > 1    --When ID has been seen more than once?
   INSERT INTO Table2(ID, startTime)
   SELECT ID, CURRENT_TIMESTAMP
   FROM Table1
   WHERE ID = Table2.PTID

ELSE
   INSERT INTO Table2(ID, startTime)
   SELECT ID, CURRENT_TIMESTAMP
   FROM Table1

这很好用,并允许在别处定义实际的字符串常量,以便在项目之间轻松更改。但由于编译器的限制,我不能再使用灵活的数组成员了。我想避免硬编码char数组,我基本上只需要将两个字节添加到宽字符数组的前面。是否有任何聪明的方法来声明可以实现此目的的阵列或预处理器宏?

3 个答案:

答案 0 :(得分:3)

我认为无法以可移植的方式使用动态内存分配来实现灵活大小的结构。

但是,以下两种具有固定大小结构的方法之一也可能对您有用:

  1. 定义结构,使bstring是一个固定大小的数组;如果您的程序中只有一个STR_PRODUCT,则可以使用它的大小。如果您有更多结构实例,请使用您要允许的最大大小来定义数组。
  2. 定义结构,bstring为指针;您仍然可以使用指向字符串文字的指针对其进行初始化。请注意,对象将不包含连续内存块中的所有信息。如果您需要包含所有信息的连续内存,则此方法2将不起作用。
  3. 请参阅以下程序,该程序演示了两种方法:

    #define STR_PRODUCT_1   L"My Cool Product"
    #define STR_PRODUCT_2   L"Another Cool Product"
    #define MAX_PRODUCT_STR  "012345678901234567890"
    
    // Approach 1:
    typedef struct {
        uint8_t  bLength;
        uint8_t  bDescriptorType;
        wchar_t  bString[sizeof(MAX_PRODUCT_STR)];
    } USB_StringDescriptor;
    
    const USB_StringDescriptor manufacturer_string = {
        sizeof(STR_PRODUCT_1)+2,
        0x03,
        STR_PRODUCT_1
    };
    
    const USB_StringDescriptor manufacturer_string2 = {
        sizeof(STR_PRODUCT_2)+2,
        0x03,
        STR_PRODUCT_2
    };
    
    // Approach 2:
    typedef struct {
        uint8_t  bLength;
        uint8_t  bDescriptorType;
        wchar_t  *bString;
    } USB_StringDescriptor_V2;
    
    const USB_StringDescriptor_V2 manufacturer_string_v2 = {
        sizeof(STR_PRODUCT_1)+2,
        0x03,
        STR_PRODUCT_1
    };
    
    const USB_StringDescriptor_V2 manufacturer_string_v2_2 = {
        sizeof(STR_PRODUCT_2)+2,
        0x03,
        STR_PRODUCT_2
    };
    
    int main() {
        wprintf(L"1: %ls\n",manufacturer_string.bString);
        wprintf(L"2: %ls\n",manufacturer_string2.bString);
    
        wprintf(L"1 ptr: %ls\n",manufacturer_string_v2.bString);
        wprintf(L"2 ptr: %ls\n",manufacturer_string_v2_2.bString);
    }
    

答案 1 :(得分:2)

除了Stephen Lechner's answer之外,您可以使用外部程序生成字符串描述符(和其他类似程序),输出要包含在实现中的C源文件。只有当你有很多常量需要按摩时才有意义。

请注意,您可以使用uint16_t数组作为USB字符串描述符,而不是结构。 (您需要知道的是架构上的字节顺序,因此字节长度字节也是内存中的第一个字节:它是大端架构的高字节,而小字节的低字节 - endian体系结构。)

举个例子,考虑以下awk脚本, constants.awk

#!/usr/bin/awk -f
BEGIN {
    # 0 = little endian, least significant byte first
    # 1 = big endian, most significant byte first
    BYTEORDER = 1

    RS = "[\t\v\f ]*(\r\n|\n\r|\r|\n)[\t\v\f ]*"
    FS = "[\t\v\f ]+"

    split("", codepoint)
    for (i = 1; i < 128; i++)
        codepoint[sprintf("%c", i)] = i
    # Note: Could add unicode code points U+00A0 to U+FFFF
    #       to codepoint[] array.

    printf "#ifndef   CONSTANTS_H\n"
    printf "#define   CONSTANTS_H\n"
    printf "\n"
    printf "/* Do not edit this file; edit constants.in instead.\n"
    printf "   This file is automatically generated by constants.awk.\n"
    printf "*/\n\n"
}

$1 == "usb_string_descriptor" && NF >= 3 {
    name = $2
    value = $0
    sub(/^[^"]*"/, "", value)   # Remove everything before first "
    sub(/"[^"]*$/, "", value)   # Remove everything after last "
    valuelen = length(value)
    type = 3

    printf "#define  %s_size  %d\n", name, 2*valuelen + 2
    printf "#define  %s_type  %d\n", name, type
    printf "#define  %s_len   %d\n", name, valuelen
    printf "static const uint16_t %s[%d] = {\n", name, valuelen + 1
    printf "    /* \"%s\" */\n", value
    if (BYTEORDER == 1)
        printf "    0x%02x%02x, /* length = %d bytes, type = 0x%02x */", 2*valuelen + 2, type, 2*valuelen + 2, type
    else
        printf "    0x%02x%02x, /* length = %d bytes, type = 0x%02x */", type, 2*valuelen + 2, 2*valuelen + 2, type
    for (i = 1; i <= valuelen; i++) {
        if ((i % 8) == 1)
            printf "\n    "
        printf "0x%04x, ", codepoint[substr(value, i, 1)]
    }
    printf "\n};\n\n"
}

END {
    printf "#endif /* CONSTANTS_H */\n"
}

我们假设您有一个文件,比如说constants.in,描述了上述scriptlet应该定义的一些常量:

usb_string_descriptor manufacturer_string_1 "My Cool Product"
usb_string_descriptor manufacturer_string_2 "My Other Cool Product"

让您的构建机器运行,例如awk -f constants.awk constants.in > constants.h。如果您使用Makefile,那么

AWK := awk

constants.h: constants.in
    $(AWK) -f constants.h constants.in > constants.h

应该做的伎俩。 (如果您编辑constant.h

,它甚至会导致constants.in头文件重新生成

在实际的C源文件实现中,您只需#include "constants.h"

以上将输出

#ifndef   CONSTANTS_H
#define   CONSTANTS_H

/* Do not edit this file; edit constants.in instead.
   This file is automatically generated by constants.awk.
*/

#define  manufacturer_string_1_size  32
#define  manufacturer_string_1_type  3
#define  manufacturer_string_1_len   15
static const uint16_t manufacturer_string_1[16] = {
    /* "My Cool Product" */
    0x2003, /* length = 32 bytes, type = 0x03 */
    0x004d, 0x0079, 0x0020, 0x0043, 0x006f, 0x006f, 0x006c, 0x0020, 
    0x0050, 0x0072, 0x006f, 0x0064, 0x0075, 0x0063, 0x0074, 
};

#define  manufacturer_string_2_size  44
#define  manufacturer_string_2_type  3
#define  manufacturer_string_2_len   21
static const uint16_t manufacturer_string_2[22] = {
    /* "My Other Cool Product" */
    0x2c03, /* length = 44 bytes, type = 0x03 */
    0x004d, 0x0079, 0x0020, 0x004f, 0x0074, 0x0068, 0x0065, 0x0072, 
    0x0020, 0x0043, 0x006f, 0x006f, 0x006c, 0x0020, 0x0050, 0x0072, 
    0x006f, 0x0064, 0x0075, 0x0063, 0x0074, 
};

#endif /* CONSTANTS_H */

(注意:我似乎记得USB字符串描述符不需要终止NUL字符(0)。我可能记得错了。)

我使用了awk,因为它适用于各种风格的所有平台 - 不仅仅是GNU awk,还有nawk,mawk和many more。您将要使用脚本语言来生成易于维护的脚本;除了USB字符串描述符之外,上述版本还可以扩展到其他类型。

我怀疑Python脚本可能更容易维护,因为它比awk(或Perl,另一种用于此类预处理的典型脚本语言)更受欢迎。此外,Python具有您可以使用的ord()内置函数,并允许您在USB字符串描述符中使用非ASCII Unicode字符。

答案 2 :(得分:1)

在我看来,如果您的编译器不支持这种类型的初始化,最好的方法是使用宏(我不是长多行宏的粉丝,但如果没有其他方法)

#define STR_PRODUCT  L"My Cool Product"

#define USB_desc(name, bL, bD, bS) struct { \
    uint8_t  bLength; \
    uint8_t  bDescriptorType;  \
    wchar_t  bString[]; \
} name = {bL, bD, bS}

USB_desc(manufacturer_string, sizeof(STR_PRODUCT) + 2, 3, STR_PRODUCT);