【问题标题】:Unexpected output from Bubblesort program with MSVC vs TCC带有 MSVC 与 TCC 的 Bubblesort 程序的意外输出
【发布时间】:2018-09-21 07:12:05
【问题描述】:

我的一个朋友将此代码发送给我,说它没有按预期工作:

#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 &lt; 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++ 专家可以对此有所了解吗?
无关说明:我认真考虑过重写整个代码以使用快速排序或合并排序,然后再将其发布到此处。但是,毕竟不是Stooge排序……

编辑:我知道代码不正确(它超出了最后一个元素),但我很好奇为什么变量名会有所不同。

【问题讨论】:

  • 感谢您的编辑,但只是注意到它搞砸了 >和 < (>.

标签: c visual-studio-2008


【解决方案1】:

就像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 更高的地址(刚好超过数组的末尾),而当变量命名为 @987654325 @ 它将它放置在比数组a 更低的地址,而不是刚好超过数组的末尾。这意味着在具有名为sizeOfInput 的变量的构建中,当您修改a[10] 时发生的未定义行为正在更改sizeOfInput 的值。在使用名称 sizeOfInputt 的构建中,由于该变量不在数组的末尾,因此写入 a[10] 会破坏其他内容。

至于为什么编译器会以不同的方式布置变量,当一个人的名字以一种明显无关紧要的方式发生变化时——我不知道。

但这是一个很好的例子,说明了为什么你不应该依赖局部变量(或几乎任何变量,尽管你可以依赖结构元素的布局顺序)的布局,以及为什么当涉及到未定义的行为时,“它可以在我的机器上运行”并不能证明某些东西有效。

【讨论】:

  • 迈克尔,这有帮助!这就是我一直在寻找的。接受你的回答。感谢您抽出宝贵时间对此进行研究。
  • 可能所有变量都放入某种形式的哈希映射中,sizeOfInputsizeOfInputt 的哈希完全不同。
【解决方案2】:

您的代码读取到数组末尾之后。 outer 的最大值为 10,inner 的最大值为 9,所以a[inner+1] 将读取为a[10]。这会给你undefined behavior,这解释了为什么不同的编译器给出不同的结果。

至于变量名有什么不同:它可能没有。如果您两次运行相同的代码(使用相同的变量名),您可能会得到不同的结果。基本上,当调用未定义的行为时,您无法确定程序会做什么,因此最好不要尝试从变量名之类的东西中寻找意义。

变量名也有可能产生影响。这取决于编译器的实现:例如,使用不同的变量名可能会使编译器以不同的方式组织内存,这可能会导致程序变得不同。我认为如果您更改变量名,大多数编译器都会输出相同的代码,所以这可能只是运气问题。

【讨论】:

  • interjay:谢谢,我更新了我的帖子。我知道代码又读取了一个元素 - 从输出中可以明显看出。我对变量的名称产生如此大的影响感到困惑。
  • @interjay:看到你的更新:不,如果我将变量命名为别的东西,它会编译并在第一次给我正确的答案。此外,使用“sizeOfInput”重新编译没有任何区别。
  • @Sujith:即使这是真的,如果不检查编译器的源代码,几乎不可能确定为什么会发生这种情况。我做了另一个编辑来举个例子。
  • @interjay:感谢您的更新。正如我在帖子中提到的,它适用于 Turbo C 编译器,具有任何变量名。对于 MS Visual Studio,它也适用于任何变量名称,“sizeOfInput”除外。这一定是因为微软实现他们的编译器的方式,但我想知道是否有任何 C/C++ 专家以前遇到过同样的行为。
  • @Sujith:C++ 专家会先避免未定义的行为,然后就不会有这样的问题 :) interjay 很好地解释了可能发生的事情
【解决方案3】:

Michael Burr's reply 暴露了编译器的一个有趣行为。但我仍然怀疑局部变量名称是否会影响其在堆栈中的布局以用于函数的活动记录。

我正在使用 VC++ 2013 和上面的命令行 (cl.exe /EHsc progam.cpp) 并且无法重现它 - 更改变量名不会改变程序行为,相反,它会稳定显示随机崩溃(一些运行返回结果很好,一些运行崩溃)。

上述随机崩溃的原因是__security_cookie直接存储在数组a的上方(更大的地址),并且由于a被定义为单整数数组,结果将取决于符号位(错误解释) __security_cookie 的值。如果是大于100的正整数,仍然是数组a中的最大值,所以sort不会切换到其他位置,那么函数末尾的check(__security_check_cookie)就可以了.如果小于 100 或负整数,排序后会切换到数组中的较低元素,所以__security_check_cookie 报告失败。这意味着程序行为取决于__security_cookie 的随机生成值。

我把原来的测试程序改成下面这样方便测试。我还扩展了输出以包含非一元素 (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]);
}

【讨论】:

    猜你喜欢
    • 2011-03-29
    • 2015-09-20
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-11
    相关资源
    最近更新 更多