我刚刚遇到一个奇怪的问题,我正在尝试printf一个整数变量,但我忘了指定变量名,即
printf("%d");
而不是
printf("%d", integerName);
令人惊讶的是程序编译,有输出,它不是随机的。事实上,它恰好是我想要首先打印的整数,恰好是m-1。
只要程序继续运行,错误的printf
语句就会一直输出m-1 ...换句话说,它的行为就像语句读取的那样
printf("%d", m-1);
有谁知道这种行为背后的原因?我在没有任何命令行选项的情况下使用g ++。
#include <iostream>
#define maxN 100
#define ON 1
#define OFF 0
using namespace std;
void clearArray(int* array, int n);
int fillArray(int* array, int m, int n);
int main()
{
int n = -1, i, m;
int array[maxN];
int found;
scanf("%d", &n);
while(n!=0)
{
found=0;
m = 1;
while(found!=1)
{
if(m != 2 && m != 3 && m != 4 && m != 6 && m != 12)
{
clearArray(array, n);
if(fillArray(array, m, n) == 0)
{
found = 1;
}
}
m++;
}
printf("%d\n");
scanf("%d", &n);
}
return 0;
}
void clearArray(int* array, int n)
{
for(int i = 1; i <= n; i++)
array[i] = ON;
}
int fillArray(int* array, int m, int n)
{
int i = 1, j, offCounter = 0, incrementCounter;
while(offCounter != n)
{
if(*(array+i)==ON)
{
*(array+i) = OFF;
offCounter++;
}
else
{
j = 0;
while((*array+i+j)==OFF)
{
j++;
}
*(array+i+j) = OFF;
offCounter++;
}
if(*(array+13) == OFF && offCounter != n) return 1;
if(offCounter ==n) break;
incrementCounter = 0;
while(incrementCounter != m)
{
i++;
if(i > n) i = 1;
if(*(array+i) == ON) incrementCounter++;
}
}
return 0;
}
答案 0 :(得分:25)
你说“令人惊讶的是程序编译”。实际上,这并不奇怪。 C&amp; C ++允许函数具有可变参数列表。 printf的定义是这样的:
int printf(char*, ...);
“...”表示该函数有零个或多个可选参数。事实上,C有可选参数的主要原因之一是支持printf&amp; amp; scanf系列函数。
C没有printf函数的特殊知识。在您的示例中:
printf("%d");
编译器不分析格式字符串并确定缺少整数参数。这是完全合法的C代码。您缺少参数的事实是仅在运行时出现的语义问题。 printf函数将假定您已提供参数并在堆栈中查找它。它会接收发生在那里的任何事情。只是在您的特殊情况下,它正在打印正确的东西,但这是一个例外。通常,您将获得垃圾数据。这种行为因编译器而异,并且也会根据您使用的编译选项而改变;如果你打开编译器优化,你可能会得到不同的结果。
正如我的回答中的一条评论所指出的,一些编译器具有“lint”功能,可以实际检测错误的printf / scanf调用。这涉及编译器解析格式字符串并确定预期的额外参数的数量。这是非常特殊的编译器行为,并且在一般情况下不会检测错误。即如果您编写自己的“printf_better”函数,该函数与printf具有相同的签名,则编译器将不会检测是否缺少任何参数。
答案 1 :(得分:9)
看起来像这样。
printf("%d", m);
在大多数系统中,字符串的地址将被推送到堆栈上,然后'm'
作为整数(假设它是int / short / char)。没有警告,因为printf
基本上被声明为'int printf(const char *, ...);'
- 意思是'任何事情都有'。
所以,既然'任何事情都会发生',那么当你把变量放在那里时会发生一些奇怪任何小于int的整数类型都是int - 就像那样。完全没有发送也没关系。
在printf实现中(或者至少是一个'简单'实现),您会发现va_list
和va_arg
的使用(某些时候名称根据一致性略有不同)。这些是实现用于遍历参数列表的“...”部分的内容。这里的问题是没有类型检查。由于没有类型检查,printf
会在查看格式字符串("%d")
时从执行堆栈中提取随机数据,并认为接下来应该是'int'
。
在黑暗中随机拍摄会说你在printf之前调用的函数可能会通过'm-1'
,因为它是第二个parm?这是许多可能性中的一种 - 但如果碰巧是这种情况,那将会很有趣。 :)
顺便说一下 - 大多数现代编译器(我相信GCC?)都有警告可以启用来检测这个问题。我相信Lint也是如此。不幸的是,我认为使用VC你需要使用/ analyze标志而不是免费获得。
答案 2 :(得分:3)
它从堆栈中获得了一个int。
答案 3 :(得分:1)
你正凝视着堆叠。更改优化程序值,这可能会更改。更改变量声明的顺序(特别是)m
。使m
成为寄存器变量。将m
设为全局变量。
你会看到发生了什么变化。
这类似于您在执行简单I / O时获得的着名缓冲区溢出黑客。
答案 4 :(得分:1)
虽然我非常怀疑这会导致内存违规,但你得到的整数是未定义的垃圾。
答案 5 :(得分:0)
您找到了一个行为。它可能是任何其他行为,包括无效的内存访问。