我在Ubuntu 14.0.4中使用GNU C.我编写了一个CRC_XOR()函数,并使用相同的输入参数多次调用它。但是很奇怪,每次通话有时会得到不同的结果。出了什么问题? 示例代码在这里:
#include <stdio.h>
#include <stdlib.h>
char CRC_XOR(char *as_Pkt,int ai_len);
void DO_Get_CRC(void);
int main(void)
{
DO_Get_CRC(); //get 01 00 02 03
DO_Get_CRC(); //get 01 00 02 03
DO_Get_CRC(); //get 01 00 02 00 (strange?)
DO_Get_CRC(); //get 01 00 02 03
DO_Get_CRC(); //get 01 00 02 00 (strange?)
DO_Get_CRC(); //get 01 00 02 03
DO_Get_CRC(); //get 01 00 02 00 (strange?)
exit(0);
}
/*
use same input to invoke CRC_XOR()
*/
void DO_Get_CRC(void)
{
char ls_pkt[20];
int li_idx;
short li_max = 512;
int li_len = 0;
ls_pkt[li_len++] = 0x01; //station ID
ls_pkt[li_len++] = 0x00; //length-low byte
ls_pkt[li_len++] = 0x02; //length-high byte
ls_pkt[li_len++] = CRC_XOR(ls_pkt,li_len);
ls_pkt[li_len] = 0;
for (li_idx=0; li_idx<li_len;li_idx++) {
printf("%02X ", ls_pkt[li_idx]); //display in hexdigits
}
printf("\n");
}
/*
return 1 byte of CRC by XOR byte array
*/
char CRC_XOR(char *as_Pkt,int ai_len)
{
int li_idx;
char lc_CRC = 0x00;
for (li_idx=0; li_idx < ai_len; li_idx++){
lc_CRC ^= as_Pkt[li_idx]; //XOR each byte
}
return (char)(lc_CRC & 0x000000FF); //return CRC byte
}
答案 0 :(得分:8)
您有未定义的行为。
原因是因为没有分配的序列点。
有关
ls_pkt[li_len++] = CRC_XOR(ls_pkt,li_len);
您不知道li_len++
是否在函数调用中li_len
的评估之前或之后发生。这反过来可能意味着li_len
可能等于4
,这意味着您的CRC_XOR
函数将使用尚未初始化的ls_pkt[4]
。
由于ls_pkt[4]
在分配之前未初始化,因此其值将为 indeterminate ,并且可能看似随机。
简单的解决方案是在分配后增加li_len
:
ls_pkt[li_len] = CRC_XOR(ls_pkt,li_len);
++li_len;
答案 1 :(得分:1)
我最初无法在我的x86中以64位模式重现,但它似乎很容易在我的计算机上以32位模式重现;也许不同的堆栈宽度是原因。
% gcc crc.c -m32
% ./a.out
01 00 02 03
01 00 02 00
01 00 02 03
01 00 02 00
01 00 02 03
01 00 02 00
01 00 02 03
我在这里对行进行了编号,这样就可以很容易地看到它们与汇编程序输出中的.loc
行的对应关系:
20 void DO_Get_CRC(void)
21 {
22 char ls_pkt[20];
23 int li_idx;
24 short li_max = 512;
25 int li_len = 0;
26
27 ls_pkt[li_len++] = 0x01; //station ID
28 ls_pkt[li_len++] = 0x00; //length-low byte
29 ls_pkt[li_len++] = 0x02; //length-high byte
30 ls_pkt[li_len++] = CRC_XOR(ls_pkt,li_len);
31 ls_pkt[li_len] = 0;
32 for (li_idx=0; li_idx<li_len;li_idx++) {
33 printf("%02X ", ls_pkt[li_idx]); //display in hexdigits
34 }
35
36 printf("\n");
37 }
将其编译成程序集,我们得到以下汇编程序输出:
DO_Get_CRC:
.LFB3:
.loc 1 21 0
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %esi
pushl %ebx
subl $48, %esp
.cfi_offset 6, -12
.cfi_offset 3, -16
call __x86.get_pc_thunk.bx
addl $_GLOBAL_OFFSET_TABLE_, %ebx
.loc 1 21 0
movl %gs:20, %eax
movl %eax, -12(%ebp)
xorl %eax, %eax
.loc 1 24 0
movw $512, -42(%ebp)
.loc 1 25 0
movl $0, -36(%ebp) // int li_len = 0
.loc 1 27 0
movl -36(%ebp), %eax // eax = li_len
leal 1(%eax), %edx // edx = eax + 1
movl %edx, -36(%ebp) // li_len = edx
movb $1, -32(%ebp,%eax) // ls_pkt[eax] = 1
.loc 1 28 0
movl -36(%ebp), %eax // eax = li_len
leal 1(%eax), %edx // edx = eax + 1
movl %edx, -36(%ebp) // li_len = edx
movb $0, -32(%ebp,%eax) // ls_pkt[eax] = 0
.loc 1 29 0
movl -36(%ebp), %eax // eax = li_len
leal 1(%eax), %edx // edx = eax + 1
movl %edx, -36(%ebp) // li_len = edx
movb $2, -32(%ebp,%eax) // ls_pkt[eax] = 2
.loc 1 30 0
movl -36(%ebp), %esi // esi = li_len
leal 1(%esi), %eax // eax = esi + 1
movl %eax, -36(%ebp) // li_len = eax --li_len is now **4**
subl $8, %esp
pushl -36(%ebp) // push the value of li_len (2nd arg)
leal -32(%ebp), %eax // load address of ls_pkt
pushl %eax // and push ls_pkt (1śt arg)
call CRC_XOR // call CRC_XOR
后增量被编码为“加载有效地址”指令,X86汇编器中的常见技巧是使用地址计算硬件进行常数运算,因为指令较小。无论如何,总共 4 leal 1(%XXX), %YYY
后跟movl %YYY, -36(%ebp)
,其中-36(%ebp)
是变量li_len
的位置,这意味着{{ 1}}增加 4 次,然后将其值作为li_len
的参数压入堆栈。但是,如果代码在其他位置发生变异,则很容易发生,因此相同的编译器会在调用函数之前生成仅增加CRC_XOR
3次的代码,证明它们确实是不确定的顺序。
答案 2 :(得分:0)
删除此行中的;
void DO_Get_CRC(void); --> void DO_Get_CRC(void)
修改此行,因为您希望传递li_len
,但在分配
li_len+1
而传递++
ls_pkt[li_len++] = CRC_XOR(ls_pkt,li_len); --> ls_pkt[li_len++] = CRC_XOR(ls_pkt,li_len-1);
它的工作正常没有任何问题
main.c:31:27:注意:每个未声明的标识符仅针对它出现的每个函数报告一次
sh-4.2 $ gcc -o main * .c
sh-4.2 $ main
01 00 02 03
01 00 02 03
01 00 02 03
01 00 02 03
01 00 02 03
01 00 02 03
01 00 02 03