2016-01-16 2 views
5

Я не знаю много о C, но я понимаю основы, и, насколько я могу сказать:Каким образом утверждения на C синтаксически однозначны?

int main() { 
    if (1 == 1) printf("Hello World!\n"); 
    return 0; 
} 

и

int main() { 
    if (1 == 1) 
    printf("Hello World!\n"); 
    return 0; 
} 

и

int main() { 
    if (1 == 1) { 
    printf("Hello World!\n"); 
    } 
    return 0; 
} 

являются все точно синтаксически эквивалентны. Утверждение верно; строка печатается; фигурные скобки (по-видимому) необязательны.

Иногда, особенно здесь на SO, я вижу что-то вроде следующего:

int main() { 
    if (1 == 1) 
    printf("one is one\n"); 
    printf("is this inside the if statement??/who kn0WS\n"); 
    return 0; 
} 

Властью в CodeGolf, я привел полагать, что С пробельные агностик; лексический анализатор разбивает токены на свои составные части и пропускает пробелы вне строк.

(я имею в виду, вся причина для запятой-на-каждый оператор-вещи, так что синтаксический анализатор может лишить \n, \t, буквенных пространств и до сих пор знает, где заканчивается каждое утверждение, верно ??)

Итак, как можно однозначно разобрать предыдущий бит кода (или, может быть, кто-то может придумать лучший пример того, что я имею в виду), если игнорировать пробелы?

Если программисты C хотят писать в синтаксисе, зависящем от пробела, то почему они пишут C, и почему он учит везде, где C учит, что писать лексически двусмысленно (как мне, программисту, так и компьютеру) такие заявления?

+1

Почему вы не читали С11 проект стандарта, 'в разделе 6.8.4 заявления выбора '(возможно, также' 6.8.2 Compound statement'), посмотрите, можете ли вы это понять? – EOF

ответ

5
if (1 == 1) 
    printf("one is one\n"); 
printf("is this inside the if statement??/who kn0WS\n"); 

Второй printf() никогда не должны выполнять внутриif заявление.

Причина в том, что предыдущая строка заканчивается точкой с запятой, которая указывает конец if-block для выполнения.

(я имею в виду, вся причина для запятой-на каждый оператор-вещь так синтаксический анализатор может лишить \ п, \ т, буквенные пространства и до сих пор знают, где каждый оператор заканчивается, верно? ?)

Так как можно однозначно разобрать предыдущий фрагмент кода (или, возможно, кто-то может придумать лучший пример того, что я имею в виду), если пробелы не принимается во внимание?

Синтаксический пример:

if (1 == 1) // если - ( и ) - заявления (или блок) следовать, не пропустите все пробельные

// нет { found -> single statement, scan до ; (внешние цитаты/комментарии)

printf("one is one\n"); // ;, конец if-блока

Без брекетов в может быть только один оператор, если -блоки.

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

Особый случай:

int i = 0; 
while(i++ < 10); 
    printf("%d", i); 

Здесь printf() будет выполнять только один раз. Отметьте ; в конце while().

В случае пустого оператора, то лучше использовать:

while(i++ < 10) 
    ; 

сделать намерение ясно (или, в качестве альтернативы, пустой блок {} может быть использован, а).

+4

Так бесцельные if-блоки могут состоять максимум из одного заявления? – cat

+5

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

+0

Правильно, дважды :) –

2

Без брекетов это просто следующее утверждение после if. Пробел не имеет значения

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

3

tl; dr Единственная двусмысленность в том, как трудно человеку читать. С точки зрения компилятора синтаксис совершенно однозначен.

Есть только два (скомпилирована и синтаксический приемлемая) возможность после if заявления:

  1. брекетов, как в

    if(x) { 
        DoFoo(); 
    } 
    // or 
    if(x) { DoFoo(); } 
    

    В этом случае все, что в {...} будет выполнять если условие выполнено.

  2. Нет брекеты, как и в

    if(x) 
        DoFoo(); 
    // or 
    if(x) DoFoo(); 
    

    В этом случае только следующий оператор не будет выполняться, если условие выполнено.

Вы правы, что C является непрозрачным агностиком. В результате опускание брекетов может привести к некоторым сложным ошибкам. Например, в этом коде, DoBar() выполнит ли или не удовлетворяется условие:

if(x) 
    DoFoo(); 
    DoBar(); 

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

if(x) 
    DoFoo(); 
    DoBar(); 
else 
    DoBaz(); 

Ни один из примеров вы вывешенные не неоднозначны с точки зрения компилятора, но не-фигурные скобки версии путаете от человеческая перспектива. Оставляя брекеты, часто приводит к труднодоступным ошибкам.

+0

C не имеет «команд». Вы имеете в виду «заявление»? –

+1

@TomKarzes Исправлено. Благодарю. –

2

Читаемость является единственной двусмысленностью в заявлении:

if (1 == 1) 
    printf("one is one\n"); 
printf("is this inside the if statement??/who kn0WS\n"); 

Единственный раз, когда первое заявление после if(...) заявления следует выполнить, то, если это оценивается TRUE.

брекеты, {...} помогают удалить читаемость двусмысленности,

if (1 == 1) 
{ 
    printf("one is one\n"); 
} 
printf("is this inside the if statement??/who kn0WS\n"); 

но синтаксические правила остаются прежними.

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

5

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

Что является двусмысленным, в C и на многих других языках, является «болтающимся другим». Например, предположим, что у вас есть вложенный оператор if с одним else после второго. Это можно сгруппировать следующим образом:

if (expr) 
    if (expr) 
     statement 
    else 
     statement 

Или можно сгруппировать следующим образом:

if (expr) 
    if (expr) 
     statement 
else 
    statement 

Единственное различие между этими двумя, как они с отступом, который игнорирует C. В этом случае двусмысленность разрешается с использованием первой интерпретации, то есть оператор else связывается с ближайшим предыдущим оператором if.Для достижения второй интерпретации, нужны фигурные скобки:

if (expr) { 
    if (expr) 
     statement 
} 
else 
    statement 

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

if (expr) { 
    if (expr) 
     statement 
    else 
     statement 
} 
+0

Выходит в основном из [бирка: golang], я желаю, чтобы они * были * необходимы. – cat

+1

@cat: ... и Python избегает проблемы, требуя локально согласованного отступа. Есть более чем один способ обмануть ... о, извините. Существует несколько подходов к этой проблеме. –

+0

@JonathanLeffler действительно, теперь, только если у меня есть Python с фигурными скобками и необязательной статической типизацией (также спасибо за смех: P) – cat

2

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

for(i = 0; i < 2; i++) 
    for(j = 0; j < 4; j++) 
     If(...) 
     printf(..); 
     else 
     printf(..); 

эквивалентно:

for(i = 0; i < 2; i++) 
    { 
     for(j = 0; j < 4; j++) 
     { 
      If(...) 
      { 
       printf(..); 
       } 
       else 
      { 
       printf(..); 
      } 
     } 
    } 

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

+1

Если вы использовали K & R-стиль на фигурных скобках (например, 'if (cond) { 'и'} else {'), это будет не так долго ... –

+0

@JohnHascall Вы имеете в виду, например: if (..) {..} в одной строке? –

+0

Трудно передать в формате комментариев, но короткий оператор if (cond); на одной строке - пример. 'If ​​(cond) {' 'statement1;' 'statement2;' '}' на четырех строках - это другое. 'If ​​(cond) {' 'statement1;' '} else {' 'statement2;' '}' на пяти строках - это другое. –

2

Другой причиной использования брекетов является то, что просто опечатка может укусить вас трудно:

#include <stdio.h> 
int main(void) { 
    if (0 == 1) 
    printf("zero is one\n"), 
    printf("is this inside the if statement?? /who kn0WS\n"); 
    return 0; 
} 

Посмотрите внимательно ...

+1

Действительно! Я также вижу, что ваша подлая попытка разоружить мой триграф, но ни в коем случае ;-) – cat

+1

Триггеры никогда не должны были быть выброшены в темноту из того, что произошло ... –

+0

Я слышал, как люди называли их «одним из самых умных/полезных вещи о С ", но я не вижу, как они служат цели, кроме путаных программистов. – cat

Смежные вопросы