Неожиданный вывод из программы Bubblesort с MSVC против TCC - PullRequest
11 голосов
/ 01 января 2011

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

#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 ++ эксперты, которые могут пролить свет на это?Несвязанное примечание: я серьезно думал о переписывании всего кода с использованием быстрой сортировки или сортировки слиянием, прежде чем публиковать его здесь.Но, в конце концов, это не сортировка по Стогу ...

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

Ответы [ 3 ]

28 голосов
/ 02 января 2011

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

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

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

    ***** 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] перебивает что-то еще.

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

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

7 голосов
/ 01 января 2011

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

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

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

1 голос
/ 26 сентября 2014

Ответ Майкла Барра раскрывает интересное поведение компилятора.Но я все еще сомневаюсь, что имя локальной переменной может повлиять на ее расположение в стеке для активной записи функции.

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

Причина указанного выше случайного сбоя __security_cookieхранится непосредственно над (больший адрес) массивом a, и, поскольку a определяется как массив целых чисел с единичным целым, результат будет зависеть от знакового бита (неверно истолкованного) значения __security_cookie.Если это положительное целое число больше 100, оно все еще является наибольшим значением в массиве a, поэтому sort не переключит его на другие позиции, тогда проверка (__security_check_cookie) в конце функции будет в порядке,Если оно меньше 100 или отрицательное целое число, оно будет переключено на нижний элемент в массиве после сортировки, поэтому __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]);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...