Неожиданный выпуск программы Bubblesort с MSVC vs TCC

Один из моих друзей отправил мне этот код, сказав, что он работает не так, как ожидалось:

#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 Command Line Tool для компиляции на компьютере под управлением Windows XP. cl /EHsc bubblesort01.c

Мой друг получает правильный вывод на машине динозавра (код скомпилирован с использованием TCC там).
Мой выход неожиданен. Массив таинственно растет в размерах, между ними.

Если вы измените код так, чтобы переменная sizeOfInput была изменена на sizeOfInputt, она дает ожидаемые результаты!

Поиск по Центр разработки Microsoft Visual С++ не дает никаких результатов для "sizeOfInput".

Я не эксперт по C/С++, и мне интересно узнать, почему это происходит - любые эксперты C/С++, которые могут "пролить свет" на это?
Несвязанное примечание: я серьезно подумал о переписывании всего кода, чтобы использовать сортировку quicksort или merge, прежде чем публиковать ее здесь. Но, в конце концов, это не Stooge sort...

Изменить: я знаю, что код неверен (он читается за последним элементом), но мне любопытно, почему имя переменной имеет значение.

Ответ 1

Как упоминается ответ на interjay, как только вы попадаете в поведение undefined, все ставки отключены. Однако, когда вы сказали, что просто переименование переменной изменило поведение программы, мне стало интересно, что происходит (undefined или нет).

Во-первых, я не думал, что переименование переменной изменит выход компилятора (при прочих равных условиях), но, конечно же, - когда я его попробовал, я был удивлен, увидев именно то, что вы описали.

Итак, у меня был компилятор, дамп сборки для кода, который он генерировал для каждой версии исходного файла, и провел сравнение. Вот что я нашел в компиляторах описание того, как он выделял локальные переменные:

    ***** 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, поведение undefined, возникающее при изменении a[10], меняет значение sizeOfInput. В сборке, которая использует имя sizeOfInputt, поскольку эта переменная не находится в конце массива, запись в a[10] уничтожает что-то еще.

Что касается того, почему компилятор будет выставлять переменные по-разному, когда имя одного изменяется, по-видимому, незначительно - я понятия не имею.

Но это хороший пример того, почему вы не должны рассчитывать на компоновку локальных переменных (или почти любых переменных, хотя вы можете рассчитывать на порядок размещения элементов структуры) и почему, когда дело доходит до undefined поведение, "он работает на моей машине", не режет его как доказательство того, что что-то работает.

Ответ 2

Ваш код читается за концом массива. Максимальное значение outer равно 10, а максимальное значение inner равно 9, поэтому a[inner+1] будет читать a[10]. Это даст вам undefined поведение, что объясняет, почему разные компиляторы дают разные результаты.

Что касается имени переменной, делающего разницу: вероятно, нет. Возможно, если вы дважды запустите тот же код (используя одно и то же имя переменной), вы получите разные результаты. В принципе, при вызове поведения undefined вы не можете быть уверены в том, что ваша программа будет делать, поэтому лучше не пытаться найти смысл в таких вещах, как имена переменных.

Также существует вероятность, что имя переменной имеет значение. Это зависит от реализации компилятора: например, использование другого имени переменной может заставить компилятор каким-то образом организовать память по-разному, что может привести к тому, что программа будет работать по-разному. Я думаю, что большинство компиляторов выведет один и тот же код, если вы измените имена переменных, хотя это, вероятно, было просто удачей.

Ответ 3

Michael Burr reply раскрывает интересное поведение компилятора. Но я все же сомневаюсь, что имя локальной переменной может повлиять на его расположение в стеке для активной записи функции.

Я использую VС++ 2013 с командной строкой выше (cl.exe/EHsc progam.cpp) и не могу ее перепрограммировать - изменение имени переменной не изменяет поведение программы, вместо этого она стабильно обнаруживает случайный сбой (некоторые прогоны возвращают результаты хорошо, а некоторые запускаются сбой).

Причина вышеупомянутого случайного сбоя __security_cookie хранится непосредственно над (большим адресом) массивом a, а так как a определяется как целочисленный целочисленный массив, результат будет зависеть от знакового бита (неверно интерпретируемый ) значения __security_cookie. Если это положительное целое число больше 100, оно по-прежнему является наибольшим значением в массиве a, поэтому сортировка не переключит его на другие позиции, тогда проверка (__security_check_cookie) в конце функции будет прекрасной, Если оно меньше 100 или отрицательное целое число, оно будет переключено на нижний элемент в массиве после сортировки, поэтому __security_check_cookie сообщает об ошибке. Это означает, что поведение программы зависит от случайного сгенерированного значения для __security_cookie.

Я изменил исходную тестовую программу ниже, чтобы облегчить тестирование. Я также расширил вывод, чтобы включить элемент by-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]);
}