【问题标题】:Padding issue? (C / GCC)填充问题? (C/海合会)
【发布时间】:2014-01-17 16:59:48
【问题描述】:

我正在尝试统一 ARM 项目(特别是运行 Linux 2.6.33.3 的 i.MX27 CPU,使用 GCC 4.3.2 编译)的 SQLite 交互方法。作为其中的一部分,我创建了一个带有联合的结构,该联合用于保存要绑定到准备好的语句的值。

#define SQLITE_DATA_CHARACTER_STRING_MAX 1024

typedef struct
{
    int data_type;
    union
    {
        int integer;
        double floating_point;
        unsigned char character_string[SQLITE_DATA_CHARACTER_STRING_MAX];
    };
}sqlite_data;

最初是intfloatchar。我想使用long longdoublechar。但是,这似乎会导致问题。如上所述,以下代码产生可预测的输出:

int data_fields = 15;
int data_fields_index = 0;
sqlite_data data[data_fields];

LogMsg(LOG_INFO, "%s: Assigning", __FUNCTION__);

for(data_fields_index = 0; data_fields_index < data_fields; data_fields_index++)
{
    data[data_fields_index].data_type = (100 + data_fields_index);
    data[data_fields_index].integer = (1000 + data_fields_index);
    LogMsg(LOG_INFO, "%s: data[%d] - %d; type - %d", __FUNCTION__, data_fields_index, data[data_fields_index].integer, data[data_fields_index].data_type);
}

它的输出是这样的:

 Assigning
 data[0] - 1000; type - 100
 data[1] - 1001; type - 101
 data[2] - 1002; type - 102
 data[3] - 1003; type - 103
 data[4] - 1004; type - 104
 data[5] - 1005; type - 105
 data[6] - 1006; type - 106
 data[7] - 1007; type - 107
 data[8] - 1008; type - 108
 data[9] - 1009; type - 109
 data[10] - 1010; type - 110
 data[11] - 1011; type - 111
 data[12] - 1012; type - 112
 data[13] - 1013; type - 113
 data[14] - 1014; type - 114

但是,如果我只进行一次更改(给 integer 类型 long long),那么一切都会崩溃。因此,以下更改:

typedef struct
{
    int data_type;
    union
    {
        long long integer;
        double floating_point;
        unsigned char character_string[SQLITE_DATA_CHARACTER_STRING_MAX];
    };
}sqlite_data;

产生这个输出:

Assigning
data[0] - 1000; type - 0
data[1] - 1001; type - 0
data[2] - 1002; type - 0
data[3] - 1003; type - 0
data[4] - 1004; type - 0
data[5] - 1005; type - 0
data[6] - 1006; type - 0
data[7] - 1007; type - 0
data[8] - 1008; type - 0
data[9] - 1009; type - 0
data[10] - 1010; type - 0
data[11] - 1011; type - 0
data[12] - 1012; type - 0
data[13] - 1013; type - 0
data[14] - 1014; type - 0

我尝试使用 #pragma pack(6) 对它们进行去工会化,并将该数组放在堆上,所有结果都相同:int 有效,long long 无效。

这是怎么回事?

【问题讨论】:

  • 旁注,除非您的目标是 C11,unnamed unions are not portable。上周在将自定义协议移植到 Windows 时遇到了一些问题。
  • 也可以打印出struct的地址,看看step是否随着声明的变化而变化。这会让你直接看到填充效果。
  • 未命名的联合不符合标准,但它们非常便携。我不知道有任何主流编译器不支持它们作为扩展。

标签: c gcc


【解决方案1】:

你不是告诉printf() 期待long long。在格式字符串中使用%lld 而不是%d

【讨论】:

  • long long 打印正常。 int type 打印为零。
  • @musasabi 但也许你用错误的说明符弄乱了你的堆栈(它会导致未定义的行为)。所以使用%lld 打印long long
  • 我添加了一些可能发生的事情的可视化,希望能帮助你理解(这仍然是未定义的行为,所以我们不能 100% 确定我所展示的事情正在发生)。 Sneftel 和 H2CO3 是正确的。
【解决方案2】:

Sneftel 是绝对正确的。问题是您没有指定long long,这会导致未定义的行为。为了帮助您可视化以下图片:

"%s: data[%d] - %d; type - %d"

%s - [✓] __FUNCTION__ is a valid string.
%d - [✓] data_fields_index is an int
%d - [x] data[data_fields_index].integer is a long long, this will only read 
          the first 4 bytes of an 8 byte integer.
%d - [x] this is likely reading the last 4 bytes of 
          data[data_fields_index].integer which will be 0 for smaller numbers 
          on a little endian architecture.

因此,long long 1000 将以 little endian 形式存储在内存中:

[0x00 0x20 0x00 0x00 0x00 0x00 0x00 0x00]
  ^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^
      first %d          second %d

将格式更改为"%s: data[%d] - %lld; type - %d" will fix this.

【讨论】:

  • 没错。平台的字节顺序在这里很重要,底层 ABI 也是如此。要点是“未定义的行为可以看起来像任何东西——它不是本地化的”。
  • 我要感谢你们两个完全正确如此详细。 =) 赞成,但我只能选择一个答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-06
  • 2011-03-28
相关资源
最近更新 更多