[Перевод] Думаешь ты знаешь Си?

image

Многие программисты утверждают, что знают С. Ну что ж, у него самый известный синтаксис, он существует уже 44 года и он не захламлен непонятными функциями. Он прост!

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

Если вы думаете что он прост — пройдите этот тест. В нем всего 5 вопросов. Каждый вопрос в принципе одинаковый: какое будет значение возврата?

В каждом вопросе есть 4 варианта ответа из которых один и только один является верным.

1


struct S{
  int i;
  char c;
} s;

main(){
  return sizeof(*(&s));
}

А. 4
В. 5
С. 8
D. Я не знаю

2


main(){
  char a = 0;
  short int b = 0;
  return sizeof(b) == sizeof(a+b);
}

А. 0
В. 1
С. 2
D. Я не знаю

3


main(){
  char a = ‘ ‘ * 13;
  return a;
}

А. 416
В. 160
С. -96
D. Я не знаю

4


main()
{
  int i = 16;
  return (((((i >= i) << i) >> i) <= i));
}

А. 0
В. 1
С. 16
D. Я не знаю

5



main(){
  int i = 0;
  return i++ + ++i;
}

А. 1
В. 2
С. 3
D. Я не знаю

Вот и все, положите свои ручки. Ответы последуют сразу после музыкальной паузы

image
Великая месса в С миноре Вольфганга Амадеуса Моцарта. Да, Моцарт тоже писал на С.

Итак верные ответы
063ac49627dd48bd8e527618eb0a7fc5.jpg
Да, правильный ответ к каждому вопросу «Я не знаю».

Теперь давайте разберемся с каждым из них.

Первый в действительности о структуре отступов. C компилятор знает что хранение невыравненных данных в RAM может быть дорогостоящим, поэтому он выравнивает ваши данные за вас. Если у вас есть 5 байт данных в структуре, вероятнее всего он сделает из них 8. Или 16. Или 6. Или сколько он хочет. Существуют такие расширения как GCC атрибуты aligned и packed которые позволяют вам получить некоторый контроль над этим процессом, но они не стандартизированные. Сам по себе C не определяет атрибуты отступов и поэтому верный ответ «Я не знаю».

Второй вопрос о integer promotion. Логично предположить, что тип short int и выражение, где наибольший тип тоже short int будут одинаковыми. Но логичное не означает верное для С. Существует правило где каждое целое выражение продвигается до int. Вообще-то все еще более запутанно. Загляните в стандарт, вам понравится.

Но даже так, мы не сравниваем типы, мы сравниваем размеры. И единственная гарантия которую стандарт дает о размерах short int и int в том, что предшествующий не должен быть больше последующего. Они вполне могут быть равными. И поэтому верный ответ «Я не знаю».

Третий вопрос полностью о темных углах. Начиная с того, что ни переполнения integer ни наличие знака у типа char не определены стандартом. В первом случае у нас неопределенное поведение, во втором наличие знака зависит от конкретной реализации. Более того, размер типа char в битах не определен. Существовали платформы где он был по 6 бит (помните триграфы?) и существуют платформы где все пять целочисленных типов по 32 бита. Без уточнения всех этих деталей каждое предположение о результате невалидное, поэтому ответ будет «Я не знаю».

Четвертый вопрос выглядит коварно, но, оглядываясь назад, он не такой сложный, потому что вы уже знаете, что размер int не описывается стандартом. Он легко может быть 16 бит, тогда самая первая операция вызовет чрезмерный сдвиг и это обычное неопределенное поведение. Это не ошибка С, на некоторых платформах это даже не определено на уровне ассемблера, поэтому компилятор просто не может дать вам действующие гарантии без пожирания производительности.

И поэтому снова верным ответом будет » Я не знаю».

И последний вопрос — классический. Ни порядок вычисления операндов для + ни даже порядок приоритета между операторами инкримента не определены, поэтому по существу каждая нетривиальная операция которая вовлекает i++ и ++i является ловушкой, так как они изменяют своего операнда. Все может работать как вы и ожидаете на одной платформе и легко может сломаться на другой. Или нет. Вот она проблема неопределенных вещей. Когда вы встречаете такое, верный ответ всегда будет » Я не знаю».

image
Великая месса в С миноре, написанная Вольфгангом Амадейсом Моцартом.

И на этом этапе я должен извиниться. Очевидно, что тест провокационный и может быть даже немного оскорбительный. Я приношу извинения если он вызвал какое-либо раздражение.

Дело в том, что я изучил С приблизительно в 1998 и на протяжении целых 15 лет думал, что я хорош в нем. Я выбрал этот язык в институте и реализовал некоторые успешные проекты на С на моей первой работе и даже тогда, когда я в основном работал на С++ я воспринимал его как чрезмерно раздутый С.

Поворотный момент настал в 2013 году, когда я участвовал в программировании критического для безопасности PLC. Это был исследовательский проект автоматизации на ядерной станции, где совершенно не принималась не полная спецификация. Я должен был осознать то, что хотя я знал многое о программировании на С, абсолютно бОльшая часть того что я знал было неверным. И это знание далось мне нелегко.

В конечном итоге мне пришлось научиться полагаться на стандарт вместо народной мудрости; доверять измерениям, а не предположениям, относиться к «вещам которые просто работают» скептически, — мне пришлось изучить подход технического проектирования. Это самое главное, а не какие-то конкретные WAT анекдоты.

Я надеюсь, что этот маленький тест поможет кому-то вроде меня из прошлого, узнать этот подход за 15 минут, а не 15 лет.

Комментарии (6)

  • 11 ноября 2016 в 15:52

    –2

    Жаль, что в ИТ-мире нет гравитации или массовых вымираний, в первом случае технологии бы падали и разбивались/тонули/улетали в космос (подальше, к Конской Голове, например), а во-втором случае технологии бы вымирали вообще насовсем, как трилобиты, например. Жаль, что Си ещё жив.
    • 11 ноября 2016 в 16:17

      0

      Для системного программирования у C маловато альтернатив, зато имеется огромный багаж существующего кода. Непонятно, на что его можно заменить сейчас там, где указатели реальны, а из инструментов отладки только UART и доброе слово?
      На ум приходят Ada и Rust, но первый язык так и набрал популярности (хотя он во многих аспектах намного лучше подходит для системного программирования), а второй пока еще довольно молод, но я искренне за него болею.
      Можно, конечно, попробовать «причесать» уже имеющийся С (Cyclone, Checked C, etc.) или ограничиться небольшим «безопасным» подмножеством языка (JPL Coding Rules, MISRA C, etc.), но это все мало кто может себе позволить, т.к. программировать с такими ограничениями умеют меньшее количество специалистов, и сама разработка в итоге становится дороже (иногда существенно) и медленее (иногда на порядок).
  • 11 ноября 2016 в 15:55

    +3

    в 4 вопросе не хватает ответа »42», так как блока с вопросом там нет, видимо, это вопрос жизни, вселенной и всего такого…
    • 11 ноября 2016 в 15:59

      0

      Чукча не читатель, чукча писатель
  • 11 ноября 2016 в 16:04

    0

    Вопрос 4 порадовал:
    4

    А. 0
    В. 1
    С. 16
    D. Я не знаю

    • 11 ноября 2016 в 16:08

      0

      А я на него правильно ответил =)

© Habrahabr.ru