我的一位朋友将此代码发送给我,说它无法按预期运行:
#include<stdio.h>
void main()
{
int a [10] ={23, 100, 20, 30, 25, 45, 40, 55, 43, 42};
int sizeOfInput = sizeof(a)/sizeof(int);
int b, outer, inner, c;
printf("Size is : %d \n", sizeOfInput);
printf("Values before bubble sort are : \n");
for ( b = 0; b < sizeOfInput; b++)
printf("%d\n", a[b]);
printf("End of values before bubble sort... \n");
for ( outer = sizeOfInput; outer > 0; outer-- )
{
for ( inner = 0 ; inner < outer ; inner++)
{
printf ( "Comparing positions: %d and %d\n",inner,inner+1);
if ( a[inner] > a[inner + 1] )
{
int tmp = a[inner];
a[inner] = a [inner+1];
a[inner+1] = tmp;
}
}
printf ( "Bubble sort total array size after inner loop is %d :\n",sizeOfInput);
printf ( "Bubble sort sizeOfInput after inner loop is %d :\n",sizeOfInput);
}
printf ( "Bubble sort total array size at the end is %d :\n",sizeOfInput);
for ( c = 0 ; c < sizeOfInput; c++)
printf("Element: %d\n", a[c]);
}
我正在使用Micosoft Visual Studio命令行工具在Windows XP计算机上进行编译。
cl /EHsc bubblesort01.c
我的朋友在恐龙机器上获得正确的输出(代码是使用TCC编译的)。 我的输出是出乎意料的。阵列神秘地增长,介于两者之间。
如果您更改代码以便将变量sizeOfInput
更改为sizeOfInputt
,则会显示预期结果!
在Microsoft Visual C++ Developer Center处进行的搜索未对“sizeOfInput”提供任何结果。
我不是C / C ++专家,很想知道为什么会发生这种情况 - 任何C / C ++专家都可以对此有所了解?
无关的注意事项:我认真考虑重写整个代码,以便在发布之前使用quicksort或merge sort。但是,毕竟,它不是Stooge排序......
编辑:我知道代码不正确(它超出了最后一个元素),但我很好奇为什么变量名称有所不同。
答案 0 :(得分:28)
与interjay's answer mentioned一样,一旦你进入未定义的行为,所有的赌注都会被取消。但是,当你说只是重命名变量改变了程序的行为时,我对正在发生的事情感到好奇(未定义或未定义)。
首先,我不相信重命名变量会改变编译器的输出(所有其他条件相同),但果然 - 当我尝试它时,我很惊讶地看到你所描述的内容。
所以我让编译器为每个源文件版本生成的代码转储程序集,然后进行比较。以下是我在编译器描述中如何布置局部变量的内容:
***** test.sizeOfInput.cod
_c$ = -56 ; size = 4
_b$ = -52 ; size = 4
_inner$ = -48 ; size = 4
_a$ = -44 ; size = 40
>>> _sizeOfInput$ = -4 ; size = 4
_main PROC
***** test.sizeOfInputt.cod
_c$ = -56 ; size = 4
>>> _sizeOfInputt$ = -52 ; size = 4
_b$ = -48 ; size = 4
_inner$ = -44 ; size = 4
_a$ = -40 ; size = 40
_main PROC
*****
你会注意到,当变量命名为sizeOfInput
时,编译器将它放在比数组a
更高的地址(刚好超过数组的末尾),并且当变量被命名为sizeOfInputt
它将它放在比数组a
更低的地址,而不是刚好超过数组的末尾。这意味着在具有名为sizeOfInput
的变量的构建中,修改a[10]
时发生的未定义行为正在更改sizeOfInput
的值。在使用名称sizeOfInputt
的构建中,由于该变量不在数组的末尾,因此写入a[10]
会破坏其他内容。
至于为什么编译器会以一种明显无关紧要的方式改变变量的方式对变量进行不同的布局 - 我不知道。
但这是一个很好的例子,为什么你不应该依赖局部变量的布局(或几乎任何变量,尽管你可以依赖于struct元素的布局顺序),以及为什么当涉及到未定义的行为时,“它可以在我的机器上运行”并没有将其视为有效的证明。
答案 1 :(得分:7)
您的代码读取超出数组的末尾。 outer
的最大值为10,inner
的最大值为9,因此a[inner+1]
将为a[10]
。这将为您提供undefined behavior,这解释了为什么不同的编译器会给出不同的结果。
变量名称有所不同:它可能没有。如果您运行相同的代码两次(使用相同的变量名称),则可能会得到不同的结果。基本上,在调用未定义的行为时,您无法确定程序将执行的任何操作,因此最好不要尝试查找变量名称等内容。
变量名也有可能产生影响。这取决于编译器的实现:例如,使用不同的变量名可能会使编译器以某种方式组织内存,这可能导致程序不同。我认为如果你改变变量名,大多数编译器会输出相同的代码,所以这可能只是运气问题。
答案 2 :(得分:1)
Michael Burr's reply暴露了编译器的一个有趣的行为。但我仍然怀疑局部变量名可能会影响其在函数的活动记录堆栈中的布局。
我正在使用VC ++ 2013上面的命令行(cl.exe / EHsc progam.cpp)并且无法重复它 - 更改变量名称不会改变程序行为,相反,它会显示随机崩溃(某些运行)返回结果很好,有些运行崩溃)。
上述随机崩溃的原因是__security_cookie
直接存储在数组a
的上方(较大地址),而a被定义为烧结整数数组,结果将取决于符号位(误解)__security_cookie
的值。如果它是一个大于100的正整数,它仍然是数组a
中的最大值,因此sort不会将其切换到其他位置,然后检查(__security_check_cookie
)功能会很好。如果它小于100或负整数,它将在排序后切换到数组中的lower元素,因此__security_check_cookie
报告失败。这意味着程序行为取决于__security_cookie
的随机生成值。
我将原始测试程序更改为以下以简化测试。我还扩展了输出以包含off-by-one元素(arrayLen + 1),我们可以根据定义的数组a
之后的元素中的原始值来预测行为。
#include<stdio.h>
#define arrayLen sizeOfInputt
void main()
{
int a [10] ={23, 100, 20, 30, 25, 45, 40, 55, 43, 42};
int arrayLen = sizeof(a)/sizeof(int);
int b, outer, inner, c;
printf("Size is : %d \n", arrayLen);
printf("Values before bubble sort are : \n");
for ( b = 0; b < arrayLen + 1; b++)
printf("%d\n", a[b]);
printf("End of values before bubble sort... \n");
for ( outer = arrayLen; outer > 0; outer-- )
{
for ( inner = 0 ; inner < outer ; inner++)
{
printf ( "Comparing positions: %d and %d\n",inner,inner+1);
if ( a[inner] > a[inner + 1] )
{
int tmp = a[inner];
a[inner] = a [inner+1];
a[inner+1] = tmp;
}
}
printf ( "Bubble sort total array size after inner loop is %d :\n",arrayLen);
printf ( "Bubble sort arrayLen after inner loop is %d :\n",arrayLen);
}
printf ( "Bubble sort total array size at the end is %d :\n",arrayLen);
for ( c = 0 ; c < arrayLen; c++)
printf("Element: %d\n", a[c]);
}