C 使用相同的输入多次调用函数,但得到不同的返回值

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

我在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 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汇编程序中,使用地址计算硬件进行常数运算是一种常见的技巧,因为指令较小。无论如何,总共有4
leal 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