gcc 4.4.3下C代码中的奇怪行为

gcc 4.4.3下C代码中的奇怪行为,c,C,我今天在指导一些学生学习C编程语言课程时遇到了这段代码。 演习要求执行两项职能。第一个扫描来自用户的输入,第二个显示以前扫描过的内容。 我遇到的代码如下: #包括 void myInput(int i,int n) { int cpt; int tab[n]; 对于(cpt=0;cpt来说,如果这样做有效,那完全是因为运气不好。在myDisp中打印的是未初始化的堆栈,它可能包含也可能不包含在myInput中放入类似命名变量的数据 下面是一个简单的方法,可以使用“不做任何事”代码打破它: voi

我今天在指导一些学生学习C编程语言课程时遇到了这段代码。 演习要求执行两项职能。第一个扫描来自用户的输入,第二个显示以前扫描过的内容。 我遇到的代码如下:


#包括
void myInput(int i,int n)
{
int cpt;
int tab[n];

对于(cpt=0;cpt来说,如果这样做有效,那完全是因为运气不好。在
myDisp
中打印的是未初始化的堆栈,它可能包含也可能不包含在
myInput
中放入类似命名变量的数据

下面是一个简单的方法,可以使用“不做任何事”代码打破它:

void myInput(int i,int n)
{
  // Add some variables to mess up the stack positioning.
  int breaker;
  int cpt;
  int stomper;
  int tab[n];
  int smasher;

  for ( cpt=0; cpt<n; cpt++)
  {
    printf("Enter a number :");
    scanf("%d",&i); 
    tab[cpt]=i;
   }

  // Trick the compiler into thinking these variables do something.
  breaker = 1;
  smasher = 3 * breaker;
  stomper = smasher + breaker;
  breaker = stomper * smasher;
 }
void myInput(int i,int n)
{
//添加一些变量以扰乱堆栈定位。
int断路器;
int cpt;
int stomper;
int tab[n];
int粉碎器;

对于(cpt=0;cpt由于这两个数组是完全分开的,它真的不应该工作。如果它工作,那只是因为它们在内存中的同一个位置结束。

它不工作,至少不一致。假设我有gcc 4.4.4而不是4.4.3

$ ./a.out
 Entrer the numbers of elements you want:
5
2
Enter a number :Enter a number :4
Enter a number :1
Enter a number :2
Enter a number :3
2 4 1 134514562 3

故事的寓意是,当您访问未初始化的内存时,任何事情都可能发生,包括工作的外观。

可能是因为
选项卡的
本地
myInput
e
myDisp
的内存位置恰好(几乎?)相同


这听起来并不奇怪:
myInput
myDisp
几乎有相同的签名(它们只因一个
int
参数不同);即使在最坏的情况下,两个函数中
tab
引用的堆栈上的位置仍将正确对齐,最多移动两个
int
s(
i
cpt
myInput
中)。

程序似乎正在为每个数组
int-tab访问相同的内存位置[n] 
您声明了,但如前所述,它不应该起作用

但是我认为这里发生的事情是这样的:您在main()内分配tab[],比方说,在地址0x00000001下(仅以它为例)。数组有n个整数,但根本没有值

然后进入myInput(),在其他地址(如0x001F0000)中再次声明数组(大小相同),然后逐个设置值

因此,当函数终止时,它会释放其变量的分配内存,因此数组不再存在

但是,等等,这是C,所以当你释放内存时,你只告诉堆(或者内存分配器,一般来说)地址可以再次使用。你不能完全从内存中删除这些值

然后调用myDisp()并再次声明数组。看起来您刚才请求的内存具有更高的优先级,然后它又被重新分配给您的程序。因此,您的数组再次实例化并位于相同的地址上

因此,即使没有用值填充内存,也会读取内存(因为它在C中始终有效),并且值仍然存在

哦,在main()中声明的数组?没有发生任何变化。尝试打印它的值,我打赌你不会得到正确的值

这是我的猜测


编辑:只是看看发生了什么:尝试在tab之后声明另一个数组(不要重命名tab),说tab2,相同的长度,用它来放置值而不是tab,然后让程序再次运行:)

供将来参考:请突出显示您的代码,然后按Ctrl+K或单击带有“0 1”之类的按钮来格式化您的代码。谢谢。据我所见,这是非常有效的C99。@Electro,@Birrree:他可能正在考虑在
myDisp
-1中使用未初始化变量
选项卡
,以便“为什么不正确的代码给出的结果与我希望它做的一样?”C不能保证不正确的代码给出错误的结果,只能弱地保证正确的代码给出正确的结果…;-)要扩展:您可能会很幸运,
myDisp
正在将
tab
分配到与其他函数相同的内存段中。它似乎可以工作,但代码不正确,需要修复。嗨,纳森!我刚刚在gcc下重新编译并运行了它。它可以工作。它显示了我输入的numbres!!!@strangeLoop:当然可以,但请尝试compi与-O3一起使用。它仍然有效吗?它是否以正确的顺序显示您输入的所有数字?嗨,Thanatos!由于在myDisp结束时没有释放内存,它应该会丢失。因此myDisp中的指针不应该指向此内存位置!!我错了吗?@strangeLoop:内存没有丢失,只是留在堆栈上。当您获得新的对于
myDisp
,它使用相同的(未清除的)内存作用域是完全无用的。我只是按照给定的方式重写了代码。如果对这个数组声明进行注释,代码仍然会运行并给出良好的值!当然。我将这种情况归咎于内存堆。在我看来,正如我所说的,你声明了那个数组,而堆给了你一个地址X,因为它位于堆的顶部。然后你释放X,它将再次位于堆的顶部(听起来很奇怪),然后当您再次声明tab时,堆将为您提供相同的地址。
$ ./a.out
 Entrer the numbers of elements you want:
5
2
Enter a number :Enter a number :4
Enter a number :1
Enter a number :2
Enter a number :3
2 4 1 134514562 3