Почему вывод 5, когда сумма ничего не возвращает? [Дубликат]

Это означает, что ваш код использовал ссылочную переменную объекта, которая была установлена ​​в нуль (т. е. она не ссылалась на экземпляр фактического объекта).

Чтобы предотвратить ошибку, объекты, которые могут быть пустыми, должны быть протестированы для null перед тем, как использовать.

if (myvar != null)
{
    // Go ahead and use myvar
    myvar.property = ...
}
else
{
    // Whoops! myvar is null and cannot be used without first
    // assigning it to an instance reference
    // Attempting to use myvar here will result in NullReferenceException
}
9
задан stakx 2 September 2011 в 10:02
поделиться

8 ответов

Что случилось с вами, так это то, что когда программа C была скомпилирована на ассемблере, ваша функция toUpper закончилась так:

_toUpper:
LFB4:
        pushq   %rbp
LCFI3:
        movq    %rsp, %rbp
LCFI4:
        movb    %dil, -4(%rbp)
        cmpb    $96, -4(%rbp)
        jle     L8
        cmpb    $122, -4(%rbp)
        jg      L8
        movzbl  -4(%rbp), %eax
        subl    $32, %eax
        movb    %al, -4(%rbp)
L8:
        leave
        ret

Вычитание 32 было выполнено в% eax регистр. И в соглашении на вызов x86, это регистр, в котором ожидается возвращаемое значение! Итак ... вам повезло.

Но, пожалуйста, обратите внимание на предупреждения. Они там по какой-то причине!

19
ответ дан Ray Toal 25 August 2018 в 09:45
поделиться

Я пробовал небольшую программу:

#include <stdio.h>
int f1() {
}
int main() {
    printf("TEST: <%d>\n",  f1());
    printf("TEST: <%d>\n",  f1());
    printf("TEST: <%d>\n",  f1());
    printf("TEST: <%d>\n",  f1());
    printf("TEST: <%d>\n",  f1());
}

Результат:

TEST: & lt; 1>

TEST: & lt; 10>

TEST: & lt; 11>

TEST: & lt; 11>

TEST: & lt; 11>

Я использовал mingw32-gcc компилятор, поэтому могут быть разные.

Вы могли бы просто поиграть и попробовать например функция char. Если вы не используете значение результата, оно будет работать нормально.

#include <stdio.h>
char f1() {
}
int main() {
    f1();
}

Но я бы рекомендовал установить либо функцию void, либо дать некоторое возвращаемое значение.

Ваш функция, похоже, нуждается в возврате:

char toUpper(char c)
{
    if(c>='a'&&c<='z')
        c = c - 32;
    return c;
}
0
ответ дан Andreas L. 25 August 2018 в 09:45
поделиться

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

0
ответ дан ARA1307 25 August 2018 в 09:45
поделиться

Локальных переменных нет, поэтому значение в верхней части стека в конце функции будет параметром c. Значение в верхней части стека при выходе - это возвращаемое значение. Таким образом, независимо от c, это возвращаемое значение.

0
ответ дан David Griffiths 25 August 2018 в 09:45
поделиться

Это зависит от Application Binary Interface и какие регистры используются для вычисления.

Например. на x86 первый параметр функции и возвращаемое значение сохраняются в EAX, и поэтому gcc, скорее всего, использует это, чтобы сохранить результат вычисления.

7
ответ дан Mats 25 August 2018 в 09:45
поделиться

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

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

Если функция ничего не возвращает, компилятор не будет генерировать код, который явно заполняет это местоположение с возвращаемым значением. Однако, как я уже сказал выше, он может использовать это местоположение во время функции. Когда вы пишете код, который читает возвращаемое значение (ch2 = toUpper(ch);), компилятор будет писать код, который использует свое соглашение о том, как получить это возвращение из обычного местоположения. Что касается кода вызывающего абонента, он просто прочитает это значение из местоположения, даже если там ничего явно не было написано. Следовательно, вы получаете значение.

Теперь посмотрите на пример @ Ray, компилятор использовал регистр EAX, чтобы сохранить результаты операции верхнего корпуса. Так получилось, это, вероятно, место, в которое записываются значения возврата. На вызывающей стороне ch2 загружается значение, которое находится в EAX - следовательно, возвращается фантом. Это справедливо только для процессоров x86, так как на других архитектурах компилятор может использовать совершенно другую схему при определении того, как должно быть организовано соглашение

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

1
ответ дан Preet Sangha 25 August 2018 в 09:45
поделиться

Одна недостающая вещь, которая важна для понимания, заключается в том, что редко диагностическая ошибка пропускает оператор return. Рассмотрим эту функцию:

int f(int x)
{
    if (x!=42) return x*x;
}

Пока вы не вызываете ее с аргументом 42, программа, содержащая эту функцию, отлично действует C и не вызывает никакого неопределенного поведения, несмотря на то, что она будет вызывать UB, если вы вызвали f(42) и впоследствии попытались использовать возвращаемое значение.

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

2
ответ дан R.. 25 August 2018 в 09:45
поделиться

По существу, c помещается в пятно, которое позднее должно быть заполнено возвращаемым значением; поскольку он не перезаписывается с помощью return, он заканчивается как возвращаемое значение.

Обратите внимание, что полагаться на это (на C или на любом другом языке, где это не является явной функцией языка, например Perl), это плохая идея. В крайнем случае.

2
ответ дан Williham Totland 25 August 2018 в 09:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: