在C中用字符数组初始化数值结构域?

hivapdat  于 2023-01-29  发布在  其他
关注(0)|答案(3)|浏览(99)

假设我创建了一个主要包含数值字段的结构体,然后,我想用ASCII字符的值初始化这些不同的字段,这样最后,我就可以将该结构体转换为char*指针,并一次性打印出来,获得该结构体的每字节“Map”打印输出。
下面是编译的例子:

#include <stdio.h>
#include <string.h>
#include <inttypes.h>

struct mystruct_s {
  uint8_t id_tag[6]; 
  uint8_t fld_one[2];
  uint64_t fld_two;
  uint16_t fld_three;
  uint16_t fld_four;
  uint16_t fld_five;
};

struct mystruct_s myrh = {
  .id_tag = "123456",
  .fld_one = "78",
  .fld_two = (uint64_t) ((uint8_t[8]) { 'T', 'T', 'T', 'T', 'T', 'T', 'T', 'T' }),
};

int main() {
  printf("size %zu (%zu) bytes\n", sizeof(myrh), sizeof(struct mystruct_s));
  printf("as string:\n");
  printf("%s\n", (char*)&myrh);
  printf("char 10: '%c'\n", ((char*)&myrh)[9]);
  return 0;
}

...但是,当它运行时,它会产生:

$ gcc -g main.c -o main.exe
$ ./main.exe
size 24 (24) bytes
as string:
12345678`�V
char 10: ''

......而我原本期望的是12345678TTTTTTT(char 10也是“T”)。所以,很明显我的“类型转换”不太起作用。
那么,有没有某种语法可以让我用等价于char数组的值初始化一个结构体的数值字段呢?(注意,上面的例子显示了将.fld_two中的所有字节初始化为'T'的意图,也就是说,每个ASCII都是0x 54-但我最终感兴趣的是指定像这样的任意字符串,使用不同的字符)
第二件令我惊讶的事情是,如果我添加另一个字段初始化:

...
struct mystruct_s myrh = {
  .id_tag = "123456",
  .fld_one = "78",
  .fld_two = (uint64_t) ((uint8_t[8]) { 'T', 'T', 'T', 'T', 'T', 'T', 'T', 'T' }),
  .fld_three = (uint16_t) ((uint8_t[2]) { 'E', 'E' }),
};
...

...则构建失败,原因如下:

gcc -g main.c -o main.exe
main.c:18:16: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
   .fld_three = (uint16_t) ((uint8_t[2]) { 'E', 'E' }),
                ^
main.c:18:16: error: initializer element is not constant
main.c:18:16: note: (near initialization for ‘myrh.fld_three’)

那么,为什么它没有检测到此行的错误:

.fld_two = (uint64_t) ((uint8_t[8]) { 'T', 'T', 'T', 'T', 'T', 'T', 'T', 'T' }),

...然而它 * 没有 * 发现一个错误在此行:

.fld_three = (uint16_t) ((uint8_t[2]) { 'E', 'E' }),

...即使两行显然在做同样的事情,只是大小和值不同?

6yt4nkrj

6yt4nkrj1#

(uint64_t) ((uint8_t[8]) { 'T', 'T', 'T', 'T', 'T', 'T', 'T', 'T' })
这将在使用它的相同作用域创建一个 compound literal。像任何数组一样,uint8_t [8]在表达式中使用时衰减为指向第一个元素的指针。因此,最终将 * address* 转换为uint64_t,而不是数据。
你可以用联合类型的双关语来解决这个问题。

typedef union
{
  uint8_t  bytes [8];
  uint64_t u64;
} pun_intended;

...

.fld_two = (pun_intended){ .bytes = {'T', 'T', 'T', 'T', 'T', 'T', 'T', 'T'} }.u64,

然而这带来了另一个问题,即它不再是一个编译时常量表达式,当然理想的解决方法是首先不要使用草率的全局变量,但如果这不是一个选项(也许你需要const),你可以选择改变结构体成员的类型:

pun_intended fld_two;
...
.fld_two = (pun_intended){ .bytes = {'T', 'T', 'T', 'T', 'T', 'T', 'T', 'T'} },
eqqqjvef

eqqqjvef2#

嗯,我设法通过联合达到了我想要的效果--但是有太多的类型和样板,这违背了目的(我想在OP中使用语法,因为它看起来很容易:)

#include <stdio.h>
#include <string.h>
#include <inttypes.h>

struct mystruct_s {
  uint8_t id_tag[6]; 
  uint8_t fld_one[2];
  uint64_t fld_two;
  uint16_t fld_three;
  uint16_t fld_four;
  uint16_t fld_five;
} __attribute__((packed));
struct mystruct_alt_s {
  unsigned char id_tag[6]; 
  unsigned char fld_one[2];
  unsigned char fld_two[8];
  unsigned char fld_three[2];
  unsigned char fld_four[2];
  unsigned char fld_five[2];
} __attribute__((packed));

union undata {
  struct mystruct_alt_s myo_alt;
  struct mystruct_s myo;
} ;

union undata data = {
  .myo_alt = {
    .id_tag = "123456",
    .fld_one = "78",
    .fld_two = "TTTTTTTT",
    .fld_three = "HH",
    .fld_four = "RR",
    .fld_five = "VV",
  }
};

int main() {
  printf("size %zu (%zu) bytes\n", sizeof(data), sizeof(struct mystruct_s));
  printf("as string:\n");
  printf("%s\n", (char*)&data.myo);
  return 0;
}

这将打印:

$ gcc -g main.c -o main.exe
$ ./main.exe
size 22 (22) bytes
as string:
12345678TTTTTTTTHHRRVV
2o7dmzc5

2o7dmzc53#

除了你自己的答案之外,你还可以使用新学的X宏来把变量定义和初始化字符串集中在一个地方:

#include <stdio.h>
#include <string.h>
#include <inttypes.h>

// X macro taking Type, Name, Count, ValueString
#define FIELDS \
  X(uint8_t, id_tag, [6], "123456") \
  X(uint8_t, fld_one, [2], "78") \
  X(uint64_t, fld_two, , "TTTTTTTT") \
  X(uint16_t, fld_three, , "HH") \
  X(uint16_t, fld_four, , "RR") \
  X(uint16_t, fld_five, , "VV")

#define X(T,N,C,V) T N C;
struct mystruct_s {
  FIELDS
} __attribute__((packed));
#undef X

#define X(T,N,C,V) unsigned char N[sizeof(T C)];
struct mystruct_alt_s {
  FIELDS
} __attribute__((packed));
#undef X

union undata {
  struct mystruct_alt_s myo_alt;
  struct mystruct_s myo;
} ;

#define X(T,N,C,V) .N = V,
union undata data = {
  .myo_alt = {
    FIELDS
  }
};
#undef X

int main() {
  printf("size %zu (%zu) bytes\n", sizeof(data), sizeof(struct mystruct_s));
  printf("as string:\n");
  printf("%s\n", (char*)&data.myo);
  return 0;
}

相关问题