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

时间:2017-08-10 05:54:31

标签: c xor crc

我在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
}

3 个答案:

答案 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