C函数有趣的返回行为

时间:2019-03-04 19:53:07

标签: c return return-value return-type

我有一个函数,该函数以char数组的形式进行数学运算,并返回一个int结果(这全部有效,并且仅是出于上下文考虑,与问题无关)。

自然,我的函数定义是:int calc(char * operation){},它期望返回一个int。

在解析字符串以确定操作数和要执行的操作之后,我将结果分配给变量。我只是意识到我忘记了将return语句放入函数中,但是我仍然得到了正确的结果...

这是函数。我本来忘记了最后一行。

// Function to return int results of operation specified in char* (argv[1])
int calc(char* operation)
{
    int op_index = 0;
    int end_index = 0;
    for (int i = 0; i < 128; i ++)
    {
        if ((operation[i] < 48 || operation[i] > 57) && op_index == 0)
            op_index = i;
        if (operation[i] == '\0')
        {
            end_index = i;
        i = 128;
        }
    }

    int opa = 0;
    int opb = 0;
    for (int i = 0; i < op_index; i ++)
        opa += (operation[i]-48)*power(10, op_index - (i+1));
    for (int i = op_index+1; i < end_index; i ++)
        opb += (operation[i]-48)*power(10, end_index - (i+1));

    int res = 0;
    if (operation[op_index] == '+')
        res = opa + opb;
    else if (operation[op_index] == '-')
        res = opa - opb;
    else if (operation[op_index] == '*')
        res = opa * opb;
    else if (operation[op_index] == '/')
        res = opa / opb;
    else if (operation[op_index] == '%')
        res = opa % opb;

    // This is the line that I had forgotten... but still got the right results when calling this function
    return res;
}

有人对此有解释吗?我的猜测是,默认情况下它将返回最后一个函数调用的结果,由于最终语句的if / else结构,这将是正确的。

谢谢!

2 个答案:

答案 0 :(得分:5)

main函数外,任何定义为返回值的函数都必须这样做。如果不是,并且调用函数尝试使用返回的值,则您调用了undefined behavior

这在C standard的6.9.1p12节中指定:

  

如果达到了终止函数的},并且   函数调用由调用者使用,其行为是不确定的。

在这种情况下,您“幸运地”知道该程序可以正常工作,但是不能保证总是如此。您的程序看似无关的更改可能会更改未定义行为的显示方式。

答案 1 :(得分:5)

技术上未定义的行为。

如果这是x86 Intel,则可能发生的情况是,从函数返回之前执行的数学运算恰好是在EAX寄存器中保留了预期的返回值。对于返回整数的函数,EAX寄存器也是返回值如何传递回调用方的方法。

calc函数的尾部已生成如下所示的程序集:

    int res = 0;
 mov         dword ptr [res],0  
    if (operation[op_index] == '+')
 mov         eax,dword ptr [operation]  
 add         eax,dword ptr [op_index]    // MATH OPERATION WINDS UP IN EAX REGISTER
 movsx       ecx,byte ptr [eax]  
 cmp         ecx,2Bh  
 jne         calc+149h (05719F9h)  

并调用如下代码:

int x;
x = calc((char*)"4+5");
printf("%d\n", x);

生成的程序集是这个

    x = calc((char*)"4+5");
 push        offset string "4+5" (0E87B30h)  
 call        _calc (0E8128Ah)  
 add         esp,4  
 mov         dword ptr [x],eax   // TAKE EAX AS RESULT OF FUNCTION AND ASSIGN TO X

但是,当我将项目设置从调试版本切换到优化零售时,所有赌注都关闭了。编译器和链接器将开始内联程序集,进行疯狂的优化等。甚至它甚至会围绕函数未返回任何事实的事实进行优化……事实上,它将在{附近产生一个错误。 {1}}语句抱怨printf尚未初始化,即使它是根据calc的结果显式分配的。

因此简短的答案是您很幸运。但是我想指出为什么它“恰好起作用”。