У меня есть простая программа:
#include <stdio.h>
struct S
{
int i;
};
void swap(struct S *a, struct S *b)
{
struct S temp;
temp = *a /* Oops, missing a semicolon here... */
*a = *b;
*b = temp;
}
int main(void)
{
struct S a = { 1 };
struct S b = { 2 };
swap(&a, &b);
}
Как видно на, например, ideone.com, это дает ошибку:
prog.c: In function 'swap': prog.c:12:5: error: invalid operands to binary * (have 'struct S' and 'struct S *') *a = *b; ^
Почему компилятор не обнаруживает отсутствующую точку с запятой?
Примечание: этот вопрос и ответ на него мотивированы этим вопросом . Хотя есть и другие вопросы, похожие на этот, я не нашел ничего, что упоминало бы о возможностях свободной формы языка C, что является причиной этой и связанных с ней ошибок.
Ответы:
C - это язык свободной формы . Это означает, что вы можете форматировать его разными способами, и это все равно будет законная программа.
Например, заявление вроде
можно было бы написать как
или как
Итак, когда компилятор видит строки
он думает это значит
Это, конечно, недопустимое выражение, и компилятор будет жаловаться на это вместо отсутствующей точки с запятой. Причина, по которой он недействителен, заключается в том, что
a
это указатель на структуру, поэтому*a * a
он пытается умножить экземпляр структуры (*a
) на указатель на структуру (a
).Хотя компилятор не может обнаружить отсутствующую точку с запятой, он также сообщает о совершенно несвязанной ошибке в неправильной строке. Это важно заметить, потому что сколько бы вы ни смотрели на строку, в которой сообщается об ошибке, ошибки там нет. Иногда такие проблемы требуют, чтобы вы посмотрели на предыдущие строки, чтобы убедиться, что они в порядке и без ошибок.
Иногда вам даже нужно заглянуть в другой файл, чтобы найти ошибку. Например, если файл заголовка определяет структуру последним в файле заголовка, а точка с запятой, завершающая структуру, отсутствует, то ошибка будет не в файле заголовка, а в файле, который включает файл заголовка.
А иногда становится еще хуже: если вы включаете два (или более) заголовочных файла, и первый из них содержит неполное объявление, скорее всего, синтаксическая ошибка будет указана во втором заголовочном файле.
С этим связана концепция последующих ошибок. Некоторые ошибки, как правило, из-за отсутствия точек с запятой, сообщаются как множественные ошибки. Вот почему при исправлении ошибок важно начинать сверху, так как исправление первой ошибки может привести к исчезновению нескольких ошибок.
Это, конечно, может привести к исправлению одной ошибки за раз и частой перекомпиляции, что может быть обременительным для больших проектов. Признание таких последующих ошибок - это то, что приходит с опытом, и, увидев их несколько раз, легче найти настоящие ошибки и исправить более одной ошибки за одну перекомпиляцию.
источник
temp = *a * a = *b
может быть допустимым выражением, если оноoperator*
было перегружено. (Вопрос, тем не менее, помечен как «C».)Следует запомнить три вещи.
*
в C может быть как унарным, так и бинарным оператором. Как унарный оператор он означает «разыменование», как бинарный оператор - «умножить».Результатом этих двух фактов является анализ.
Первый и последний
*
интерпретируются как унарные, а второй*
интерпретируется как двоичный. С точки зрения синтаксиса это выглядит нормально.Ошибка возникает только после синтаксического анализа, когда компилятор пытается интерпретировать операторы в контексте их типов операндов.
источник
Несколько хороших ответов выше, но я уточню.
На самом деле это тот случай,
x = y = z;
когда обоимx
иy
присваивается значениеz
.То, что вы говорите
the contents of address (a times a) become equal to the contents of b, as does temp
.Короче говоря,
*a *a = <any integer value>
это верное заявление. Как указывалось ранее, первый*
разыменовывает указатель, а второй умножает два значения.источник
y
это даже не переменная, это выражение*a *a
, и вы не можете присвоить результат умножения.Большинство компиляторов анализируют исходные файлы по порядку и сообщают строку, в которой обнаруживают, что что-то не так. Первые 12 строк вашей программы C могут быть началом действующей (безошибочной) программы C. Первые 13 строк вашей программы не могут. Некоторые компиляторы будут отмечать расположение вещей, с которыми они сталкиваются, которые сами по себе не являются ошибками, и в большинстве случаев не будут вызывать ошибки позже в коде, но могут быть недопустимыми в сочетании с чем-то еще. Например:
Само
int foo;
по себе заявление было бы прекрасно. Аналогично декларацииfloat foo;
. Некоторые компиляторы могут записывать номер строки, в которой появилось первое объявление, и связывать с этой строкой информационное сообщение, чтобы помочь программисту идентифицировать случаи, когда более раннее определение на самом деле является ошибочным. Компиляторы также могут сохранять номера строк, связанные с чем-то вроде ado
, о котором можно сообщить, если связанныйwhile
не отображается в нужном месте. Однако в случаях, когда вероятное место возникновения проблемы должно быть непосредственно перед строкой, в которой обнаружена ошибка, компиляторы обычно не утруждают себя добавлением дополнительного отчета для этой позиции.источник