C 使用相同的输入多次调用函数,但得到不同的返回值
我在Ubuntu 14.0.4中使用GNUC。我编写了一个CRC_XOR()函数,并使用相同的输入参数多次调用它。但很奇怪,每次通话有时可能会得到不同的结果。怎么了? 示例代码如下:C 使用相同的输入多次调用函数,但得到不同的返回值,c,xor,crc,C,Xor,Crc,我在Ubuntu 14.0.4中使用GNUC。我编写了一个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
#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
}
#包括
#包括
字符CRC\u XOR(字符*as\u Pkt,国际语言);
void DO_Get_CRC(void);
内部主(空)
{
DO_Get_CRC();//Get 01 00 02 03
DO_Get_CRC();//Get 01 00 02 03
是否获取CRC();//获取01 00 02 00(奇怪?)
DO_Get_CRC();//Get 01 00 02 03
是否获取CRC();//获取01 00 02 00(奇怪?)
DO_Get_CRC();//Get 01 00 02 03
是否获取CRC();//获取01 00 02 00(奇怪?)
出口(0);
}
/*
使用相同的输入调用CRC_XOR()
*/
void DO_Get_CRC(void)
{
char ls_pkt[20];
国际图书馆;
短li_max=512;
int li_len=0;
ls_pkt[li_len++]=0x01;//站点ID
ls_pkt[li_len++]=0x00;//长度低字节
ls_pkt[li_len++]=0x02;//长度高字节
ls_-pkt[li_-len++]=CRC_-XOR(ls_-pkt,li_-len);
ls_pkt[li_len]=0;
对于(li_idx=0;li_idx删除此行中的;
)
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您有未定义的行为
原因是没有赋值的序列点
为了
您不知道li_len++
是在函数调用中对li_len
求值之前还是之后发生的。这反过来可能意味着li_len
可能等于4
,这意味着您的CRC_XOR
函数将使用尚未初始化的ls\u pkt[4]
由于ls_pkt[4]
在赋值之前未初始化,其值将是不确定的,并且可能看起来是随机的
简单的解决方案是在分配后增加li_len
:
ls_pkt[li_len] = CRC_XOR(ls_pkt,li_len);
++li_len;
我最初无法在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
post增量被编码为“加载有效地址”指令,在X86汇编程序中,使用地址计算硬件进行常数运算是一种常见的技巧,因为指令较小。无论如何,总共有4leal 1(%XXX),%YYY
后跟movl%YYY,-36(%ebp)
,其中-36(%ebp)
是变量li_len
的位置,这意味着li_len
在其值作为CRC\u XOR
的参数推送到堆栈上之前,被递增4次。但是,如果代码在其他地方发生变异,则很容易发生这种情况,从而同一编译器生成递增li_len的代码ode>在调用函数之前只调用了3次,证明它们确实是不确定序列。您不应该构建的程序。例如,li_len
是什么?它定义在哪里?它初始化为什么?至于您的问题,请Eric Lippert花些时间阅读。并学习如何使用调试器,这是这是解决此类问题的正确工具。对不起,我删除了li_len,当然是4。您是否删除了任何其他可能具有实质意义的内容?我无法复制您的行为。一个完全无关的注意事项是:main
函数应该声明为返回int
。这是C规范所要求的。更相关的是ed注意:在构建时是否收到任何警告?如果启用更多警告(无论如何,您确实应该这样做,我建议在编译时至少添加-Wall
)?如果你收到警告,他们会怎么说?他们可能与你的问题有关吗?那个放错的分号应该会导致生成错误,而不是意外的结果。@Someprogrammerdude感谢你的指点,因为我没有粘贴重要的代码行更改否,那个修复是绝对错误的。请阅读并理解@Someprogrammerdude的回答。@AnttiHaapala不知道您指的是什么。但根据逻辑,CRC_XOR函数应该收到3,但由于增量,它收到4。@ThiruShetty您的“修复”重复同样的问题。一般来说,带有副作用的编程是一个坏主意。它只会给你带来麻烦。我认为这是在以下条款下,即序列点之间的所有读取都应该直接用于写入。
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