C功能指针行为

时间:2010-01-21 22:56:32

标签: c function-pointers

对于一个类,我正在编写一个简单的crypt函数。一切都按预期工作:

int crypt(char *key, int (*callback)(int, char*, int, int))
{
    int c, i = 0, n = strlen(key);
    while((c = fgetc(stdin)) != EOF)
    {
        // only change the char if it's printable
        if(c >= 32 && c <= 126)
            c = callback(c, key, n, i);

        fputc(c, stdout);
        i++;
    }
}

int encryptCallback(int c, char *key, int n, int i)
{
    return (c - 32 + key[i % n]) % 95 + 32;
}
int decryptCallback(int c, char *key, int n, int i)
{
    return (c - 32 - key[i % n] + 3 * 95) % 95 + 32;
}

使用教授的测试用例,一切正常。但是当我最初编写回调时,我忽略了返回。它们的编码如下:

int encryptCallback(int c, char *key, int n, int i)
{
    c = (c - 32 + key[i % n]) % 95 + 32; // no return
}
int decryptCallback(int c, char *key, int n, int i)
{
   c = (c - 32 - key[i % n] + 3 * 95) % 95 + 32; // no return
}

当我使用非返回回调运行代码时,在我应用测试用例时输出仍然正确(是的,我重新编译,我不是'运行旧代码)。当我用-Wall编译时,我只注意到'错误'。

所以我很困惑。为什么c(在crypt()中)在分配给callback的返回值(回调不返回任何内容)后得到正确的值? c不是指针,它只是一个常规的int。

P.S。 The assignment与函数指针无关。

6 个答案:

答案 0 :(得分:7)

如果调用者尝试使用函数的返回值,则在return以外的返回类型的函数中未能使用void是未定义的行为。

在这种情况下,您明显看到的未定义行为是“它返回了我想要的值,无论如何”。你刚刚(非)幸运。 (但它与函数指针无关。)


如果要在x86上进行编译,那么它解决的根本原因是大多数x86调用约定指定在int寄存器中返回类型为%eax的返回值。在您的情况下,编译器还决定使用此寄存器来计算c的新值 - 因此恰好将该值重新显示为“返回值”。如果你在计算c后在函数中做了一些更多的计算,你就会看到别的东西。

答案 1 :(得分:1)

这可能与方式有关,在您的平台上返回函数值。例如,在许多基于x86的平台上,寄存器EAX用于返回整数值。如果正确的值碰巧在函数末尾的那个寄存器中(由于计算正在进行),那么调用者就不会感觉到差异:就好像这个值是故意放在那里{{1声明。

幸运的是,你的代码实际上是第一次工作。

答案 2 :(得分:1)

我想到的是(尽管可能很愚蠢)是c被分配给一个用作架构中“返回”寄存器的寄存器。

答案 3 :(得分:0)

它没有定义它将做什么,因为那应该是一个不可恢复的编译错误。我会看看makefile / ide /编译器,看看为什么它不应该生成可执行文件

答案 4 :(得分:0)

Intel x86架构 BASICALLY 累加器机器。这意味着有一个寄存器非常适合算术和逻辑结果。通常,这种机器架构的编译器会发出代码,将结果留在该寄存器中,并从该寄存器中存储。这些编译器实现的函数返回约定通常会指定该寄存器中的返回值,因为这是它最便宜的地方(因为那是计算它的代码可能离开它的地方)。所有这些加起来“你很幸运。”

答案 5 :(得分:0)

反汇编您的程序,您可能会看到发生了什么。将你的一个函数作为c = something和c = something的唯一区别;返回(C);是popq%rbx上方的行的添加。我的x86生锈了,但我认为它可以解决它。 eax恰好以结果结束,并且堆栈中的eax或-12是为x86返回保留返回值的位置。

imull   $95, %eax, %eax
movl    %ecx, %edx
subl    %eax, %edx
movl    %edx, %eax
addl    $32, %eax
movl    %eax, -12(%rbp)
movl    -12(%rbp), %eax
popq    %rbx
leave
ret
.cfi_endproc