Turbo C++: Почему printf выводит ожидаемые значения, когда ему не передаются никакие переменные?

В тесте с множественным выбором был задан вопрос: что будет на выходе следующей программы:

#include <stdio.h>

int main(void)
{
    int a = 10, b = 5, c = 2;

    printf("%d %d %d\n");

    return 0;
}

и варианты были различными перестановками 10, 5 и 2. По какой-то причине это работает в Turbo C++, который мы используем в колледже. Однако этого не происходит при компиляции с помощью gcc (который выдает предупреждение при включении -Wall) или clang (в котором включен -Wformat и выдает предупреждение по умолчанию) или в Visual C++. На выходе, как и ожидалось, мусорные значения. Я предполагаю, что это как-то связано с тем, что либо Turbo C++ является 16-разрядным и работает на 32-разрядной Windows XP, либо с тем, что TCC ужасен, когда дело доходит до стандартов.


person SgrA    schedule 22.08.2013    source источник
comment
Вывод может быть буквально любым, так как этот код приводит к Undefined Behaviour.   -  person Paul R    schedule 22.08.2013
comment
Да, вся эта штука с компилятором — отвлекающий маневр.   -  person    schedule 22.08.2013
comment
В спецификаторе формата gcc должно быть указано имя переменной, например printf(%d, x); если не printf напечатает какое-то значение   -  person sujin    schedule 22.08.2013
comment
Было бы интересно, если бы OP мог сообщить нам, является ли «неопределенное поведение» одним из ответов с множественным выбором.   -  person    schedule 22.08.2013
comment
Я совершенно не понимаю, почему колледж может использовать снятое с производства и явно плохое программное обеспечение для обучения C или C++, и даже задавать такой бессмысленный вопрос MCT, в то время как есть компиляторы, такие как GCC и Clang, доступные бесплатно.   -  person SirDarius    schedule 22.08.2013
comment
Есть ли в tc++ возможность листинга в смешанном режиме, где вы всегда видите одну строку c++ и выходит за пределы сгенерированного кода на ассемблере?   -  person ott--    schedule 22.08.2013
comment
@sirdarius Как я уже сказал, покажите нам ответы, прежде чем делать поспешные заявления.   -  person    schedule 22.08.2013
comment
@remyabel: Нет, только перестановки 2, 5 и 10.   -  person SgrA    schedule 22.08.2013
comment
В этом случае они, вероятно, зависят от порядка стека.   -  person    schedule 22.08.2013
comment
@SirDarius Turbo C++ здесь, по-видимому, является стандартом даже для обучения C++; Я согласен, что глупо учить C на курсах под предлогом C++.   -  person SgrA    schedule 22.08.2013
comment
@remyabel Я не делал поспешных заявлений. Вопрос здесь демонстрирует, что турбо С++ не считает отсутствие достаточного количества параметров для printf ошибкой. Так что видно, что это плохо. В любом случае, формулировка вопроса ясно дает понять, что поведение undefiend не является частью выбора.   -  person SirDarius    schedule 23.08.2013


Ответы (5)


Код имеет неопределенное поведение.

В Turbo C++ так уж получилось, что три переменные находятся в стеке точно в тех позициях, где должен быть отсутствующий аргумент printf(). Это приводит к тому, что неопределенное поведение проявляется в виде вывода «правильных» значений.

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

person NPE    schedule 22.08.2013
comment
Спасибо, это имеет смысл. Я пробовал одну и ту же программу (скомпилированную с помощью gcc) несколько раз в Linux, и, видимо, это совпадение довольно редко. Связано ли то совпадение, что они находятся именно в тех позициях стека, которых требует printf, с тем, как 16-разрядные программы работают в Windows XP? Что-то вроде некоторой памяти, вероятно, зарезервировано для запуска именно этой программы? - person SgrA; 22.08.2013
comment
@SgrA: Довольно много звезд должно сойтись, чтобы эта программа вела себя так, как вы описываете. Было бы слишком упрощенно говорить, что это из-за XP или потому что программа 16-битная. - person NPE; 22.08.2013
comment
Интригует то, что это работало, как описано выше, каждый раз в Turbo C++, но никогда вне его, что вызывает у меня любопытство. - person SgrA; 22.08.2013
comment
@SgrA Это не случайно, это вопрос того, как компилятор упорядочивает вещи. Это не определено, но это не всегда означает случайное. Это всегда будет работать в этой ситуации. Он может легко сломаться из-за несвязанных изменений. - person Loren Pechtel; 23.08.2013

Ответ здесь в том, что программа может делать что угодно — это поведение undefined. Согласно документации printf()s (выделено мной):

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

Если в вашем тесте с множественным выбором нет выбора для «неопределенного поведения», то это ошибочный тест. Под влиянием неопределенного поведения любой ответ на такой тестовый вопрос с несколькими вариантами ответов является технически правильным.

person cdhowie    schedule 22.08.2013

Это undefined behaviour. Так что это может быть что угодно.

Попробуй использовать

printf("%d %d%d", a,b,c)

Причина:- Локальные переменные вызываются в стеке, и printf в Turbo C++ видит их в том же порядке, в котором они были назначены в стеке.

ПРЕДЛОЖЕНИЕ(из комментариев):-

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

person Rahul Tripathi    schedule 22.08.2013
comment
Да, я убежден, что на выходе может быть что угодно. Я хотел бы знать, почему именно это работает в Turbo C++. - person SgrA; 22.08.2013
comment
@SgrA: - Обновил мой ответ, указав причину, хотя NPE объяснил это лучше !!! :) - person Rahul Tripathi; 22.08.2013
comment
@SgrA: Понимание того, почему он ведет себя определенным образом с конкретным компилятором, может быть полезно при диагностике проблем, но не используйте какую-либо другую информацию. - person Keith Thompson; 22.08.2013
comment
@KeithThompson Согласен, что мне не следует полагаться на эти совпадения, и мне любопытно только потому, что я буду использовать Turbo C++ пару лет в колледже. - person SgrA; 22.08.2013

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

Это поведение, как и многие другие, разрешено под эгидой undefined behavoir.

person SingleNegationElimination    schedule 22.08.2013
comment
Вы имеете в виду, что локальные переменные также помещаются в стек, а не локальные переменные также передаются в стек вызовов... - person Floris; 09.12.2013

Нет, это не связано с архитектурой. Это связано с тем, как TurboC++ обрабатывает стек. Переменные a, b и c являются локальными и поэтому размещаются в стеке. printf также ожидает значения в стеке. Судя по всему, TurboC++ больше ничего не добавляет в стек после локальных и printf умеет их принимать в качестве параметров. Просто совпадение.

person Mario Rossi    schedule 22.08.2013