c语言中奇怪的整数
Strange integers in c language
我有代码:
#include <stdio.h>
int main() {
int a = sum(1, 3);
return 0;
}
int sum(int a, int b, int c) {
printf("%d\n", c);
return a + b + c;
}
我知道我必须先声明函数,然后才能调用它们,但我想了解发生了什么。
(由gcc v6.3.0
编译)
我多次忽略了implicit declaration of function warning
和运行程序,输出是这样的:
1839551928
-2135227064
41523672
// And more strange numbers
我有 2 个问题:
1) 这些数字是什么意思?
2) 函数 main
如何知道如何在没有声明的情况下调用函数 sum
?
这是undefined behavior per 6.5.2.2 Function calls, paragraph 9 of the C standard:
If the function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function, the behavior is undefined.
在 6.5.2.2 Function calls, paragraph 6:
下允许没有原型的函数
If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined. ...
再次注意:如果传递的参数与预期的参数不匹配,则行为未定义。
在严格符合标准的 C 中,如果您在使用函数之前不声明它,它将假定 function.This 的某些默认参数类型基于具有较弱类型系统的 C 的早期版本,并仅为了向后兼容而保留。它不应该被普遍使用。
我将跳过此处的详细信息,但在您的情况下,它假设 sum 需要 2 个整数和 returns 一个整数。
如您在此处所做的那样,使用错误数量的参数调用函数是未定义的行为。当您调用 sum 时,编译器认为它需要两个整数,因此将两个整数传递给它。但是,当实际调用该函数时,它会尝试再读取一个整数 c
。由于您只传递了 2 个整数,因此 c
的 space 包含随机废话,这就是您在打印时看到的内容。请注意,它不必这样做,因为这是未定义的行为,它可以做任何事情。例如,它可以为 b 和 c 提供值。
显然这种行为令人困惑,你不应该依赖未定义的行为,所以你最好使用更严格的编译器设置进行编译,这样这个程序就不会编译。 (正确的版本会在 main 之上声明 sum。)
1) 由于您在调用函数 "sum" 时没有为参数 "c" 提供值,因此函数内的值未定义。如果你在 main 之前声明函数,你的程序甚至不会编译,你会得到 "error: too few arguments to function call" 错误。
2) 通常不会。必须在调用之前声明函数,以便编译器可以检查函数签名。在这种情况下,编译器优化为您解决了这个问题。
我不是 100% 确定 C 是否完全像这样工作,但是,您的函数调用就像内存中的堆栈一样工作。当你调用一个函数时,你的参数被放在那个堆栈上,所以在函数中你可以通过在内存中选择更少的 x 位置来访问它们。所以:
你打电话给summ(1, 3)
堆栈将有 1,顶部是 3。
执行函数时,它将看到 1º 参数的内存最后位置(它恢复 1),然后是 2º 参数之前的位置(恢复 3),但是,有一个 3º 参数,因此它访问该位置在那之前也是如此。
这个位置是垃圾,因为不是你放的,每次你都不一样运行。
希望已经足够清楚了。请记住,堆栈是倒置的,因此每次添加内容时,它都会转到前一个内存位置,而不是下一个。
我假设您问题中的代码是您实际编译的代码并且 运行ning:
int main() {
int a = sum(1, 3);
return 0;
}
int sum(int a, int b, int c) {
printf("%d\n", c);
return a + b + c;
}
对 printf
的调用无效,因为您没有所需的 #include <stdio.h>
。但这不是你要问的,所以我们会忽略它。 编辑问题以添加 include 指令。
在标准 C 中,自 1999 年标准以来,调用没有可见声明的函数(sum
在这种情况下)是 约束违规 。这意味着需要进行诊断(但符合标准的编译器仍然可以成功编译程序,如果它选择的话)。与语法错误一起,违反约束是 C 最接近于说某事是非法的。 (#error
指令除外,它必须导致翻译单元被拒绝。)
在 C99 之前,C 有一个 "implicit int
" 规则,这意味着如果您调用一个没有可见声明的函数,则会创建一个隐式声明。该声明适用于具有 return 类型 int
的函数,并且具有您传递的参数的(提升的)类型的参数。您的调用 sum(1, 3)
将创建一个隐式声明 int sum(int, int)
,并生成一个调用 ,就好像 函数是以这种方式定义的。
由于未以这种方式定义,因此行为未定义。 (很可能其中一个参数的值,也许是第三个,将从某个任意寄存器或内存位置获取,但标准说 nothing 关于调用实际做什么。)
C99(ISO C 标准的 1999 版)删除了隐含的 int
规则。如果您使用符合标准的 C99 或更高版本的编译器编译代码,则编译器需要诊断 sum(1, 3)
调用的错误。许多编译器为了与旧代码向后兼容,将打印一个非致命警告并生成 假定 定义与隐式声明匹配的代码。许多编译器在默认情况下是不符合规范的,甚至可能不会发出警告。 (顺便说一句,如果您的编译器确实打印了错误或警告消息,那么将其包含在您的问题中会非常有帮助。)
您的程序有问题。符合标准的 C 编译器必须至少警告您,并可能拒绝它。如果你 运行 它不顾警告,行为是未定义的。
我有代码:
#include <stdio.h>
int main() {
int a = sum(1, 3);
return 0;
}
int sum(int a, int b, int c) {
printf("%d\n", c);
return a + b + c;
}
我知道我必须先声明函数,然后才能调用它们,但我想了解发生了什么。
(由gcc v6.3.0
编译)
我多次忽略了implicit declaration of function warning
和运行程序,输出是这样的:
1839551928
-2135227064
41523672
// And more strange numbers
我有 2 个问题:
1) 这些数字是什么意思?
2) 函数 main
如何知道如何在没有声明的情况下调用函数 sum
?
这是undefined behavior per 6.5.2.2 Function calls, paragraph 9 of the C standard:
If the function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function, the behavior is undefined.
在 6.5.2.2 Function calls, paragraph 6:
下允许没有原型的函数If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined. ...
再次注意:如果传递的参数与预期的参数不匹配,则行为未定义。
在严格符合标准的 C 中,如果您在使用函数之前不声明它,它将假定 function.This 的某些默认参数类型基于具有较弱类型系统的 C 的早期版本,并仅为了向后兼容而保留。它不应该被普遍使用。 我将跳过此处的详细信息,但在您的情况下,它假设 sum 需要 2 个整数和 returns 一个整数。
如您在此处所做的那样,使用错误数量的参数调用函数是未定义的行为。当您调用 sum 时,编译器认为它需要两个整数,因此将两个整数传递给它。但是,当实际调用该函数时,它会尝试再读取一个整数 c
。由于您只传递了 2 个整数,因此 c
的 space 包含随机废话,这就是您在打印时看到的内容。请注意,它不必这样做,因为这是未定义的行为,它可以做任何事情。例如,它可以为 b 和 c 提供值。
显然这种行为令人困惑,你不应该依赖未定义的行为,所以你最好使用更严格的编译器设置进行编译,这样这个程序就不会编译。 (正确的版本会在 main 之上声明 sum。)
1) 由于您在调用函数 "sum" 时没有为参数 "c" 提供值,因此函数内的值未定义。如果你在 main 之前声明函数,你的程序甚至不会编译,你会得到 "error: too few arguments to function call" 错误。
2) 通常不会。必须在调用之前声明函数,以便编译器可以检查函数签名。在这种情况下,编译器优化为您解决了这个问题。
我不是 100% 确定 C 是否完全像这样工作,但是,您的函数调用就像内存中的堆栈一样工作。当你调用一个函数时,你的参数被放在那个堆栈上,所以在函数中你可以通过在内存中选择更少的 x 位置来访问它们。所以:
你打电话给summ(1, 3)
堆栈将有 1,顶部是 3。
执行函数时,它将看到 1º 参数的内存最后位置(它恢复 1),然后是 2º 参数之前的位置(恢复 3),但是,有一个 3º 参数,因此它访问该位置在那之前也是如此。
这个位置是垃圾,因为不是你放的,每次你都不一样运行。
希望已经足够清楚了。请记住,堆栈是倒置的,因此每次添加内容时,它都会转到前一个内存位置,而不是下一个。
我假设您问题中的代码是您实际编译的代码并且 运行ning:
int main() {
int a = sum(1, 3);
return 0;
}
int sum(int a, int b, int c) {
printf("%d\n", c);
return a + b + c;
}
对 printf
的调用无效,因为您没有所需的 #include <stdio.h>
。但这不是你要问的,所以我们会忽略它。 编辑问题以添加 include 指令。
在标准 C 中,自 1999 年标准以来,调用没有可见声明的函数(sum
在这种情况下)是 约束违规 。这意味着需要进行诊断(但符合标准的编译器仍然可以成功编译程序,如果它选择的话)。与语法错误一起,违反约束是 C 最接近于说某事是非法的。 (#error
指令除外,它必须导致翻译单元被拒绝。)
在 C99 之前,C 有一个 "implicit int
" 规则,这意味着如果您调用一个没有可见声明的函数,则会创建一个隐式声明。该声明适用于具有 return 类型 int
的函数,并且具有您传递的参数的(提升的)类型的参数。您的调用 sum(1, 3)
将创建一个隐式声明 int sum(int, int)
,并生成一个调用 ,就好像 函数是以这种方式定义的。
由于未以这种方式定义,因此行为未定义。 (很可能其中一个参数的值,也许是第三个,将从某个任意寄存器或内存位置获取,但标准说 nothing 关于调用实际做什么。)
C99(ISO C 标准的 1999 版)删除了隐含的 int
规则。如果您使用符合标准的 C99 或更高版本的编译器编译代码,则编译器需要诊断 sum(1, 3)
调用的错误。许多编译器为了与旧代码向后兼容,将打印一个非致命警告并生成 假定 定义与隐式声明匹配的代码。许多编译器在默认情况下是不符合规范的,甚至可能不会发出警告。 (顺便说一句,如果您的编译器确实打印了错误或警告消息,那么将其包含在您的问题中会非常有帮助。)
您的程序有问题。符合标准的 C 编译器必须至少警告您,并可能拒绝它。如果你 运行 它不顾警告,行为是未定义的。