Чем эти две части кода отличаются? - PullRequest
0 голосов
/ 28 сентября 2019

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

1.)

int i = 0;
    for(i++; i++; i++){
        if(i>10)    break;
    }
    printf("%d",i);

Вывод - 12

2.)

int i;
    for(i++; i++; i++){
        if(i>10)    break;
    }
    printf("%d",i);

Вывод - 1

Я ожидал, что выражения "int i = 0" и "int i" будут одинаковыми. В чем разница между ними?

Ответы [ 2 ]

5 голосов
/ 28 сентября 2019

Я ожидал, что выражения "int i = 0" и "int i" будут одинаковыми.

Нет, это было неверное ожидание с вашей стороны.Если переменная объявлена ​​вне функции (как «глобальная» переменная) или если она объявлена ​​с ключевым словом static, она гарантированно будет инициализирована равной 0, даже если вы не пишете = 0.Но переменные, определенные внутри функций (обычные «локальные» переменные без static), делают , а не имеют эту гарантированную инициализацию.Если вы не инициализируете их явно, они начинают содержать неопределенные значения.

(Обратите внимание, однако, что в этом контексте «неопределенный» не означает «случайный». Если вы пишете программу, которая использует или печатаетнеинициализированная переменная, часто вы обнаружите, что она начинается с одного и того же значения каждый раз, когда вы запускаете вашу программу. Случайно, это может быть даже 0. На большинстве машин происходит то, что переменная принимает любое оставленное значение "в стеке »предыдущей вызванной функцией.)

См. также следующие вопросы:

Инициализация нестатической переменной
Статическая переменнаяинициализация?

См. также раздел 4.2 и раздел 4.3 в эти примечания к классу .

См. также вопрос 1.30 в списке часто задаваемых вопросов C .


Приложение: на основе ваших комментариев звучит, как если бы вам не удалось инициализировать i, неопределенное значениеэто случается сtart out с 0, поэтому ваш вопрос сейчас:

"Учитывая программу

#include <stdio.h>
int main()
{
    int i;                   // note uninitialized
    printf("%d\n", i);       // prints 0
    for(i++; i++; i++){
        if(i>10)    break;
    }
    printf("%d\n", i);       // prints 1
}

, какую возможную последовательность операций может выдавать компилятор, который заставит его вычислить окончательное значениеиз 1? "

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

Этот ответ снова: «Неинициализированная локальная переменная ведет к неопределенному поведениюНеопределенное поведение означает, что может произойти все что угодно. "

Важным в этом ответе является то, что в нем говорится, что" все может произойти ", а" все что угодно "означает абсолютно все .Это абсолютно не имеет смысла.

Второй вопрос, как я его сформулировал, на самом деле даже не имеет смысла, поскольку в нем есть внутреннее противоречие, потому что он спрашивает, «какая возможная последовательность операций могла быкомпилятор "излучает", но поскольку программа содержит неопределенное поведение, компилятор даже не обязан выдавать разумную последовательность операций вообще.

Если вы действительно хотите знать, какая последовательность операций у вашего компилятораиспуская, ты должен спросить это.Под Unix / Linux скомпилируйте с флагом -S.Под другими компиляторами я не знаю, как просмотреть вывод на ассемблере.Но, пожалуйста, не ожидайте, что вывод будет иметь какой-то смысл, и, пожалуйста, не просите меня объяснить его вам (потому что я уже знаю, что это не имеет никакого смысла).

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

#include <stdio.h>
int main()
{
    int i;                   // note uninitialized
    printf("%d\n", i);       // prints 0
    i++;
    printf("%d\n", i);       // prints 1
}

«Но это не имеет никакого смысла!», говорите вы.«Как компилятор может превратить« for(i++; i++; i++) ... »в просто« i++ »? И ответ - вы слышали это, но, возможно, вы все еще не совсем в это поверите - это когда программа содержит неопределенное поведение, компилятору разрешено делать что угодно .

1 голос
/ 28 сентября 2019

Разница в том, что вы уже наблюдали.Первый код инициализирует i, другой - нет.Использование унифицированного значения - неопределенное поведение (UB) в c ++.Компилятор предполагает, что UB не происходит в правильной программе, и, следовательно, ему разрешено испускать код, который делает что угодно.Более простой пример:

int i;
i++;

Компилятор знает, что i++ не может произойти в правильной программе, и компилятор не пытается выдать правильный вывод при неправильном вводе, поэтому при запуске этого кода может произойти что угодно.

Подробнее см. Здесь: https://en.cppreference.com/w/cpp/language/ub

Это практическое правило, которое (помимо прочего) помогает избежать неинициализированных переменных.Он называется почти-всегда-автоматический, и он предлагает использовать auto почти всегда.Если вы напишите

auto i = 0;

Вы не можете забыть инициализировать i, потому что auto требует, чтобы инициализатор мог выводить тип.

PS: C и C ++ два разныхязыки с разными правилами.Ваш второй код UB в C ++, но я не могу ответить на ваш вопрос для C.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...