我无法理解struct指针和XOR的这种行为

时间:2012-12-04 19:35:03

标签: c pointers struct xor

我第一次使用struct指针,我似乎无法理解这里发生的事情。我的测试应用xor的基本属性,即x ^ y ^ y = x,但不是在C?

以下代码在我的主程序中,并准确恢复所有“test”字母(我继续在屏幕上打印,但我省略了很多垃圾以便保持这个问题简短(呃) ))。结构“aes”指的是这个定义:

typedef uint32_t word;

struct aes {

word iv[4];
word key[8];
word state[4];
word schedule[56];

};

正如上下文所暗示的那样,封装项目是一个AES实现(我试图通过尝试新技术来加速我当前的实现)。

在我的测试中,make_string和make_state可靠地工作,即使在相关函数中也是如此,但是为了参考:

void make_string (word in[], char out[]) {

for (int i = 0; i < 4; i++) {

    out[(i * 4) + 0] = (char) (in[i] >> 24);
    out[(i * 4) + 1] = (char) (in[i] >> 16);
    out[(i * 4) + 2] = (char) (in[i] >>  8);
    out[(i * 4) + 3] = (char) (in[i]      );

}

}

void make_state(word out[], char in[]) {

for (int i = 0; i < 4; i++) {

    out[i] =    (word) (in[(i * 4) + 0] << 24) ^
                (word) (in[(i * 4) + 1] << 16) ^
                (word) (in[(i * 4) + 2] <<  8) ^
                (word) (in[(i * 4) + 3]      );

}

}

无论如何,这是可行的块。我试图通过将其存放在一个函数中来模块化这个功能:

char test[16] = {
    'a', 'b', 'c', 'd',
    'e', 'f', 'g', 'h',
    'i', 'j', 'k', 'l',
    'm', 'n', 'o', 'p'
};

aes cipher;

struct aes * work;

work = &cipher;

make_state(work->state, test);

work->state[0] ^= 0xbc6378cd;
work->state[0] ^= 0xbc6378cd;

make_string(work->state, test);

虽然这段代码有效,但通过将它传递给函数来做同样的事情却不会:

void encipher_block (struct aes * work, char in[]) {

    make_state(work->state, in);

    work->state[0] ^= 0xff00cd00;

    make_string(work->state, in);

}

void decipher_block (struct aes * work, char in[]) {

    make_state(work->state, in);

    work->state[0] ^= 0xff00cd00;

    make_string(work->state, in);

}

然而,通过在encipher和decipher中删除make_state和make_string调用,它可以正常工作!

make_state(work->state, test);

encipher_block(&cipher, test);
decipher_block(&cipher, test);

make_string(work->state, test);

所以要澄清一下,我没有问题!我只是想了解这种行为。

2 个答案:

答案 0 :(得分:2)

char更改为unsigned charchar可能已签名,可能在您的系统上,这会在转换为其他整数类型和转移时导致问题。

(char) (in[i] >> 24)中的表达式make_string中,无符号的32位整数被转换为带符号的8位整数(在C实现中)。此表达式可能会将值转换为char中无法表示的值char,特别是128到255之间的值。根据C 2011 6.3.1.3 3,结果是实现定义或实现 - 引发了定义的信号。

(word) (in[(i * 4) + 3] )中的make_state表达式中,in[…]char,它是一个带符号的8位整数(在C实现中)。根据C 2011 6.3.1.1中定义的常规整数促销,此char转换为int。如果char为否定,则生成的int为负数。然后,当它被转换为无符号的word时,效果是符号位以高24位复制。例如,如果char的值为-166(0x90),则结果为0xffffff90,但您需要0x00000090

在此代码中将char更改为unsigned char

此外,在make_state中,in[(i * 4) + 0]应在左移之前投放到word。这是因为它将以unsigned char开头,在轮班之前提升为int。如果它具有高位设置的某个值,例如0x80,则将其向左移位24位会产生一个无法在int中表示的值,例如0x80000000。根据C 2011 6.5.7 4,行为未定义。

这在大多数C实现中都不会成为问题;二进制补码通常用于有符号整数,结果将根据需要进行换行。另外,我希望这是编译器开发人员设计的模型情况,因为它是一种非常常见的代码结构。但是,为了提高可移植性,转换为word将避免溢出的可能性。

答案 1 :(得分:0)

make_state()函数会覆盖第一个参数中传递的数组。如果您将encipher_block()decipher_block()实体放入内联,则可以获得:

/* encipher_block inline */
make_state(work->state, in);
work->state[0] ^= 0xff00cd00;
make_string(work->state, in);

/* decipher_block inline */
make_state(work->state, in);    /* <-- Here's the problem */
work->state[0] ^= 0xff00cd00;
make_string(work->state, in);