题
我刚刚遇到了一个奇怪的问题,我想给printf一个整型变量,但我忘了指定变量名,即。
printf("%d");
代替
的printf("%d", integerName);
令人惊讶的是程序编译,有输出,它是不是随机的。事实上,它正好是非常整数我想在第一位置,这恰好是m-1个打印。
在errorneous printf
语句将始终如一地输出m-1个,只要该程序继续运行...换句话说,它的行为就好像该语句读取
printf("%d", m-1);
任何人都知道这种行为背后的原因是什么?我使用克++没有任何命令行选项。
#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;
}
解决方案
您说“令人惊奇的是程序编译”。其实,这并不奇怪的。 C&C ++允许函数具有可变的参数列表。对于printf的定义是这样的:
int printf(char*, ...);
在“...”表示存在零个或多个可选的函数的自变量。事实上,主要的原因C具有可选的参数之一是支持的功能中的printf&scanf系列。
C具有printf函数的无专门知识。在您的示例:
printf("%d");
在编译器不分析格式字符串,并确定一个整数参数缺失。这是完全合法的C代码。您缺少一个观点的事实是,只出现在运行时的语义问题。 printf函数将假定您提供的参数,去寻找它的堆栈。它会拿起无论发生什么事要在那里。碰巧,在你的特殊情况下,它打印正确的事情,但是这却是个例外。一般来说,你会得到垃圾数据。这种行为将有所不同的编译器编译器和根据什么编译您使用的选项也将发生变化;如果你打开编译器的优化,你可能会得到不同的结果。
由于在评论我的回答的一个人指出,一些编译器有“棉绒”一样,实际上可以检测出错误的printf / scanf的通话功能。这包括编译器解析格式字符串和确定的预期额外参数的个数。这是非常特殊的编译器行为,在一般情况下也不会检测错误。也就是说,如果你写一个具有相同签名的printf自己的“printf_better”功能,编译器不会,如果任何参数缺失检测。
其他提示
会发生什么事是这样的。
printf("%d", m);
在大多数系统中字符串的地址将被推到堆栈,然后'm'
为一个整数(假设它是一个int /短路/炭)。没有任何警告,因为printf
基本上声明为'int printf(const char *, ...);'
- 的......意思是“怎么都行”
所以,当你把变量存在,因为“任何事情都会发生”一些奇怪的事情发生。任何整型都小于int去为int - 这样的事情。在所有发送什么是确定为好。
在printf的实现(或至少是一个“简单”的实现),你会发现va_list
和va_arg
的用法(名称有时略有不同基于一致性)。这些是一个实现使用走动参数列表的“...”的一部分。这里的问题是,没有类型检查。由于没有类型检查,printf
会拉随机数据从执行堆栈时,它看起来在格式字符串("%d")
和认为有被认为是下一个的'int'
。
在黑暗中乱箭射会说,该函数调用你做之前的printf可能通过'm-1'
作为它的第二个PARM?这是许多可能性之一 - 但如果这发生在这样的情况会很有意思。 :)
好运。
顺便说一句 - (?GCC我相信)最现代的编译器有启用后可检测到该问题的警告。林特一样好,我相信。不幸的是,我认为与VC,你需要使用/分析标志,而不是免费获得。
它得到从堆栈一个int。
您正在窥视到堆栈。更改优化值,这可能会改变。更改声明的变量(尤其是)m
的顺序。让m
寄存器变量。使m
全局变量。
您会看到一些变化发生了什么。
这是类似著名的缓冲区溢出的黑客,当你做简单的I / O,你得到的。
虽然我很怀疑这将导致内存冲突,你得到的整数是不确定的垃圾了。
您发现的一个的行为。它可能已被任何其他行为,包括无效的存储器访问。