在 C 中传递参数时的整数溢出

Integer Overflow when Passing Arguments in C

提问人:Derrick Mei 提问时间:9/3/2023 最后编辑:chqrlieDerrick Mei 更新时间:9/4/2023 访问量:123

问:

我想我遇到了整数溢出问题,我不确定如何解决它。我刚从 Python 和 JS 开始使用 C,这对我来说都是新的。

我在下面举了一个非常简单的例子来说明正在发生的事情。我正在将一个参数从函数传递到另一个函数以乘以 3,但是当它传递时,数字会溢出。数学在函数中起作用。mainmain

#include <stdio.h>

long long calc(number) {
    return number * 3;
}

int main(void)
{
    long long digits = 1111111111111;
    long long result = calc(digits);
    printf("calc result: %lld\n", result);
    
    long long mainTimes3 = digits * 3;
    printf("main result: %lld\n", mainTimes3);

    return 0;
}

我收到错误消息

main.c:3:11:警告:“number”的类型默认为“int” [-Wimplicit-int]”

正在显示printf

calc result: 438711637
main result: 3333333333333
c 参数传递 整数溢出

评论

4赞 Weijun Zhou 9/3/2023
将函数声明更改为 。long long calc(long long number)
0赞 David C. Rankin 9/3/2023
正如@Murenik在他的回答中所说,在 C 中,如果你没有明确地为变量(或函数等)提供类型,那么一切都默认为 .Your 将超过可表示为 .(有符号溢出 -> 未定义的行为intnumber * 3int)
2赞 Weijun Zhou 9/3/2023
请注意,这将不再是有效的代码,因为它删除了隐式参数和隐式返回类型。CC23intint
2赞 John Bollinger 9/3/2023
@WeijunZhou,即使在 C99 中,给出的代码也是无效的。从该版本开始,K&R风格的函数定义要求在声明列表中声明每个参数标识符。这是显式指定的,但它伴随着 C99 总体上删除隐式类型这一事实,因此每个函数参数的类型都需要显式声明。
1赞 John Bollinger 9/3/2023
@WeijunZhou,这是正确的。

答:

4赞 Mureinik 9/3/2023 #1

编译器警告提示了该问题 - 由于您没有显式定义参数的类型,因此编译器假定它是 .因此也是一个 ,并且会发生整数溢出。numberintnumber * 3int

要解决此问题,请将其显式定义为您想要的类型:long long

long long calc(long long number){
    /* Here ---^ */
    return number * 3;
}

评论

0赞 Derrick Mei 9/4/2023
非常感谢!我尝试了((长长)数字),因为我认为你需要括号,但我不知道我从哪里得到它。
1赞 ldn 9/3/2023 #2

在 C 中,当您未为 func 参数指定数据类型时,它默认为 int。 ->您收到警告。main.c:3:11: warning: type of number defaults to int [-Wimplicit-int]

在 C 程序中,我们应该记住在我们定义的函数中指定参数的数据类型。

作为您的示例,我们应该将其定义为long long

long long calc(long long number){}

评论

0赞 Jonathan Leffler 9/4/2023
本世纪以来,任何标准 C 版本都不允许使用非类型化参数。许多编译器继续允许它,因为它在 C90 和标准之前的 C 中是有效的,但学习 C 的人不应该使用 C90 提供的许可证。
0赞 ldn 9/4/2023
是的,没错。在 C99 中,非类型化参数已从 C 标准中删除,非类型化参数可能会导致错误,并且难以编写可移植代码以在多个平台上运行。
3赞 John Bollinger 9/3/2023 #3

我想我遇到了整数溢出问题,我不确定如何解决它。我刚从 Python 和 JS 开始使用 C,这对我来说都是新的。

然后,您需要习惯的一件事是,在 C 语言中,您必须显式指定每个变量、每个函数参数、每个结构或联合成员的数据类型,以及每个函数的返回类型。请注意,数组类型包括数组元素的数据类型,指针类型包括指针指向的对象或函数的数据类型。C99 及更高版本中的每个表达式都有一个数据类型,该数据类型可能根据类型转换可以追溯到一个或多个显式类型声明和/或数据类型通过其形式传达的常量和文字。

原始 C 对默认类型(键入)和向后兼容性进行了规定,这被延续到 C 标准的第一个版本中。一些编译器仍然支持(仍然)向后兼容,但从 C99 开始,标准 C 不提供默认类型。因此,在(否则)C99 或更高版本模式下支持它的编译器正在实现扩展。int

因此,此函数的问题:

long long calc(number){
    return number*3;
}

表示参数的数据类型尚未声明。这就是警告告诉你的。number

第二个问题是,这使得函数定义成为K&R风格的定义,多年来一直不受欢迎,并在C23中被删除。虽然您可以在不从 K&R 样式转换的情况下解决键入问题,但通过将所需的数据类型放入函数参数列表中,转换为 ANSI 样式会更容易更好,如下所示:

long long calc(long long number) {
    return number * 3;
}

最后注意:以你的 C 经验水平,你不应该认为编译器发出的任何警告都是可以忽略的。确保您理解并修复每个问题,即使编译器可能会在您尚未生成可执行文件时生成可执行文件。

1赞 Luis Colorado 9/4/2023 #4

这个定义:

long long calc(number) {
    return number * 3;
}

是旧的 Kernighan & Ritchie C 语言定义参数声明方式。缺少定义的参数默认为 ,以强制执行溢出。numberint

您可以使用已弃用的旧 K&R 时尚来解决它:

long long calc(number)
lont long number;
{
    ...

或者使用新的 ANSI-C(不应该说新的,因为它现在已经很旧了):

long long calc(long long number)
{
    ...