C函数接受值,即使它的参数是无效的

Posted

技术标签:

【中文标题】C函数接受值,即使它的参数是无效的【英文标题】:C Function accepting value even when it's parameter is void 【发布时间】:2015-07-19 17:54:42 【问题描述】:

我是编程新手,我在定义函数之前阅读了我们需要定义它的原型。如果我们不希望我们的函数接受任何值,那么我们将 void 作为参数写入。所以只是为了检查它,我写了这个小程序,在定义函数原型时,我把它的参数写为 void,而在写它的定义时,它写了 int a 作为它的参数(希望我会得到一个错误,但我从来没有得到一个)。然后我编译并运行它给我这个输出的程序

这个程序打印从 1 到 3 的所有数字的平方

1 的平方是 1:

2 的平方是 4:

3的平方是9:

在此之后,我将定义中的参数更改为 float a(只是为了检查程序是否仍然可以工作),这次我得到了不同的输出,如下所示

这个程序打印从 1 到 3 的所有数字的平方

1的平方是0:

2的平方是0:

3的平方是0:

.

谁能解释一下这里发生了什么?为什么我的编译器没有抛出错误,为什么我得到两个完全不同的 int 和 float 输出?我正在使用带有更新 4 的 Visual Studio Professional 2013!

//program to calulate square using functions 

#include <stdio.h>
#pragma warning (disable:4996)

int square(void);

int main()


    int i;

    printf("This program prints the square of all the numbers starting from 1 to 3");

    for (i = 1; i <= 3; i++)  
            printf("\n\nthe square of %d is %d : \t",i,square(i));

    getch();
    return 0;



square(a)


    a = a*a;
    return a;


【问题讨论】:

一方面我想回答未定义的行为,另一方面我想回答与旧标准(甚至预标准)的向后兼容性。 @JoachimPileborg 先生,请随时回答他们两个问题! :) 另外,您禁用的警告是什么?你不应该禁用警告,因为它们通常表明你做了一些你不应该做的事情,并且可能导致未定义的行为。 @JoachimPileborg 我禁用了 Visual Studio 一直希望我使用 scanf_s 而不是 scanf 的警告! “我禁用了警告...”。我已经将其作为近距离投票的理由(无法复制)。您禁用警告并想知道为什么您的编译器不抱怨? 【参考方案1】:

旧的 C 标准允许声明不带参数的函数,然后假定参数的类型为 int。只要一切都匹配,一切正常,但如果事情不匹配,可能会发生有趣的事情。

这一切都归结为calling convention。调用代码将参数放在某处(通常是堆栈和寄存器),然后函数从某处获取它们,如果这个“某处”相同,并且数据类型匹配,则一切正常。当它们不匹配时,您会得到未定义的行为。

所以int 版本有效,因为一切都匹配。编译器会警告您,它不知道您的代码是否真的可以工作(或在以后的 C 标准版本和 C++ 中给出实际错误),但这只是警告,这意味着您需要确保它正常(你真的不想这样做,这是编译器的工作,所以启用警告并修复它们!)。

您提到的float 版本是未定义的行为。计算机能够做的任何事情都可能发生(包括闯入您的银行帐户并在您下次使用网上银行时清空它,这基本上是许多恶意软件实际感染计算机的方式,利用编程错误)。但是在这里你可能得到零,因为浮点参数和返回值是在 FPU 寄存器中传递的,函数不会改变,因此返回值恰好为零。

【讨论】:

【参考方案2】:

符合标准的 C 编译器必须为您的问题中的程序发出至少一个诊断信息。

您的square 函数声明为:

int square(void);

表示它不接受任何参数并返回int 结果。然后您将其称为:

square(i)

传递一个参数。这是一个违反约束,这意味着编译器必须发出诊断(可能是警告或致命错误)。

然后你定义它为:

square(a)  /* ... */ 

这是一个老式的定义。它说square 接受int 参数并返回int 结果(在过时的“隐式int”规则下)。但是该定义在调用时不可见,因此编译器将仅使用声明来处理调用。

根据Microsoft's documentation,这个:

#pragma warning (disable:4996)

禁用“已弃用”功能的警告。它不应该应用于您程序中的任何内容,因此您仍然应该在不正确的调用 square(i) 时收到警告或致命错误。

您的编译器有问题,或者更有可能是您问题中的代码或您对编译器行为的描述不正确。

请从您的问题中复制并粘贴确切的代码并尝试编译它。如果它确实在没有警告或错误的情况下编译,则说明您的编译器或您使用它的方式存在严重问题。

(当然,square 的声明、定义和调用都应该是一致的,但你的问题是关于为什么编译器不诊断错误,而不是关于如何在源代码中修复它。)

至于为什么intfloat 得到不同的输出,如果函数的声明和定义不一致,任何调用都会导致未定义的行为。当您将定义更改为使用float 时,生成的代码可能会传递一个int 值,该函数错误地将其解释为float 值。结果毫无意义。

更新: 现在我已经看到了您的屏幕截图,看起来您的编译器正在做一些我没想到的事情。它警告声明int square(void) 有一个无效参数列表。就其本身而言,该声明是完全有效的;这只是一个问题,因为以下 定义 使用 int 参数定义了 square。它一定是决定在它看到不兼容的定义之后对声明发出警告

真正让我吃惊的是它没有警告square(i) 的调用。该调用与定义(在它之后)一致,但与声明(在它之前)不一致。

C 设计为一次性处理。构造的合法性取决于它前面的代码,而不是它后面的代码。

但 C 标准强加的唯一要求是编译器必须发出至少一个诊断。它没有说明该诊断的措辞,也没有要求它是一个致命的错误。在这种情况下,代码违反了约束(因为 square 的声明和定义不兼容),在我看来,它应该拒绝该程序——但警告符合标准的要求。

不管编译器的作者做出什么决定,它确实产生了几条警告消息,你不应该忽略它们。它(也许间接地)指出了您的代码存在的问题,您应该解决这些问题。编译器警告您的代码这一事实有时比警告的实际内容更能提供信息。

【讨论】:

先生,我还添加了我的程序的屏幕截图和我在编译时得到的警告以及我得到的输出!如果屏幕截图不可见,这里是图片的链接onedrive.live.com/… 它实际上警告的是调用而不是声明。见行号。或使用online Visual C++ compiler 和/TC 编译为C。

以上是关于C函数接受值,即使它的参数是无效的的主要内容,如果未能解决你的问题,请参考以下文章

默认参数和函数指针作为函数参数C ++

深度学习笔记33_函数式API及多输入模型的建立

Linux C:访问共享内存失败并出现“无效参数”,即使它刚刚创建

请问这个C语言中有参数的函数是怎么传递值的,调用的时候没有参数啊。

C语言线程函数参数问题

c语言函数参数传递方向