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

image

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

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

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

Поддержка публикации — компания Edison, которая разрабатывает системы электронных архивов и документооборота, а так же интегрирует софт и хард для промышленных предприятий.

В каждом вопросе есть 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 лет.

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

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

    –6

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

      +1

      Для системного программирования у 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

      А я на него правильно ответил =)
      • 11 ноября 2016 в 16:27

        0

        Он видимо для этого и задумывался. Чтобы счет был хотя бы 1 из 5.
  • 11 ноября 2016 в 16:19

    0

    На мой взгляд, для некоторых вопросов возможны более точные ответы:

    1) А, В и С возможны (в зависимости от конкретных размеров типов).
    2) A или В (потому что == возвращает или 0 или 1). Но не С (потому что == не может вернуть 2, стандарт это явно оговаривает)
    3) А, B или С возможны, в зависимости от размера char и от его знаковости. Но если копнуть глубже, то тут вариант «Не знаю» действительно подходит лучше, потому что стандарт С не оговаривает кодировку символов! И код пробела, вообще-то, может быть любым (стандарт только обязывает его влезать в 5 бит).
    4) Вопроса нет, поэтому я действительно не знаю.
    5) Это не «я не знаю», это называется «неопределенное поведение».

    Еще пара моментов:
     — Почему-то паддинг в структурах вы называете «отступы» — никогда не слышал такого применения, обычно отступ — это отступ в начале строки исходного кода. Но допустим, с русскоязычной терминологией у меня плохо.
     — «Более того, размер типа char в битах не определен.» Вообще-то, он определен в макросе CHAR_BITS. Вы, видимо, имели в виду, что он не определен в стандарте — это так.

  • 11 ноября 2016 в 16:23 (комментарий был изменён)

    0

    опрос скорее о том «знаете ли вы стандарт».
    на практике мы работаем на определенных платформах и компиляторах, и их поведение нам известно.
    итого. верные ответы должны быть такими:
    1)зависит от окружения и настроек выравнивания.
    2)зависит от окружения.
    3,4)опять же зависит от окружения, либо UB.
    5)UB.

    в общем-то таких примеров можно массу подобрать

    получился опрос для школьников… с понурыми ответами у доски вида «нууу я не знаю…»

    надеялся на что-то более серьезное, а не про сферических коней в вакууме.

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

    0

    Какой простой тест, я на все вопросы верно ответил как оказалось.
  • 11 ноября 2016 в 16:24 (комментарий был изменён)

    +3

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

    Вас обманули, причем дважды. Во-первых, по-русски это называется до-минор. Надеюсь, на языке «До» Моцарт не писал. Во-вторых, в латинизированной нотации до-минор — это c (прописная буква), а C (заглавная буква) — это до-мажор.


    Бонус: похожий на букву C знак в начале каждого нотного стана (после ключевых знаков) — это разорванная окружность, обозначающая размер 4/4 (и происходящая из мензуральной нотации, где она означала несовершенный, т.е. четно-дольный метр).


    (Ну и еще по-русски это называется «Большая месса»)

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

    –1

    Я бы не взял программиста который такие трюки будет использовать в реальном коде.
  • 11 ноября 2016 в 16:28

    –1

    Есть мнение, что при написании критически важных вещей первичным будет знание не стандарта, а целевых компилятора и железа, ибо разработчики оных могли забить на стандарт.
  • 11 ноября 2016 в 16:31

    0

    Сначала воспринял ответ «Я не знаю», как шуточный заведомо неверный, типа сдался. «Не определено» aka UB тут больше подходит. Как только это понял — на все ответил верно.

© Habrahabr.ru