Никогда не доверяйте сообщениям об ошибках компилятора
Товарищ-программист сделал пост под названием «Базовая отладка». Чувак вообще не понимает, как отлаживать. Он упоминает два способа, но затем начинает рассказывать о том, как писать простой код. На мой взгляд, это не совсем отладка. Конечно, вам часто приходится писать код для отладки, но это не совсем одно и то же. Процесс написания простого кода отличается от процесса отладки.
Чувак начинает писать простой код, и на самом деле его разглагольствования вовсе не об отладке.
Во-первых, лично я считаю, что простой код должен быть стандартом. Одно утверждение в строке. Очень редко смешивайте вызовы функций с другими операторами или выражениями. Сохраняйте простоту, чтобы при возникновении ошибок вы могли сократить ошибку до номера строки (обычно).
Я думаю, что этот пост немного расстроил меня, потому что я часто прохожу процессы отладки со студентами, а процесс переписывания и/или рефакторинга (пожалуйста, Господи, скажи мне разницу) кода обычно выполняется в процессе отладки, но это не сам процесс. Часто возникает желание переписать свой или чужой код, чтобы добиться ясности, особенно если вы не совсем понимаете, как что-то достигается. Смешивание операторов и вызовов в сложные однострочные выражения не даст никакого выигрыша в производительности по сравнению с тем, насколько эффективен, быстр или мал ваш код.
Я думал о своих любимых ошибках, которые больше всего раздражают. Обычно вы везде пишете совершенно логичный код, но вы забываете критический одиночный символ синтаксиса, и компилятор/интерпретатор начинает гадить кирпичом. В старших классах (примерно 2003-2004 гг.) я заставил компилятор C++ выдать более 100 ошибок из-за пропуска одной точки с запятой (отсюда моя наиболее часто произносимая фраза студентам C/C++/Java/Javascript/PHP: «Дон не забудьте точку с запятой»).
Интересно… могу ли я намеренно снова вызвать эту ошибку? И если да, то я, безусловно, могу показать вам, как интерпретировать сообщения об ошибках, генерируемые компилятором.
Давайте рассмотрим простой пример программы на C++. Мы объявляем класс с именем MyClass
и напишите базовую программу Hello World.
Можете ли вы обнаружить синтаксическую ошибку?
1 #include <iostream>
2
3 using namespace std;
4
5 class MyClass {
6 public:
7 MyClass() {
8 };
9
10 int main() {
11 cout << "Hello, world!" << endl;
12 return 0;
13 }
Если вы сказали: «В конструкторе, объявленном в строке 7, отсутствует закрывающая фигурная скобка», вы правы!
Однако давайте посмотрим, что компилятор clang++
говорит об этом коде:
clang++ main.cpp
main.cpp:13:2: error: expected '}'
}
^
main.cpp:5:15: note: to match this '{'
class MyClass {
^
main.cpp:13:2: error: expected ';' after class
}
^
;
2 errors generated.
Компилятор цитирует фигурную скобку в конце файла, но распознает, что декларатору класса не хватает закрывающей фигурной скобки. На самом деле он дважды цитирует строку 13 и утверждает, что в ней две ошибки:
- Закрывающая фигурная скобка, по его мнению, отсутствует для декларатора класса.
- В закрывающей фигурной скобке, которой соответствует декларатор класса, отсутствует точка с запятой.
Обе эти ошибки генерируются из-за того, как компилятор анализирует наш код. Сами по себе ошибки не являются логической синтаксической проблемой, с которой мы сталкиваемся на самом деле, поэтому вы не можете полностью доверять сообщениям об ошибках.
Давайте посмотрим на вывод другого компилятора: g++
main.cpp:13:1: error: expected ‘}’ at end of input
}
^
main.cpp:13:1: error: expected unqualified-id at end of input
Снова две ошибки, но на этот раз фактические сообщения об ошибках другие. Clang фактически распознал отсутствие закрывающей фигурной скобки декларатора класса. Это не дисс на g++ или что-то в этом роде, но это один из первых случаев, когда я действительно сравнивал результаты двух.
Что произойдет, если вы просто слепо добавите дополнительную фигурную скобку в конце программы, думая: «О, мне просто не хватает фигурной скобки в конце, давайте добавим это»:
g++ main.cpp
main.cpp:14:2: error: expected ';' after class
}
Хорошо, давайте добавим недостающее ;
в настоящее время:
Undefined symbols for architecture x86_64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Теперь он даже не может найти мою основную программу!
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass() {
};
int main() {
cout << "Hello, world!" << endl;
return 0;
}
};
Теперь компилятор даже не возвращает номер строки с ошибкой или что-то в этом роде, он просто говорит, что не может найти int main()
. Это еще худшее положение, чем раньше, и все потому, что вы не можете найти время, чтобы прочитать свой собственный код…
Тск тск тск.
Что мы собираемся с этим делать?
Многие методы, которые вы можете использовать для оттачивания проблемы такого рода, могут быть использованы для облегчения процесса поиска ошибки. Правильное форматирование вашего кода, чтобы фигурные скобки и отступы были симметричными и позволяли вашим глазам просто пройтись по коду без какого-либо ужасного форматирования (у меня есть разглагольствования по этому поводу, и в конечном итоге они будут написаны) — это хорошо, но это само по себе только упрощает отладку и само по себе не является отладкой.
Если вы не можете доверять даже сообщениям об ошибках компилятора, чему вы можете доверять?
- Вы можете доверять, когда ваш компилятор не может скомпилировать код. Тот факт, что код компилируется, не означает, что его можно безопасно запускать.
- Вы можете доверять сбою вашей программы. Часто ошибки сегментации возникают из-за неправильного использования указателя и обращения к памяти. Вы когда-нибудь пытались получить доступ к памяти в ячейке 0? Возможно, это будет еще одна запись в блоге.
- Вы можете доверять выходу вашего кода, когда он успешно компилируется, чтобы вернуть именно то, что вы ему говорите, поэтому, если он возвращает что-то неправильное, это потому, что ваш код логически/семантически неверен, и вам нужно посмотреть, что вы говорите. компьютер, чтобы сделать точно. Часто то, что вы думаете, что говорите компьютеру делать, на самом деле не то, что вы хотите ему сказать. Компьютеры не имеют возможности очень легко вывести ваше значение (проверка типов, кто-нибудь?), поэтому вы должны четко давать свои инструкции. Все, что меньше, требует двусмысленности. Если вы не получаете желаемого результата, изучите свои предубеждения относительно того, что вы уже написали и скомпилировали, и посмотрите, что нужно изменить.
Сообщения об ошибках компилятора иногда являются лишь подсказками о том, что не так с вашим кодом, а в C++, в частности, часто вообще бесполезны.
Убедитесь, что ваши фигурные скобки выстроены в любом стиле, который вам нравится, либо в стиле Страуструпа, либо в стиле K&R:
Страуструп-стиль
int main() {
return 0;
}
K&R-стиль
int main()
{
return 0;
}
И не забывайте про точки с запятой!