Лучший способ объяснить декларативную ошибку в C ++? - PullRequest
6 голосов
/ 01 сентября 2009

Как можно составить хорошее объяснение того, почему следующий код некорректен, поскольку автор пытается писать код C ++ декларативно, а не процедурно?

const double NEWTONS_PER_POUND = 4.448;

int main()
{
   double pounds, newtons;
   pounds = newtons/NEWTONS_PER_POUND; /* pounds equals 'unassigned variable'/4.448 */
   newtons = 10.0;
   cout << pounds << endl;             /* a big number, not 10.0/4.448 */
   return 0;
}

Автор ожидал, что cout отобразит правильный расчет, но вместо этого получит "сумасшедшее число".

Я бы объяснил это как "C ++ является процедурным, и, следовательно, во время объявления

pounds = newtons/NEWTONS_PER_POUND;

newtons не было присвоено значение.

Какие-нибудь лучшие предложения? Или объяснение того, почему C ++ недостаточно «умен», чтобы вести себя так, как ожидал пользователь?

Ответы [ 9 ]

21 голосов
/ 01 сентября 2009

Скажите автору, что

pounds = newtons/NEWTONS_PER_POUND;

подает команду ЦПУ на

  • принять значение по адресу, называемому "ньютонами"
  • принять значение по адресу, который называется "NEWTONS_PER_POUND"
  • разделите их
  • сохранить результат по адресу, называемому "фунтами"

то, что он ищет, скорее всего, является функцией императивного термина:

double newtons_to_pounds(double newtons) {
  return newtons/NEWTONS_PER_POUND;
}

...

newtons = 10.0;
cout << newtons_to_pounds(newtons) << endl;    /* a big number, not 10.0/4.448 */
return 0;
18 голосов
/ 01 сентября 2009

C ++ - это императивный язык программирования, а не решатель уравнений.

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

Вот подробное объяснение:

double pounds, newtons;
pounds = newtons/NEWTONS_PER_POUND;
newtons = 10.0;

Первый оператор объявляет две переменные без их инициализации. На данный момент их значения не определены.

Второе утверждение читает значение newtons (которое может быть чем угодно) и делит его на NEWTONS_PER_POUND. Результат (который может быть чем угодно) присваивается pounds.

Третий оператор инициализирует newtons, но уже слишком поздно влиять на только что выполненные вычисления.

8 голосов
/ 01 сентября 2009

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

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

4 голосов
/ 01 сентября 2009

Если человек не слишком технический, вы можете попробовать:

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

4 голосов
/ 01 сентября 2009

Не лень оценивать ньютоны

Таким образом, расчет выполняется во время декларации, а не во время запроса. Он занимается функциональным кодом, а не тем, что будет делать C ++.

1 голос
/ 01 сентября 2009

Как насчет пошагового выполнения кода в отладчике?

IME нет ничего подобного, чтобы понимать выполнение программы, написанной на процедурном языке (то есть, смоделировано на основе того, как процессор фактически выполняет код).

1 голос
/ 01 сентября 2009

Объясните, что фунту присваивается значение в строке с оператором присваивания:

pounds = newtons/NEWTONS_PER_POUND;

Если бы это было не так, но фунты оценивались при его использовании (как в выражении cout), то, если значение ньютонов изменилось, значение фунтов также изменилось бы. Поскольку фунты - это не указатель любого типа, а простое целое число, это невозможно.

0 голосов
/ 02 сентября 2009

Возьмем немного более сложный пример, где переменная типа newtons используется повторно и ей присваиваются значения более одного раза. Например:

double pounds, newtons;

newtons = 10.0;
pounds = newtons/NEWTONS_PER_POUND;
cout << pounds << endl;

newtons = 15.0;
pounds = newtons/NEWTONS_PER_POUND;
cout << pounds << endl;

return 0;

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

0 голосов
/ 01 сентября 2009

Вы пытаетесь заставить слушателя изменить парадигму - изменить весь свой подход к пониманию этого кода.

"фунты" это просто число. Нет Понятие о том, как его создали. Вы говорите "фунтов", как он создан, он не будет Помните. Это будет просто помнить, что это не так, как оно создано.

Может показаться немного странным антропоморфизировать блок памяти. : -)

...