Думаешь, ты знаешь Си?

Original author: Oleksandr Kaleniuk
  • Translation
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
Большая месса в С миноре Вольфганга Амадеуса Моцарта. Да, Моцарт тоже писал на С.

Итак верные ответы

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

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

Первый в действительности о структуре отступов. 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 лет.

Перевод: Ольга Чернопицкая

Only registered users can participate in poll. Log in, please.

На сколько вопросов вы ответили «я не знаю»

  • 9.7%1132
  • 5.7%277
  • 5.1%369
  • 4.7%464
  • 14.3%5195
  • 60.5%я не знаю823
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 270

    –32
    Жаль, что в ИТ-мире нет гравитации или массовых вымираний, в первом случае технологии бы падали и разбивались/тонули/улетали в космос (подальше, к Конской Голове, например), а во-втором случае технологии бы вымирали вообще насовсем, как трилобиты, например. Жаль, что Си ещё жив.
      +16
      Для системного программирования у C маловато альтернатив, зато имеется огромный багаж существующего кода. Непонятно, на что его можно заменить сейчас там, где указатели реальны, а из инструментов отладки только UART и доброе слово?
      На ум приходят Ada и Rust, но первый язык так и набрал популярности (хотя он во многих аспектах намного лучше подходит для системного программирования), а второй пока еще довольно молод, но я искренне за него болею.
      Можно, конечно, попробовать «причесать» уже имеющийся С (Cyclone, Checked C, etc.) или ограничиться небольшим «безопасным» подмножеством языка (JPL Coding Rules, MISRA C, etc.), но это все мало кто может себе позволить, т.к. программировать с такими ограничениями умеют меньшее количество специалистов, и сама разработка в итоге становится дороже (иногда существенно) и медленее (иногда на порядок).
        +7
        Иногда из инструментов отладки только 1 светодиод. А иногда нет даже его…
          +3
          … но есть логический анализатор и осциллограф…
          • UFO just landed and posted this here
            +3
            Писал программки для защищенного режима, в процессе работы отключалась видеокарта, выводил адрес инструкции с ошибкой спикером — два коротких бипа «1», один длинный «0».
            –4
            C++ же.
            Я, наверное, ни за какие деньги не взялся бы писать на чистом С. Это хороший язык, но слишком простой. Я не доверяю софту, написанному на С, потому что понимаю, как сложно писать на этом языке большие системы без ошибок. Это тот же ассемблер, только кросс-платформенный (ну, по большей части).
              +3
              На самом деле C++ добавляет собственных тараканов к тем, что есть в C. Конечно, если писать на С++ правильно, то код будет намного безопасней. Но если писать на С правильно, то опять же можно бороться со сложностью. Есть куча прекрасных примеров: ядро linux, git, nginx, etc
                –1
                В ядре linux, на сколько знаю, используют «С с классами», у гита (на зеркале на гитхабе) меньше половины кода на сях, что о многом по моему говорит, nginx по первому взгляду (не изучал особо) и правда на чистых сях, но это скорей исключение и такой проект, как мне кажется, гораздо трудней поддерживать, чем любой ООП. Хотя не исключаю, что у меня ООП головного мозга.
                  –2
                  В ядре linux, на сколько знаю, используют «С с классами»


                  Но как, сэр?
                  Это же чисто эппловская примочка, да ещё и какая-то высокоуровневая…
                    +3
                    Элементарно, сэр. Структура, которая хранит указатели на функции — и вот мы получаем абстрактный объект. Заполняем указатели — и вот у нас экземпляр конкретного класса. Заменяем некоторые указатели на свои, немного магии с container_of и вот у нас наследование.

                    Ядро Linux объектно-ориентировано, хоть и написано на C. Такие дела.
                      +1
                      «Си с классами» — «C with classes» — название первой версии С++, написанной Страусом трупом.
                      Соотв., Ваше утверждение выглядит странно.
                      Я уже не говорю о том, что «структура, хранящая указатели на функции» — не обязательно эмулирует классы.
                        0
                        Извините, но «Си с классами» сказал не я. Я просто рассказал как ядро устроенно внутри.

                        Я уже не говорю о том, что «структура, хранящая указатели на функции» — не обязательно эмулирует классы.

                        Не обязательно. А ещё для реализации ООП не обязательно иметь ключевое слово class в языке. Более того, язык вообще не может знать про классы. И при этом на нём можно писать объектно-ориентированный код.
                          0
                          Я имел ввиду совсем не то. Прошу прощения, не углублялся в историю и «трупов» не изучал, потому любую имитацию ООП на чистом С называл «С с классами». Больше не буду, еще раз простите.
                      • UFO just landed and posted this here
                          –1
                          Вы говорите про ObjC, а это совсем другой язык. И еще вам стоит знать, что «компилятор Objective-C входит в GCC и доступен на большинстве основных платформ». То, что это был основной язык программирования (коим возможно остается и сейчас) для устройств фирмы Apple никак не делает его «эппловским».
                            0
                            Да что я опять не так сказал? Вместо минуса лучше бы написали где я не прав, в следующий раз хоть знать буду.
                          0
                          Зависит от задач, как всегда. Если у нас что-то гетерогенное, с базами данных, REST-запросами, гибким логгированием и GUI до кучи, но при этом хочется скорости — надо брать C++ иначе можно рехнуться.
                          Если у нас есть конкретное очерченная задача (git) — хватит и C. Если нет rich runtime (linux) — опять С. Если мы максимально экономим ресурсы (nginx) — снова С.

                          Но при этом почти весь хороший код на C всё равно оперирует объектами. Да, там часто проблемы с полиморфизмом и наследованием. Но абстракция данных и инкапсуляция цветет пышным цветом. Достаточно посмотреть API любой сишной библиотеки (кроме libc, естественно).
                            0
                            Если мы максимально экономим ресурсы (nginx) — снова С.

                            Совершенно неочевидно, почему, кстати.
                            0
                            В ядре активно используются gcc-шные расширения, но «объектно-ориентированные» фишки реализуются вполне себе штатными средствами чистого Си. Никакого особого «Си с классами» там нет:)
                              0
                              Прошу прощения. Я не особо знаю всякие экзотические вещи, такие как названы выше. Под «С с классами» я имел ввиду реализацию ООП на чистых Сях, иногда с использованием GTK и прочих, по моему мнению, извращений. Да, экономия ресурсов и «переносимость», но это неудобно и во многих случаях напоминает как раз написание своего С++, но почему не взять готовый никто отвечать не хочет.
                                0

                                Я не противник С++, но в ветке видел несколько ответов почему в некоторых задачах не взять готовый С++ вместо С:


                                • Больший рантайм. Напирмер в андройде libstdc++ есть несколько на выбор Android/Sdk/ndk-bundle/sources/cxx-stl/, подключив любой и хоть немного заюзав — мой apk вырастет. Конечно можно и на урезанном С++ писать, но тут писали якобы таких спецов меньше. Так же если мы скажем что-то оптимизируем и глазами изучаем скажем .map файл, куча мелких с++ функций рантайма начнут мешать.
                                • Маленький компилятор, JIT tcc умещается где-то в 100-200кб. Аналогов тут не так много, разве что lua. Так что если мы хотим вставить в тот же apk и не раздуть его, llvm тут не подойдет.
                                  0
                                  Вы правда смотрите на объем apk и вообще скомпилированного кода?
                                  Может в этом и есть какой-то смысл, но не в ущерб читаемости, удобству и качеству. По крайней мере на тех платформах, где оперируют уже давно гигабайтами.
                                  В ближайший месяц мы выпускаем приложение на Android, в котором часть кода написано на C++ (NDK) с использованием c++_shared (LLVM). Объем библиотек под все платформы вышел на 40-45Мб (7 архитектур), то есть в среднем 6.5Мб на архитектуру. Все приложение весит около 17Мб (apk). Для сравнения то же приложение на iOS весит 15Мб (С и ObjC). Вот уж не вижу смысла бороться ради пары мегабайт.
                                  В плане оперативной памяти все аналогично.
                                    0

                                    А я и не говорю, что всегда это надо, я сам предпочитаю C++, потому и говорю — в некоторых задачах. А смотреть на размер — да, бывало, было дело сократили размер so до 1.5мб\архитектура (3мб apk, против изначальных ~40мб). А вот приложения по 17мб я и сам не люблю, с нашим то мобильным инетом, а как дела в Китае? А в Индии?


                                    Другое дело что, если это игра, то ресурсы и\или сам код могут занять много больше рантайма и смысла действительно не будет. Но это не значит, что таких задач не бывает, это мы еще не коснулись микроконтроллеров, демосцены, системного\ядерного кода, специфичных кейсов типа портирования c winelib.

                          0
                          Непонятно, в чем преимущество C++ в тех нишах, где позиции С по прежнему сильны: embedded, firmware, kernel core, вот это все. Там не нужны исключения, полиморфизм и RTTI, там нет стандартной библиотеки, там мало толку от шаблонов, там часто вообще нет кучи, т.е. единственный доступный operator new — размещающий, и т.п.
                          Для «нормального» C++ нужен гораздо более жирный рантайм, а специалистов, умеющих писать на «обрезаном» С++ — намного меньше, чем специалистов по С, т.к. такому «ограниченному» использованию С++ почти нигде не обучают.
                            +3
                            Основное преимущество C++ перед C в перечисленных нишах — метапрограммирование.
                              +3
                              Наверное, если система очень небольшая и влазит в считаные килобайты, от С++ пользы нет, один вред.Я под такие системы использовал компилятор С++, но писал, по сути, на С. А вот под STM32 d проекте на 5000 строк кода я использовал и шаблоны, и полиморфизм. Можно ли без них? Конечно, можно. Хочу ли я не пользоваться этими средствами? Нет, не хочу, они позволяют мне писать понятный, легко расширяемый и корректный код.
                                0
                                Я как-то делал библиотечку, которая напрямую компилировалась в простые выражения присваивания или чтения битов ввода-вывода.
                                Ну, типа — там несколько связанных битов, конфигурирующих ввод-вывод и сам I/O pin.
                                Разумеется, это было несколько инлайновых шаблонов с наследованием — выглядело, в общем, неплохо.
                                А биты адресовать невозможно (т.е.Си с ними вообще с ума сходил).
                                Ну и конструкторы-деструкторы — штука няшная.
                                  0
                                  А биты адресовать невозможно (т.е.Си с ними вообще с ума сходил).

                                  Битовые поля структур не могли бы помочь?

                                    0
                                    Битовые поля структур точно так же не адресуемы.
                                    Ну т.е. сделать на них тоже можно было — но вряд-ли получилось бы эффективнее.
                                    Плюс необходимость их самому определять «ручками» — а биты определялсь при конфигурировании проекта и выборе контроллера.
                                    0
                                    А биты адресовать невозможно

                                    std::vector<bool> смотрит на это заявление с недоумением.
                                      –1
                                      И хде там биты?
                                        0
                                        Внутри. Это битовый вектор.
                                          +1
                                          Внутри.


                                          А.
                                          Значиццо у нас есть микроконтроллер, для которого есть Си/С++ со специфическим расширением — типом _bit.
                                          Некоторые биты имеют специфическую функциональность — ну там ввод/вывод, настрока пина на вывод, открытый сток/пуш-пул…
                                          И поверх всей этой радости предлагается присобачивать std::vector, который вообще «Не обязательно хранит свои данные в одном непрерывном куске памяти.» (© cppreference)?

                                          Обёртку я тоже нопейсать могу — что я, собственно, там и сделал.
                                            0
                                            поверх всей этой радости предлагается присобачивать

                                            Нет, std::vector<boot> приведён как широко известный пример техники адресации битов.

                                            И, да, правила.
                                              0
                                              Это эмуляция битовой адресации, а не.
                                              И да, в треде утверждалось, что «С++ нафик не нужен в эмбеде со своими шаблонами, наследованием и т.д.».
                                              Например, вот tyt.
                                              Ваш тезис ничего не добавляет к моему оппонированию этому.
                                                0
                                                Попробуйте сделать std::swap на двух элементах std::vector<bool>.
                                                  –1
                                                  Попробовал, сделал:
                                                  #include <vector>
                                                  
                                                  namespace std {
                                                          void swap(vector<bool>::reference a, vector<bool>::reference b)
                                                          {
                                                                  bool t = a;
                                                                  a = b;
                                                                  b = t;
                                                          }
                                                  }
                                                  
                                                  int main()
                                                  {
                                                          std::vector<bool> v(2);
                                                  
                                                          std::swap(v[0], v[1]);
                                                          return 0;
                                                  }
                                                  

                                                  Учитывая то, что исключений здесь быть не может, в чём подвох?
                                                    0
                                                    А скажите, зачем вы переопределили std::swap?
                                                      0
                                                      Готовый std::swap ожидает, что ссылочный тип оканчивается на '&', что, конечно же, не всегда правда. Это же, вроде, известная ловушка шаблонных функций?
                                                      0
                                                      В современных компиляторах, поддерживающих C++11 и выше, swap давно уже как перегружен для
                                                      vector<bool>::reference
                                                      
                                                      и всё отлично работает.
                                                        0
                                                        Да, верно. Перегрузка нужна для С++98. Тем более непонятно, в чём подвох.
                                      0
                                      а есть и недостатки. Например, подключение библиотек STL, и программа разрастается до таких размеров, что не влазит в контроллер. В итоге придется писать на подмножестве, одинаковом для с и с++.
                                      +4
                                      Метапрограммирование, строгая типизация
                                      Нередко более эффективный код на шаблонах, чем код в лоб, правда ценой раздутия кода. То же самое, конечно, можно сделать на сях, но дольше и с большей вероятностью ошибки.
                                        –3

                                        Язык С практически ничем не отличается от языка С++ в области строгости типизации. Единственное отличие, которое навскидку приходит в голову — это возможность неявной конверсии void * в другие объектные типы указателей в языке С. Все.


                                        Так о какой строгой типизации вы ведете речь?

                                          0
                                          например, неявный каст const T* к T*
                                            0
                                            Это откуда? В языке С никогда не было неявного приведения `const T*` к `T*`. Разумеется с того момента, комитет X3J11 ввел в язык С `const`.
                                            +1
                                            Ещё перечисления (преобразующиеся к целочисленным типам, но не обратно), особенно появившиеся в C++11 scoped enumerations (к которым и целочисленные типы не преобразуются).

                                            Явное приведение типов (которое в cast notation преобразует практически что угодно к чему угодно) разбито на static_cast, const_cast и reinterpret_cast (а также имеющий смысл только для C++ dynamic_cast).

                                            Объектно-ориентированным программированием на Си тоже занимаются, реализуя наследование вложением одних структур в другие. Отсутствие встроенных для этого средств приводит к использованию грязных хаков, со статическим контролем типов при этом всё совсем плохо. Например, в Glib Object System такие функции, как g_object_ref/g_object_unref/g_object_new принимают/возвращают просто void*, чтобы не приходилось явно приводить типы; в gtk функции создания виджетов возвращают GtkWidget* (указатель на базовый класс всех виджетов, а не на конкретный класс виджета), и т. д.
                                              –1

                                              Да, перечисления — хороший пример, в дополнение к void *. Еще что нибудь? (Явные касты же — не по теме. Явные касты — это средства обхода контроля типов.)


                                              Заявления о более либеральном контроле типов в С как правило базируются на "пионэрских" верованиях, что С якобы разрешает такие вещи, как int *p = 123 или игнорирование const-корректности (см. яркий образчик такого заявления выше). В качестве доказательства приводится тот факт, что чей-то уютненький компилерчик выдает на это "всего лишь warning".


                                              Понятно, что к реальности такие заявления никакого отношения не имеют.

                                                +1
                                                Явные касты же — не по теме. Явные касты — это средства обхода контроля типов.

                                                Так обходить по-разному можно. (T)v значит вообще практически все проверки выключить, в то время как специализированные касты отключают лишь некоторые проверки (например, static_cast и reinterpret_cast не позволят ненароком сбросить const).
                                                  –1

                                                  Это прекрасно. Базовые факты о поведении С++-style casts тут все хорошо известны.


                                                  Как это относится к теме более строго контроля типов? Контроль типов в языке — это именно контроль неявных преобразований. Явные преобразования (касты) к этой теме не относятся.


                                                  В любом случае, "созвездие" С++-style casts позволяет вам сделать все, что угодно (не говоря уже о том, что C-style cast никто в С++ не запрещал).

                                                    0
                                                    Как это относится к теме более строго контроля типов?

                                                    Приведу пример. Допустим, есть у нас некая функция, которая по каким-то причинам (крайне устаревший код, некомпетентность автора, потенциальное наличие заведомо нереализуемых в данной ситуации особых вариантов поведения и т. д.) принимает параметр типа char*, а не const char*, но аргумент свой, как мы точно знаем, не модифицирует:
                                                    int f(char *str);
                                                    
                                                    А нам нужно вызвать её из другой функции, передав аргумент типа const char*. Приходится использовать const_cast:
                                                    int g(const char *str, int x)
                                                    {
                                                        return x + f(const_cast<char*>(str));
                                                    }
                                                    

                                                    Если человек на фоне недосыпа вместо этого случайно написал
                                                    int g(const char *str, int x)
                                                    {
                                                        return x + f(const_cast<char*>(x));
                                                    }
                                                    
                                                    то компилятор ему об этом скажет: const_cast<char*>(x) — невалидная конструкция.

                                                    Но в языке Си мы вынуждены использовать cast notation (ничего другого нет) — там аналогичная функция
                                                    int g(const char *str, int x)
                                                    {
                                                        return x + f((char*)x);
                                                    }
                                                    
                                                    с точки зрения компилятора будет полностью валидной.

                                                    В одном случае контроль типов сработал, в другом — нет.
                                                    В любом случае, «созвездие» С++-style casts позволяет вам сделать все, что угодно (не говоря уже о том, что C-style cast никто в С++ не запрещал).

                                                    В любом случае ремни безопасности можно не пристёгивать.

                                                    Если у человека есть _цель_ отстрелить себе ногу, то C++-style casts ему действительно ничем не помогут. Но контроль типов — это немного про другое.
                                                  0
                                                  Собственно, warning's не имеют никакого отношения к корректности языковых конструкций (соответствия синтаксису).
                                                  Это всего лишь дружеская необязательная помощь компилятора.
                                                    0
                                                    Таки имеют.

                                                    5.1.1.3 Diagnostics
                                                    A conforming implementation shall produce at least one
                                                    diagnostic message (identified in an implementation-defined
                                                    manner) if a preprocessing translation unit or translation
                                                    unit contains a violation of any syntax rule or constraint...

                                                    В сноске:
                                                    ...Of course, an implementation is free to produce any
                                                    number of diagnostics as long as a valid program is still
                                                    correctly translated. It may also successfully translate
                                                    an invalid program.

                                                    Warning — одна из реализаций «diagnostic message». А успешная компиляция программы допускается даже если последняя некорректна.
                                                      +1
                                                      Но из этого не следует, что каждый warning приходится на одно такое требование стандарта.
                                                      –2

                                                      Нет. Это популярное наивное заблуждение.


                                                      Во-первых, это было бы справедливо только в отношении компиляторов, которые строго и аккуратно разделяют warnings и errors про указанному вами принципу. Таких компиляторов С просто нет.


                                                      Например, при компиляции в GCC в умолчательной конфигурации огромное число warnings — это именно грубейше некорректные языковые конструкции, которые GCC просто так решил отрапортовать как warnings (ради совместимости с каким-нибудь старинным кодом).


                                                      Попыткой достижения строгого деления на warnings и errors в GCC является флаг -pedantic-errors, но и он еще не идеален.


                                                      Во-вторых, формально придраться к GCC тут нельзя, ибо в С нет понятия warnings и errors. Есть только понятие diagniostic message. Задача компилятора — сказать хоть что-то. А уж разбираться затем в формальной корректности конструкции, листая стандарт языка — это ваша задача.

                                                      0
                                                      main()
                                                      {
                                                      	int *p = 123;
                                                      	return 6["Нет"];
                                                      }
                                                      

                                                      gcc -std=c99 testc.c
                                                      testc.c:1:1: warning: return type defaults to 'int'
                                                      main()
                                                      ^
                                                      testc.c: In function 'main':
                                                      testc.c:3:11: warning: initialization makes pointer from integer without a cast
                                                      int *p = 123;
                                                      ^


                                                      Т.е. оба примера допустимы в С, но недопустимы в С++
                                                        0

                                                        Неверно. И main() и int *p = 123; являются грубыми ошибками ("contraint violation") в одинаковой степени и в С, и в С++. Ваш компилятор С выдал требуемые диагностические сообщения, сообщив вам тем самым, что ваша программа не является программой на С и ее поведение не определено языком С. Точка.


                                                        Однако видя, что вы пока не умеете "на глаз" отличать "безобидные" диагностические сообщения от серьезных ошибок, я бы посоветовал вам почитать документацию своего компилятора и попросить у него помощи в этом. В частности, в GCC это делается флагом -pedantic-errors. Возьмите в привычку пользоваться этим флагом всегда


                                                        main.c:1:1: error: return type defaults to 'int' [-Wimplicit-int]
                                                         main()
                                                         ^~~~
                                                        main.c: In function 'main':
                                                        main.c:3:11: error: initialization makes pointer from integer without a cast [-Wint-conversion]
                                                          int *p = 123;
                                                                   ^~~
                                                        
                                                          0
                                                          Я прекрасно понимаю как оно скомпилируется и как работает.
                                                          Например вижу ошибку выхода за границу массива в коде выше, о которой НЕТ варнинга.

                                                          И меня такое деление устраивает — error как принципиально не компилируемый код, warning- обратите внимание.

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

                                                          Например, вчера у меня после смены версии gcc, в newlib (это такая версия libc, кто не в курсе), появились варнинги (типа char применяется как индекс массива, ай-яй).

                                                          И что вы предложите мне выкинуть — платформу, gcc, newlib или %^&%##-pedantic-errors?
                                                            0

                                                            Уважаемый, во-первых, то, что вам тут пытаются объяснить, это тот простой факт, что main() и int *p = 123; в языке С формально являются именно принципиально не компилируемым кодом. То, что ваш компилятор этот код проглатывает — это замечательно, но к языку С никакого отношения не имеет. И, соответственно, не имеет никакого отношения к теме этой дискуссии. С таким же успехом вы могли бы рассказывать нам, что там у вас компилируется в компиляторе Фортрана.


                                                            Никто вас при этом не призывает прекратить писать такой код — пишите на здоровье что вам заблагорассудится. Но не удивляйтесь потом, что на ваши попытки выдать ваш код за код на С будут смотреть с откровенным нескрываемым недоумением.


                                                            Во-вторых, упоминая -pedantic-errors я вам не пытаюсь навязывать никакие привычки. Никто вас не заставляет использовать -pedantic-errors повседневно, тем более, что вы к этому явно еще не готовы. Вы сделали стандартный, уже всем надоевший ляп: попытались доказывать какие-то странные верования о языке С на основе разухабистого поведения неумело сконфигурированного (или вообще не сконфигурированного) компилятора. Но вот перед тем как презентовать свои верования на публику — не помешало бы проверить их с помощью этого флага. Это позволит вам избежать попадания в такие постыдные ситуации в будущем.


                                                            В-третьих, если я что вам и навязываю тут — так это сам язык С. Не больше, не меньше. За одиннадцать лет в JTC1/SC22/WG14 у меня выработалась такая привычка.


                                                            P.S. К чему здесь вдруг ваши разглагольствования на тему "что вы предложите мне выкинуть" — мне не ясно. Никто вам тут ничего выкидывать пока не предлагал.

                                                              0
                                                              Я не знаю, в каком выдуманном мире вы живете, или как трактуете мат.часть С, но этот код прекрасно компилируется и Clang, и MSVC, и самое интересное работает так, как мне и требуется.

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

                                                              Это глупая дискуссия между теоретиком и практиком.
                                                                –2

                                                                О! Уж совсем детские наезды пошли!


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


                                                                Что касается вашей феерически "альтернативной" интерпретации работы этих компиляторов — здесь это никому не интересно. Здесь речь идет именно и только о языке С. И все эти компиляторы немедленно откажутся трактовать ваш ошибочный код, как код на С. В противном случае они бы под общий хохот были бы вышвырнуты с рынка смачным пинком под зад.


                                                                Что же касается вашей "практики" — это не практика, заблудший вы наш, это что-то вроде "диссертации Василия Ивановича", в которой он, согласно известному анекдоту, оторвав таракану ноги, пришел к выводу, что "безногий таракан не слышит". Или, если угодно, это — изучение творчества группы Битлз по напевам Рабиновича, согласно другому известному анекдоту.


                                                                Если вы намерены серьезно изучать и использовать язык С, всю эту вашу "практику" вам придется тщательно стереть из головы — она бесполезна и будет вам только мешать. Придется накапливать с нуля совсем новую практику. А уж когда вы напрактикуетесь хотя бы с половину моего — вот тогда и понимание теории подтянется. Я знаю, ибо сам когда-то таким был.

                                                                  +2
                                                                  Как показано ниже со ссылками на первоисточники, стандарта вы тоже не знаете, как и практики.

                                                                  Потому оставьте весь поучающий пафос себе.
                                                                    –2

                                                                    Молодой человек, вы со своими "ссылками не первоисточники" смачно сели в лужу именно так, как садится в нее практически каждый новичок, неправильно понявший 6.3.2.3. Вы у меня уже, наверное, тысячный...


                                                                    Учитесь не просто читать, а еще и понимать прочитанное. Я вам там все подробно разъяснил, под вашей "ссылкой не первоисточник".


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

                                                              +1
                                                              Например вижу ошибку выхода за границу массива

                                                              То есть вы ещё не перешли на utf-8?
                                                                0
                                                                Нет. И если верить стандарту С11, надо было писать u8«Нет»
                                                                  +1
                                                                  В цивилизованном мире UTF-8 — кодировка по умолчанию. Так что если вы сохранили исходник в UTF-8, то и результирующая строка в норме должна быть в UTF-8, даже в отсутствие префикса u8 (как минимум, у меня под линуксом в gcc дела обстоят именно так). Это не требуется стандартом (source character set и execution character set, в принципе, могут быть разными), но и не противоречит ему.

                                                                  Чтобы (достоверно) увидеть ошибку выхода за границы массива в вашем примере, надо знать компилятор и кодировку исходника. Без этой информации отсутствие выхода за границы более вероятно.
                                                                    –1
                                                                    В цивилизованном мире UTF-8 — кодировка по умолчанию.
                                                                    У меня — нет, на гитхабе я тоже встречал в основном ascii.

                                                                    Полагаться на кодировку исходников — чистой воды непереносимость.
                                                                      +3

                                                                      ASCII является валидным UTF-8, если что.

                                                                        0
                                                                        Ваш пример не может быть в ASCII по той простой причине, что в нём есть кириллица. Которой в 7-битной ASCII не предусмотрено:)

                                                                        Он может быть в ASCII-совместимой кодировке — например, в UTF-8 (весьма вероятно, если вы под Linux-ом) или Windows-1251 (если вы под виндой).
                                                                0
                                                                Собственно, поскольку грубую ошибку вы не заметили, а придираетесь к пуговицам, возвращаю вам «что вы пока не умеете „на глаз“ отличать „безобидные“ диагностические сообщения от серьезных ошибок, я бы посоветовал вам» =)

                                                                Поведение там определено языком С, хотя и непереносимо (только для второго ворнинга).

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

                                                                  Нет.


                                                                  Программа некорректна — содержит constraint violations, т.е. синтаксические или семантические ошибки. Такая программа именно некомпилируема (!) как программа на языке С.


                                                                  Компиляторам разрешается компилировать такой код с той оговоркой, что поведение такой программы языком С не определено, ибо программой на С такой код не является.


                                                                  И для этого достаточно только main(), не говоря уже о int *p = 123;. Дальше main() уже можно не смотреть.


                                                                  И не надо пытаться вертеть хвостом с вашим выходом за пределы массива. Он тут не имеет никакого значения. Кстати, выход за пределы массива не является constraint violation, т.е. компиляторы имеют полное право компилировать ваш выход за пределы даже и не пискнув. Диагностики времени компиляции тут не требуется.

                                                                    0
                                                                    Also, in C89, functions returning int may be implicitly declared by the function call operator and function parameters of type int do not have to be declared when using old-style function definitions. (until C99)

                                                                    Это из http://en.cppreference.com/w/c/language/declarations#Declarators

                                                                    А про создание указателя из int, я не вижу смысла обсуждать. Конечно это UB, но иногда при работе с железом так и нужно делать. Не запрещено.
                                                                      0

                                                                      Язык С — это язык, описываемый действующей на данный момент спецификацией языка. Язык С описывается спецификацией ISO/IEC 9899:2011, известной в популярной культуре, как С11.


                                                                      Спецификация С89/90 описывает язык С89/90, а не язык С. Точно также как книга K&R С, описывает язык K&R С, а не язык С.


                                                                      Создание указателя путем попытки неявного преобразования из int — это с точки зрения языка С не UB, а некомпилируемый код. При работе же с железом, когда это нужно, делается так: int *p = (int *) 123;, но ни в коем случае не int *p = 123;. Язык безусловно требует явного каста для выполнения такого преобразования.

                                                                        +2
                                                                        Вот уж нет, приведение указателей на разные типы и конверсия из int это как раз даже не UB, я преувеличил — implementation defined, так написано в 6.3.2.3 пункт 5 стандарта.
                                                                        5 An integer may be converted to any pointer type. Except as previously specified, the
                                                                        result is implementation-defined, might not be correctly aligned, might not point to an
                                                                        entity of the referenced type, and might be a trap representation.
                                                                        56)

                                                                        И следуя вашей логике, программы до С11, не являются программами на С.

                                                                        Вот это уже называется вертеть хвостом, пытаясь апеллировать к последнему стандарту =)

                                                                        Собственно, статья права — никто не знает С.
                                                                          –1

                                                                          Осспади… 6.3.2.3… Каждый пионер проходит один и тот же путь, когда пытается читать стандарт С… (Признайтесь, вы это нагуглили?) Наивное заблуждение о том, что в 6.3.2.3 каким-то образом разрешается неявное преобразование целых чисел в указатели, упорно живет в этих ваших интернетах и отказывается умирать, несмотря на все усилия С-сообщества.


                                                                          Раздел 6.3.2.3, читающий стандарт вы наш, посвящен только описанию семантики и результатов конверсий между указателями и целыми. Он не говорит ни слова о том, когда эти конверсии применяются. В частности, он не говорит ни слова о том, что эти конверсии могут применяться неявно. Раздел 6.3.2.3 к этому никакого отношения не имеет вообще.


                                                                          Цитата, которую вы привели, объясняет, что будет происходить при вычислении выражения (int *) 123. Цитата, которую вы привели, никаким боком не говорит о том, что в int *p = 123; вдруг магическим образом произойдет неявная конверсия типа int в тип int *. Ничего подобного в вашей цитате не сказано.


                                                                          А в начале раздела 6.3 ясно сказано


                                                                          6.3 Conversions
                                                                          1 Several operators convert operand values from one type to another automatically. This subclause specifies the result required from such an implicit conversion, as well as those that result from a cast operation (an explicit conversion). The list in 6.3.1.8 summarizes the conversions performed by most ordinary operators; it is supplemented as required by the discussion of each operator in 6.5.

                                                                          Более того, в 6.5.4 Cast operators специально добавлено


                                                                          3 Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast.

                                                                          А дальше вам прямая дорога в описание семантики инициализации, которая в свою очередь отошлет вас к описанию семантику оператора присваивания (а это тот самый 6.5.16 Assignment operators), где ни в коем случае не допускается комбинации из указателя и целого числа в присваивании, за исключением случая, когда справа стоит null pointer constant.


                                                                          Собственно, статья права — никто не знает С.

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

                                                                            –1
                                                                            Ах, дело лишь в том, что лично вы хотели бы там видеть явное приведение типа, а компиляторы вас не слушают?!

                                                                            Ну извините, мир с розовыми понями за углом.
                                                                            Мне с практической точки зрения важна только принципиальная возможность такого приведения в языке, поскольку языки с запретом подобного поведения адски неудобны.

                                                                            И ругнется компилятор варнингом или нет — не суть важно, программа не станет от этого принципиально неверной.

                                                                            За сим откланиваюсь.
                                                                              –1
                                                                              Не слушают? Да что вы говорите? Опять девичьи мечты?

                                                                              В полном соответствии с моими требованиями (то есть требованиями спецификации языка), компиляторы четко и ясно указывают на ошибочный код при помощи диагностического сообщения. Это вы лично их не слушаете, тупо игнорируя эти диагностические сообщения :)

                                                                              И, я вижу, опять начались томные рассказы про то, что «вам нужно»… Я же, как я уже не раз говорил выше, веду речь именно и только о языке С, а не том, что мне или кому-то еще лично нужно.

                                                                              Ну что ж — и наше вам с кисточкой.
                                                              +1
                                                              В качестве доказательства приводится тот факт, что чей-то уютненький компилерчик выдает на это «всего лишь warning».

                                                              Понятно, что к реальности такие заявления никакого отношения не имеют.

                                                              Как минимум, gcc (не такой уж и уютненький, вполне себе серьёзный, по строгой поддержке стандарта в том числе) по умолчанию ведёт себя именно так. Можно и const сбрасывать, и вообще совершенно разные объектные типы неявно приводить друг к другу — будет только предупреждение, а при компиляции C++-программ — ошибка.

                                                              Diagnostic message есть — формально требование Стандарта со стороны компилятора соблюдено.
                                                                –1

                                                                С тем что формально требование стандарта соблюдено никто не спорит. В данной подветке речь идет не об этом, а о том о том, что делать выводы о корректности программы на основе того, как некий компилятор делит свои диагностические сообщения на errors и warnings — занятие совершенно бессмысленное. Тем не менее некоторые индивидуумы этого не понимают.


                                                                Это деление в случае GCC — не более чем "уютненькая" фантазия авторов компилятора, обилием каковых фантазий компилятор GCC широко знаменит. И подрастающее студенчество, формальных документов не читавшее, из-за этой "уютности" страдает заметно, как показывают в том числе и комментарии в этой ветке.


                                                                Когда-то нам стоило огромных усилий пробить разработку в направлении "педантичного" деления на ошибки и предупреждения в GCC (пресловутый -pedantic-errors), Почему это направление разработки вызвало такое сопротивление в GNU — мне до сих пор не ясно.

                                                      +1
                                                      Авторы С подтверждают — язык С был придуман для того, чтобы Ричи и Кернигану не надо было напрягаться программированием на ассемблере. Цитировать не буду, но этот момент обозначен в их книге.
                                                        0

                                                        Надо заметить, что Брайан Керниган не имеет никакого отношения к разработке языка С вообще, как он сам не раз заявлял. Как активный участник разработки языка В (предшественника С) он участвовал в подготовке книги, но не более того.

                                                          0
                                                          Это во-первых. А во-вторых, на тот язык, который Денис Ритчи разработал «для замены ассемблера», можно взглянуть в известном документе самого Дениса Ритчи: «C Reference Manual» (CRM)

                                                          http://www.math.utah.edu/computing/compilers/c/Ritchie-CReferenceManual.pdf

                                                          В этом языке действительно чувствуется сильное влияние ассемблера. Однако все это было выкинуто из языка очень быстро: CRM C очень сильно отличается от K&R C, не говоря уже о ANSI C, не говоря уже о современном С.
                                                        –4
                                                        Обеспечение переносимости при разработке с C++ — отдельная головная боль.
                                                        То есть вы сосредоточены не только на решении задачи, вам нужно еще и с компилятором побороться.

                                                        С++ не так просто скомпилировать на разных версиях операционной системы даже при использовании одного и того же компилятора (не говоря уже про разные компиляторы и разные платформы и разные стандарты С++).

                                                        Например,
                                                        https://github.com/cocaine/cocaine-core
                                                        Это одна из базовых технологий Яндекса, сделанная не самыми глупыми программистами.
                                                          +6
                                                          Я разрабатываю кросс-платформенные приложения под Винду, Линукс, Андрои, иОС и Мак. Ваше заявление, мягко говоря, не соответствует действительности. Яндекс где-то накосячил.
                                                            –2
                                                            Это можно — кто ж спорит.

                                                            Но это требует серьезных ДОПОЛНИТЕЛЬНЫХ затрат или просто СПЕЦИАЛЬНЫХ знаний.

                                                            Я утверждаю, что львинная часть Сипласпласовцев таковыми не очень то и стремится овладеть сосредоточившись на своей узкой задаче.

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

                                                            Чтобы было понятно о чем речь и что проблема решаема в принципе — сравните, сколь небольшими усилиями делается переносимая программа на Python или Go.

                                                            С С++ не сравнить — это просто небо и земля.

                                                            В Python и Go если разработчику и нужно с чем то бороться при этом — то с отдельными аспектами не POSIX API (Windows). Что как раз ожидаемо и логично.

                                                            С С++ дополнительно приходится бороться с различиями в компиляторах и библиотеках — даже в пределах ОДНОЙ платформы.

                                                            P.S.:
                                                            Яндекс приведен как контора, которая может позволить себе разработчиков уровня выше среднего. И если даже они косячат со своими базовыми вещами (эта штука, что я привел — занимается запуском и мониторингом контейнеров с другим ПО в кластере Яндекса) — то что же ожидать от основной массы?

                                                            Не должен быть язык таким. Это плохое свойство. Существенный недостаток. Никак он не может претендовать на системный, базовый.
                                                              +5
                                                              Я не помню, когда последний раз за 6 лет кросс-платформенной разработки «боролся с различиями в компиляторах и библиотеках». ЧЯДНТ?
                                                              Но я уделяю внимание использованию стандартного С++ и всячески избегаю любых платформозависимых расширений. "-pedantic-errors", /W4, периодический статический анализ и всё такое.
                                                                +2
                                                                Возьмите и откопилируйте программу по ссылке выше.

                                                                Да, там кое-где код написано не универсально. Но дело не только в этом.

                                                                Дело не только в СТАНДАРТНОМ С++. Там еще куча ЗАВИСИМОСТЕЙ.

                                                                Все от вас зависит только в ПРОСТЕЙШЕМ ПО, которое вы пишете сами.

                                                                Как только начинаете использовать внешние вещи (библиотеки) — количество сюрпризов увеличивается.

                                                                Вплоть до багов в такой казалось бы базово-универсальной вещи как Boost, не говоря уже о более специфичных библиотеках…
                                                                  +3
                                                                  Бибилиотек у меня около 15, от exiv2 до libusb и libftdi. Естественно, используем только кросс-платформенные библиотеки.
                                                                  Буст не использую, он слишком большой и сложный в интеграции.
                                                                    0
                                                                    Алогичность детектед:

                                                                    Средний программист С++ (коих подавляющее большинство) понятия не имеет как писать 100% переносимый код.

                                                                    Вы же приводите в пример просто «историю успеха».

                                                                    Никто не спорит, что при желании на С++ можно писать переносимо.

                                                                    Совсем другой вопрос — а какими это достигается усилиями, по сравнению с более естественными в этом отношении системами программирования. Ну например, Python и Go и Java…

                                                                      +4
                                                                      Я работаю в маленькой фирме и ничего не знаю про среднего программиста. Я знаю, что пришёл работать после ВУЗа с нулевым опытом, методом тыка стал разбираться с портированием уже имевшегося когда на другие платформы, и быстро научился любить и уважать стандарт С++. А потом почти так же быстро научил любить стандарт сотрудников и начальника. В том числе и с помощью -pedantic-errors.
                                                                      Как только начинаешь писать под больше, чем один компилятор и/или одну платформу — очень быстро дисциплинируешься и всё встаёт на свои места.

                                                                      А ваши слова про подавляющее большинство — пустой звук, не подкреплённый никакими доказательствами.

                                                                      На Go и Питоне уже можно писать GUI, или по-прежнему как всегда? Питон вообще не рассматриваю как что-то большее, чем средство написания скриптов для автоматизации каких-то относительно небольших процессов. Java в целом хороша, но жирный рантайм, кривой UI и производительность «как повезёт». Это моё ИМХО, your mileage may vary.
                                                                        –1
                                                                        Вы вновь повторяете алогичность.

                                                                        Ваш частный случай — это всего лишь ваш частный случай.

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

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

                                                                        Я уже привел пример Яндекса, который может себе позволить нанять специалиста заметно выше среднего (и дело не только в деньгах, они могут позволить себе нанять специалиста даже потратив меньше, потому как все прекрасно понимают, что после Яндекса — все пути тебе открыты).

                                                                        И тем не менее — видим огромные косяки. Там даже не так просто скомпилировать под Ubuntu другой версии…

                                                                        А ведь это не какой то локальный продукт — их Cocaine базис облака Яндекс и распространяется свободно для любого желающего.

                                                                        Для того, чтобы убедиться, что большинство С++ создают продукты такого же качества — достаточно побольше взять открытых проектов и попытаться их скомпилировать (даже не запустить!!! ) на различных системах.

                                                                        Если я скажу что я делал это — и все очень часто плохо — это же не будет для вас доказательством? Вы можете это понять только самостоятельно.

                                                                        Проблема с GUI решаема.
                                                                        Совсем не обязательно создавать монолит. GUI (если у вас не нечто требовательное до 3D, к примеру) — вполне безболезненно может существовать отдельно от вычислительной части.

                                                                        И, очень интересно, а что вы называете кросс-платформенностью своей системы? Где именно возможна ее работа, на каких ОС?
                                                                          +3
                                                                          1. Факты таковы, что вы бросаетесь общими незначащими фразами, не подкреплёнными доказательствами, выдавая их за факты. Пример: «большая часть людей… не стремятся профессионально расти».
                                                                          2. Вы аппелируете к авторитету Яндекса, но для меня это не авторитет.
                                                                          3. В репозитории есть README. Там принято писать список поддерживаемых платформ и компиляторов. Ожидать беспроблемной сборки на других системах не стоит. Есть спецификация, там написано, что система гарантирует. Всё остальное вам никто не обещал, так чего вы ожидаете? Вы, наверное, из той категории людей, которые ставят приложению в Google Play негативные отзывы за то, что приложение не умеет ещё и кофе варить.

                                                                          О чём мы вообще спорим? На С++ можно писать непортируемый код? Да, можно. И на Java можно. Банально, вызвали WinAPI функцию — всё, ваш код стал непортируемым. На С++ вызывать сторонние компоненты проще, и для меня это преимущество, а не недостаток. Правильное использование фич всегда на совести програмиста, а не языка.
                                                                          Зато писать портируемый код на современном С++ намного проще, чем на С (кроме самых embedded платформ, наверное).
                                                                          4. GUI на основе другой технологии — лишняя головная боль. Всякие прослойки-адаптеры., через которые надо правильно протащить каждый чих.
                                                                          5. Я уже говорил, что почти все мои программы работают на Linux, Windows и OS X, а некоторые из них также на Android и iOS.
                                                                            –2
                                                                            Доказательством может быть только опыт.
                                                                            Лично вы работали в окружении опытных спецов — вам очень повезло.
                                                                            Нет, не так. ВАМ ОЧЕНЬ СИЛЬНО ПОВЕЗЛО, ЕСЛИ ВОКРУГ ВАС ОДНИ ПРОФИ.
                                                                            В реальной жизни это большая и большая редкость.
                                                                            В среднем человек любой квалификации довольно посредственнен как профессионал.
                                                                            То, что вы еще не сталкивались с огромным числом не профи — говорит о вашем малом жизненном опыте.
                                                                            И только.
                                                                              +1
                                                                              Если бы вы внимательно прочитали мой комментарий на один уровень выше, вы бы увидели, что я работал в окружении обычных хороших програмистов, которые тоже понаписывали непортируемого кода, но жизнь, не без моей помощи, наставила их на путь истинный.
                                                                                –1
                                                                                > что я работал в окружении обычных хороших програмистов, которые тоже понаписывали непортируемого кода

                                                                                Тем самым вы и подтвердили мое утверждение.

                                                                                > но жизнь, не без моей помощи, наставила их на путь истинный.

                                                                                Разумеется, кто же будет делать лишнюю работу в здравом уме и твердой памяти?

                                                                                С C++ этой работы слишком много, вот в чем дело.
                                                                                  +1
                                                                                  >Тем самым вы и подтвердили мое утверждение.
                                                                                  Какое? Что можно написать непортируемый код? Так я ещё два комментария назад сказал, что это так, но это не аргумент.

                                                                                  >C C++ этой работы слишком много, вот в чем дело.
                                                                                  Я так не считаю. Единственная серьёзная сложность была — избавиться от анонимных union.
                                                                                    –1
                                                                                    > Я так не считаю. Единственная серьёзная сложность была — избавиться от анонимных union.

                                                                                    В вашем собственном коде.

                                                                                    А как же про сторонние библиотеки? Повезло?

                                                                                    Во многих других языках данная проблема просто и не может возникнуть. В других языках ты как правило борешься всего то с особенностями программной платформы (например Windows vs Unix).

                                                                                    В случае C++ — плюс еще и с самим языком.
                                                                                    Это не дело.

                                                                                    Не должно от «анонимных union» ничего зависеть. Это бага дизайна языка
                                                                                      0
                                                                                      Нет. Это бага компилятора, что он компилит нестандартный код.
                                                                                      Библиотеки изначально брались только кросс-платформенные. Никакого везения, холодный расчёт.
                                                                                        –2
                                                                                        В мире С++ компиляторы, частично поддерживающих тот или иной стандарт или поддерживающих нестандарт — никого не удивляют.

                                                                                        С другими языками таких разночтений — огромная редкость.

                                                                                        Как можно на столь зыбком фундаменте жить.
                                                                                        Это не совершенно непроизводственные затраты.

                                                                                        Вместо того, чтобы сосредоточиться на задаче — ты тратить часть своего времени на борьбу со своим инструментом.
                                                                            0
                                                                            Это не алогичность. Человек доказывает возможность применения языка в этой области, и это вполне можно делать своим примером.

                                                                            А что до качества среднего программиста — что мне до этого, если я про свой уровень всё знаю и могу позволить себе выбирать место работы так, чтобы коллеги тоже были выше среднего?
                                                                              –2
                                                                              Никто не спорит что С++ можно применять.

                                                                              Мы обсуждаем статью — у С++ проблем не меньше, чем у С. Он не самый предсказуемый язык, не самый лучший для переносимости.

                                                                              Требующей дополнительной квалификации и внимания для написания действительно переносимых программ.

                                                                              По сравнению с Go, Java, Python…
                                                                          +1
                                                                          там, где надо писать на с/с++, уже не подойдут питон, го и джава
                                                            +1
                                                            На C++?
                                                            Правда там ответ на эти вопросы будет таким же.
                                                            –3
                                                            За такие комментарии надо с HABRы выпиливать.
                                                              +3

                                                              Почему нет? Законы эволюции вполне действуют и на языки программирования. Паскаль почти вымер. Кобол почти вымер. А Си — слишком хорош, чтобы его выкинуть.

                                                                +1
                                                                Си — слишком укоренился, чтобы его выкинуть.
                                                                А не слишком хорош.

                                                                Как минимум без Си исчезают все распространенные операционные системы, приличная доля компиляторов/интерпретаторов, туева хуча утилит и базовых библиотек без которых не сможет жить или создаваться софт более высокого уровня и пр. и пр.
                                                                  +2
                                                                  Чем си не хорош-то? Хоть сам на него редко сажаюсь, но его лаконичность и простота каждый раз действуют на мозг очень приятно после какой-нибудь джавки. Наверное, лучше него с KISS ничто не сочетается, но это сугубое ИМХО
                                                                    +2
                                                                    А огромного количества UB вам мало?
                                                                      –1
                                                                      UB легко избегать. Особенно, если знаешь, что пишешь код под определенную архитектуру с определенной ОС, тогда его будет не больше, чем в любом другом языке
                                                                        +4
                                                                        Си — это такой над-ассемблер. Таким его задумали, так надо было. И до тех пор пока на Си писали ядра операционных систем да драйвера — эти самые UB можно пережить.

                                                                        Но когда речь идет о прикладном ПО, то планка совсем иная.

                                                                        UB в Си побольше, чем в «любом языке», используемом для написания прикладного ПО, если в качестве любого рассматривать Python, Java, Go.

                                                                        Если под «любым языком» вы понимаете C++ — тогда конечно, да, я с вами соглашусь.
                                                                          –1
                                                                          Как я уже писал, это исключительно мое мнение, но большая часть UB, которое встречал — это работа с многопоточностью и асинхронностью. И в этом Си не сильно хуже других языков, иногда даже лучше

                                                                          Плюсы для меня темный перегруженный лес, так сложилось исторически, поэтому сравнивать с ними не хочу и не берусь)
                                                                            0

                                                                            Ну я бы не сказал, что в Python, Ruby, Perl намного меньше undefined behaviors. Все они основаны на Си-рантайме и также в основном исповедуют типа без привязки к разрядности архитектуры. Хотя есть и типы более жестко определенные, но тем не менее. И это только один из аспектов.


                                                                            Например, большая часть языков основана сейчас на LLVM, GCC или JVM, и вынуждены следовать их UB.

                                                                              0
                                                                              есть же uint16_t и прочие
                                                                                0

                                                                                В языке их нет. Это просто определения в библиотеке, описываемые под известный компилятор и известные платформы.


                                                                                Если компилятор, например, не определит макросы, по которым stdlib догадается о размерности базовых типов, то "всё пропало". А он, по стандарту, и не обязан это делать.

                                                                                  0
                                                                                  Вроде как stdint.h входит в C99 https://ru.wikipedia.org/wiki/Stdint.h. Соответственно можно проверить, есть ли uint16_t на данной платформе, если нет, то uint_least16_t обязан быть по стандарту всегда.
                                                                                    0

                                                                                    uint_least16_t имеет совсем иное (не столь строго определённое) поведение. в частности, при битовых сдвигах.

                                                                                      +1
                                                                                      Ну выше было что в стандарте нет, тем не менее в стандарте он все же есть, его может не быть на платформе и стандарт это предусматривает. Можно написать корректный код без UB, который работает и в случае наличия точного типа и когда его нет. Можно или 2 варианта кода или универсальный, который закладывается только на least. Так же в случае использования точного типа и его отсутствия — код просто не соберется. Так что все же имхо Antervis прав.
                                                                                      +1
                                                                                      Добавлю ещё, что для стандартных типов тоже есть определённые гарантии (если уж мы вынуждены писать на с89), следующие из описания хедера <limits.h>:
                                                                                      • char, signed char, unsigned char — не меньше 8 бит (в точности — CHAR_BIT бит), нет битов-заполнителей;
                                                                                      • short, unsigned short — не меньше 16 значащих бит;
                                                                                      • int, unsigned int — не меньше 16 значащих бит;
                                                                                      • long, unsigned long — не меньше 32 значащих бит;
                                                                                      • long long, unsigned long long — не меньше 64 значащих бит (но эти типы появились лишь в C99).
                                                                          +2
                                                                          Лаконичность и простота — это вам в хаскель, а не си.
                                                                            0
                                                                            Главная проблема Си для меня — отсутствие классов = нет простого и понятного механизма управления сложностью проекта через абстракции.
                                                                              0
                                                                              Я привык заменять классы отдельными для структур .h файлами, состоящими из, собственно, структуры и функций. Расширять их можно уже в .c файлах, импортирующих соответсвующие заголовочные, получая таким образом что-то вроде наследования и контрактов. По удобству это совсем не то, конечно, но уже приятнее
                                                                                0
                                                                                Логично. Лучше, чем ничего.
                                                                        +3
                                                                        Зря вас минусуют. Я разрабатываю на С и С++ уже около 15 лет, начиная от микроконтроллеров и заканчивая сложными распределенными системами — и могу сказать — да, жаль.
                                                                        И нет, я не предлагаю перейти всем на javascript:) Но необходимость создания универсального языка программирования для замены С и С++ назрела уже давно. А не происходит это из-за инертности мышления и так называемого груза унаследованного кода, который якобы никто не хочет переписывать. Господа, переписать его не такая уж и проблема, было бы на что. Программисты так или иначе постоянно переписывают код, адаптируют его, переписали бы и на единую стандартизированную модификацию Си, заодно кучу древних багов выловили бы. Ах да, бизнес же не хочет тратить лишнее время и платить лишних денег за непонятную работу… им же надо чтобы тяп ляп и в продакшен. Ничего, не обеднели бы.

                                                                        Если не углубляться в тонкости, сам язык Си вполне хорош за исключением некоторых мелочей. В нем есть некоторые странные соглашения, которые не мешало бы отменить ради универсальности (имя массива это указатель на первый элемент массива — в результате массивы это не first class objects) и некоторые древние и опасные наросты (препроцессор, #define true false, инклуды и т.п.). В нем нет многих современных фич — что плохо. И в нем есть неопределенное поведение (примеры которого показаны в статье) — что очень плохо. Не так уж и сложно жестко прописать правила выравнивания структур или размеры базовых типов. Но нет — с каких-то там древних времен, когда о стандартизации никто не задумывался и писали компиляторы на коленке для себя, получилось что под какую-то архитектуру размеры базовых типов оказались нестандартные. И может сейчас и архитектуры-то этой нет, но тягомотина с undefined behaviour продолжает тянуться ради дурацкой совместимости.
                                                                          +7
                                                                          Позвольте мне только один нескромный вопрос: а возможно ли в принципе свести воедино все зоопарки устройств, под которые писали и пишут на си, в один единый строгий стандарт?

                                                                          Все дырки из поста, они же являются не прихотью, а компромиссом. Эти дырки — не слабоумие, а чёткое понимание, что в одних устройствах есть возможность контролировать условия работы и порядок выполнения, а в других — нет. Что в одних устройствах дорого выделить маленький кусок памяти, а в других вообще все типы одного 8-битного размера, а процессор не может в умножение. Даже, казалось бы, явные прорехи, вроде неопределённости порядка выполнения операндов — компромисс по отношению к параллельным системам, в которых вычисление левой и правой части происходило бы одновременно в автономных контекстах с результатом «1». Действительно, переменная же не помечена разделяемой, почему нет, openMP бы так и сделал (если бы мы умудрились распараллелить эту строку).

                                                                          Единственный вариант отказаться от рыхлого стандарта — заставить каждого производителя железа выпускать свою надстройку над шаблонным языком. А это приведёт к той самой проблеме сужения числа специалистов, их удорожания и падения качества производимого кода.

                                                                          Единственное бесспорное место — макросы, заменить бы их более удобоваримым метаинструментом.
                                                                            0
                                                                            Возможно.
                                                                            Если все типы одного 8-битного размера, то это не значит что нельзя сделать 16-битные типы из них. Просто программист будет знать, что 16-битные типы не нативные. Подобная ситуация есть с FPU: на некоторых микроконтроллерах FPU отсутствует, но никто не мешает объявить тип float или double. Другое дело что работать это будет медленно и нередко криво.
                                                                            Более того, можно ввести понятие модульных возможностей языка. И подключать или отключать различные языковые модули в свойствах проекта (а к таковым я бы отнес floating point, длинную арифметику (GMP), исключения (кстати различных видов — SEH, DWARF, SJLJ и возможно какие-то еще), RTTI, различную рефлексию, динамическое выделение памяти, сборку мусора, многопоточность и т.д.). Если модуль используется в коде, но отключен в проекте — ошибка компиляции; или подключайте модуль, или переписывайте код так чтобы данные языковые возможности не использовались, либо пишите свой модуль (что тоже потенциально возможно).
                                                                              +1
                                                                              Вы же понимаете, что вы не избавляетесь от проблемы, а перекладываете её в другой карман. Я не спорю, в стандарте явно не хватает встроенных инструментов получения некоторых гарантий, хоть он и сделал не так давно приятный шаг вперёд, как с теми же типами.
                                                                              Но, позвольте, разве модульность не устанавливается на уровне компилятора? Тот же AVR GCC проглатывает далеко не весь код, хотя бы по причинам организационным, 32 килобайта, все дела. Все ваши модули уже спрятаны в нём. в компиляторе, и иначе быть не может, а доступ к нестандартным методам всё равно получается после подключения платформозависимых заголовочников, к которым есть ассемблерный код. То есть вы ни капли не добавите ничего нового.

                                                                              То, что вы предлагаете, оно должно продумываться на этапе разработки библиотеки. Конечно, есть проблема сужающихся возможностей, когда наши амбиции придавливает sizeof(int), но ведь есть ровно обратные ситуации. Это вопрос говнокода, не языка. Но если мы строим библиотеку, способную работать на разных архитектурах, то нам над ними придётся воспарить в некоторой, возможно. параметризуемой, абстракции. А иначе как?
                                                                                +1
                                                                                Все верно, параметризируемая абстракция должна быть.
                                                                                В простейшем случае если нам нужен просто int и все равно сколько там байт, так и пишем int. А если нужно что-то конкретное (для сетевого пакета или двоичного файла) то пишем int8 или int16 или int32.
                                                                                Но параметризация должна быть явная, как и модульность. Вообще все должно быть явное, стандартизированное и общее для всех компиляторов.
                                                                                  0
                                                                                  Мне очень интересно посмотреть на возможный синтаксис и, пожалуй, способ получения\расширения модулей. Собственно, механизм работы, как бы это могло быть. Вам не составит труда?
                                                                                –1
                                                                                За 40 лет проблему не решили. Хотя ей занимались люди поумнее и вас и меня.

                                                                                Уже не решится проблема. Очевидно.
                                                                                +1
                                                                                Легко.
                                                                                Компилятор, зависимый от платформы, который не должен пропускать подозрительные вещи.

                                                                                В Си же можно накосячить «втихую».
                                                                                  –1
                                                                                  Во-первых, модифицированные компиляторы и так не пропускают подозрительные вещи.
                                                                                  Во-вторых, а что считать подозрительным? Подозрительно что-то может быть с точки зрения платформы, конкретного компилятора, но мы же определяем независимый от предрассудков язык…

                                                                                  Да даже порядок вычисления параметров метода, его неопределённость подозрительна, только если мы вычисляем их один за другим. А если все сразу параллельно, считая глобальные переменные разделяемыми между контекстами, а локальные — в зависимости от погоды на Марсе?

                                                                                  Конечно, случайно взятая библиотека в таких условиях не соберётся сразу без проблем. Но модификация библиотеки по отметкам компилятора/анализатора/отладчика «здесь может быть гонка, здесь буду синхронизировать» — куда проще, чем писать тот же SQLite с нуля только потому, что наше подмножество языка ну очень уж далеко уехало от их подмножества.
                                                                                    +2
                                                                                    Си был создан для замены ассемблера при создании операционной системы.

                                                                                    Сейчас же его применяют и для прикладного ПО, где спускаться на столь низкий уровень нет необходимости (тем более сегодняшние компиляторы очень хорошо оптимизируют).

                                                                                    Но Си все так же остается всё таким же «ассемблерно-близким», хотя для большей части продуктов, создаваемых с его помощью — таких вольностей не требуется, они только увеличивают риски косяков…

                                                                                    Зачем мы пишем прикладное ПО на «почти ассемблере» до сих пор?
                                                                                      0
                                                                                      Вы пишите? Если да, то вопрос риторический. Если нет, то, позвольте, в чей огород камень? В огород библиотечников? Но те же libusb и прочие — системное ПО, да и SQLite не особо выше уровнем, 90% сорцов — попытка натянуть сову производительности на глобус универсальности. И, посмотрите, очень даже неплохо получается. А статья — она именно о железячнике, который писал то самое системное ПО с его системными заморочками реального времени.

                                                                                      Или вы из «новой волны», которая пишет под Arduino на JavaScript?
                                                                                –1
                                                                                Если Вам не нравятся существующие языки — пожалуйста, создавайте свой язык. Затем создайте компиляторы языка для всех более-менее распространённых платформ; ну или сделайте язык настолько хорошим, что другие программисты заходят создать компиляторы. Не забудьте добиться высокой эффективности программ на этом языке на любой платформе. И затем убедите программистов использовать Ваш язык.
                                                                                Как видите — совершенно ничего сложного; раз-два, и готово.

                                                                                Создатели и последующие развиватели C пошли немного совсем другим путём: они знали, что есть процессоры разной архитектуры, иногда — совершенно дикой с т.з. людей, привыкших к i*86 и ARM. Поэтому в C было определено поведение программ в определённых случаях. И было чётко сказано, что за пределами этой области поведение программы м.б. всяким-разным.

                                                                                Например, случай:
                                                                                x = 1 << y;
                                                                                правильно работает при y>=0. А при отрицательных y поведение не определено. Если Вы хотите, чтобы для отрицательных y выполнялось
                                                                                x = 1 >> (-y);
                                                                                то никто не мешает Вам написать проверку y на знак и выбрать соответствующее действие.

                                                                                Более того: прямо на этапе компиляции можно проверить, как данная архитектура (процессор и компилятор) ведёт себя для данного случая. И можно отказаться компилироваться на «плохой» архитектуре. Или для «плохой» архитектуры можно сделать менее эффективный, зато точно работающий код.
                                                                              +4
                                                                              в 4 вопросе не хватает ответа «42», так как блока с вопросом там нет, видимо, это вопрос жизни, вселенной и всего такого…
                                                                                +1
                                                                                Чукча не читатель, чукча писатель
                                                                                0
                                                                                Вопрос 4 порадовал:
                                                                                4

                                                                                А. 0
                                                                                В. 1
                                                                                С. 16
                                                                                D. Я не знаю
                                                                                  +1
                                                                                  А я на него правильно ответил =)
                                                                                    0
                                                                                    Он видимо для этого и задумывался. Чтобы счет был хотя бы 1 из 5.
                                                                                    –1
                                                                                    Откуда операция сравнения даст 4 ???
                                                                                    Все там верно
                                                                                    +8
                                                                                    На мой взгляд, для некоторых вопросов возможны более точные ответы:

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

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

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

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

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

                                                                                      надеялся на что-то более серьезное, а не про сферических коней в вакууме.
                                                                                        +4
                                                                                        Так это не понурые «нуу я не знаю...», а уверенные — «я не знаю, потому что».
                                                                                          0

                                                                                          Или такое же уверенное — на платформе под которую я пишу, с моими настройками на моем компиляторе в первом вопросе ответ 8, во втором 0, в последнем 2 и т.д.

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

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


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


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

                                                                                            0
                                                                                            Я бы не взял программиста который такие трюки будет использовать в реальном коде.
                                                                                              –3
                                                                                              Видимо поэтому вам никогда не доведется принимать такие решения.

                                                                                              Это не трюки, а фундаментальные свойства С, как платформы. Это атомы, из которых состоит С. Абсолютно любая программа на языке С опирается на эти «трюки».
                                                                                                +3
                                                                                                Нет. Ни одна программа не должна опираться на неопределенное поведение
                                                                                                  0
                                                                                                  В моем комментарии нет ни слова о том, что программы «должны опираться на неопределенное поведение».
                                                                                                    –1
                                                                                                    все эти трюки являются примерами неопределенного поведения. Или, как минимум, неоднозначно/контекстуально определенного/платформозависимого поведения.
                                                                                                      0

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


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

                                                                                                        +1
                                                                                                        вы написали:
                                                                                                        Абсолютно любая программа на языке С опирается на эти «трюки».

                                                                                                        Часть «трюков» — примеры неопределенного поведения, программы без ошибок его не содержат. Остальная часть — неоднозначности, закладываться на которые в общем случае нельзя.
                                                                                              +3
                                                                                              Есть мнение, что при написании критически важных вещей первичным будет знание не стандарта, а целевых компилятора и железа, ибо разработчики оных могли забить на стандарт.
                                                                                                0
                                                                                                Ну, много проблем не с тем, что разработчики забивают на стандарт, а с тем, что они реализуют по-разному то, что не регламентируется жестко стандартом.
                                                                                                Если использовать только то, что описывает стандарт без всяких(пусть и общеупотребительных) трюков, то вероятность что-то сломать гораздо меньше, а если и сломается — всегда можно попенять разработчикам компилятора, что они неправильно реализовывают стандарт.
                                                                                                  +1
                                                                                                  Стандарт Си в области целочисленной арифметики — это большая боль. Он слишком поздно появился для такого важного языка.
                                                                                                    0
                                                                                                    В этом направлении боли не видел я, кроме тривиального 3/2.

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

                                                                                                    Там не всегда «неопределённое поведение». «Неопределённое поведение» там только в последнем вопросе и, возможно, в четвёртом (зависит от размера int), в остальных «определяемое реализацией» (implementation-defined).

                                                                                                      0
                                                                                                      это не меняет сути моей притензии
                                                                                                    –6
                                                                                                    Где ответы то?
                                                                                                      +1
                                                                                                      Ctrl+F «Итак верные ответы»
                                                                                                        +11
                                                                                                        я не знаю
                                                                                                        –4
                                                                                                        В последнем случае на самом деле всё понятно
                                                                                                        Рассмотрим два пути:

                                                                                                        1. правый операнд при сложении вычисляется первым
                                                                                                        Тогда наше выражение пройдёт следующий путь
                                                                                                        > i = 0;
                                                                                                        > 0++ + ++0 -> 1++ + 1 -> 2 (i = 2)

                                                                                                        2. левый операнд вычисляется первым
                                                                                                        > i = 0;
                                                                                                        > 0++ + ++0 -> 0 + ++1 -> 2 (i = 2)

                                                                                                        и результат равен 2, и значение i равно 2
                                                                                                          +3
                                                                                                          В зависимости от компилятора (хотя могут все и вычислять одинаково, но не обязаны). Это неопределенное поведение по стандарту «Если программа пытается модифицировать одну переменную дважды не пересекая точку следования, то это ведет к undefined behavior. Так говорит Стандарт.» http://alenacpp.blogspot.com/2005/11/sequence-points.html
                                                                                                            0
                                                                                                            LynXzpSingerofthefall как это противоречит моему утверждению?
                                                                                                              0
                                                                                                              Это говорит о том что Ваше утверждение не обязательно верное, точнее результат выполнения программы такой, но только сегодня, и только в этой версии компилятора (может еще в других). Сегодня такой код выдает 2. А завтра благодаря side-effects оптимизаций компилятора он может выдать и 1 и 3 (а может еще что-то). Разработчики компиляторов не обязаны делать так чтобы результат получался 2. И если это будет выгодно в угоду другим оптимизациям они изменят логику и результат будет другой.
                                                                                                              1
                                                                                                              (i++) + (++i) =(компилятор параллельного мультискаляного процессора считает что можно вычислить результат скобок независимо, а потом сложить) = 0 + 1 = 1
                                                                                                              Как получить три, не придумал, но в более сложных и запутанных ситуациях может получится и больше разных ответов. Причем они все не будут багами, потому что UB.
                                                                                                              Еще процитирую ту же статью на которую дал ссылку:
                                                                                                              Вот пример такой баги: Bug13403. Их таких там очень много.

                                                                                                              В следующей программе
                                                                                                              #include int main(){
                                                                                                              int i=0;
                                                                                                              i = 6+ i++ + 2000;
                                                                                                              std::cout << i << std::endl;
                                                                                                              return 0;
                                                                                                              }

                                                                                                              Результат единица! А должен быть 2006.
                                                                                                              Если убрать «i++», то результат правильный («2006»). Но пока есть «i++» внутри выражения, я могу делить, умножать, вычитать, складывать, все равно результат всегда «1».
                                                                                                              Например в выражении
                                                                                                              i = (6+ i++ + 2000)/2;
                                                                                                              «i» все равно единица.

                                                                                                              Но если я заменю постфикс «i++» префиксом "++i" тогда все считается корректно.

                                                                                                              Ну отвечают на это все — «это undefined behavior по Стандарту» и баг закрывают.
                                                                                                                0
                                                                                                                Да, спасибо, уже понял)
                                                                                                            +1
                                                                                                            Это потому что автор неправильно объяснил причину. В C есть понятие точек следования. Если между выражениями A и B имеется точка следования, это гарантирует, что все side-effects, связанные с выражением A, уже произошли, но ни один side-effect, связанный с выражением B еще не произошел.

                                                                                                            Также в стандарте написано, что если у вас есть два side-effect'а, изменяющие один и тот же объект, и между ними нет точек следования, то у вас undefined behavior.

                                                                                                            Скрытый текст
                                                                                                            Строго говоря я немного упростил, дословно там написано так:

                                                                                                            If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

                                                                                                            В этом примере мы имеем два изменения результирующего объекта за одну точку следования (так как ни оператор +, ни префиксный/постфиксный операторы ++ точек следования в себе не содержат), следовательно имеем UB.
                                                                                                              +4
                                                                                                              А вот GCC 4.2 (если не ошибаюсь) с -03 выдавал 1. Объясняю на пальцах.

                                                                                                              Логика такая: компилятор принял i за константу внутри точки следования и оптимизировал левый инкремент до возврата нуля, а сам инкремент поставил в конец точки следования (и отбросил за ненадобностью, ведь дальше тела функции i не используется). После чего выполнил правый инкремент, сложение и вернул результат.
                                                                                                                +1
                                                                                                                Это не более чем частный случай проявления неопределенного поведения.
                                                                                                              +2
                                                                                                              Существуют такие расширения как GCC атрибуты aligned и packed которые позволяют вам получить некоторый контроль над этим процессом, но они не стандартизированные.

                                                                                                              Чё это они не стандартизированы? Уже лет 5 как.

                                                                                                              C11 ISO Standard, 6.7.5 Alignment specifier.
                                                                                                              April 2011.
                                                                                                                +2
                                                                                                                Все UB не от скудоумия авторов стандарта, а от стремления избежания лишних действий в рантайме.
                                                                                                                Хотя можно было бы и разработать несколько подмножеств стандарта и, соответственно, режимов компиляции. Например «легкий» — то что сейчас и «строгий» — рантайм из-за проверок утяжеляется, но UB минимизируются
                                                                                                                  –1
                                                                                                                  Ответил «я не знаю» на первые два вопроса — интуитивно отгадал правильный ответ на остальные три, даже не читая вопросов =) Итого 5.
                                                                                                                    +7
                                                                                                                    Ваш тест пройдет даже моя бабушка!
                                                                                                                      +2
                                                                                                                      Я бы рискнул сделать ставку на то, что 99% бабушек смогут пройти этот тест!
                                                                                                                        0
                                                                                                                        Вывод: все проблемы от излишней уверенности, нанимайте бабушек!
                                                                                                                      +6
                                                                                                                      Вапще когда я вижу int, short, long и прочее подобное у меня уже автоматически возникает вопрос: а архитектура то какая?

                                                                                                                      В целом для своего времени не привязывать типы к размерам, возможно, было неплохой идеей. Ибо бывали платформы и 16-битные, и 32-битные, и у каждого типа процессора были свои оптимальные размеры для целочисленных инструкций, которые разработчик процессора мог рекомендовать автору компилятора — это делало программы быстрее, причём, с теми рабочими частотами, ощутимо быстрее.

                                                                                                                      Что до UB по любому странному поводу — дык, не с проста. Каждое описанное UB это зарытый где-то рядом повод для оптимизации. В условиях нехватки процессорного времени тоже отличное решение.

                                                                                                                      Беда только в том, что теперь процессорного времени на человека есть целая гора, это позволяет нам запускать тяжеленные софтины на прожорливой Java, обрабатывать запросы к веб-серверам на интерпретируемых языках и делать много других странных вещей, недопустимых во времена создания C. Возможно, стоит сместить фокус системного программирования с оптимальности на безопасность?
                                                                                                                        0
                                                                                                                        Очевидно что Intel. Эта зараза от туда тянется.
                                                                                                                        Я уже давно привык юзать конкретные чёткие типы, как например uint32_t, int8_t и так далее.
                                                                                                                        Для математики тоже есть свои типы с чёткими размерами, от float64_t до float32_t, мелочь не используется -потому как считается в тиках намного дольше.

                                                                                                                        Насчёт выравнивания структур при оптимизации. Самый простой способ — выполнить объединение двух заведомо соразмерных структур. Для запрета перемешивания — объединение с перехлёстом.
                                                                                                                        Подобные знания требуются программисту который пишет на уровне железа — это конкретное обращение к регистрам в памяти. Всем остальным — лишний багаж знаний.

                                                                                                                        Это хорошо что ещё не затронули тему передачи параметров в функцию. Для разных архитектур ядра — параметры могут передаваться самым удивительным способом. С лева на право, с право налево, через регистры ядра, через теневые регистры ядра (не знали? :) ), через стек, через внешнюю память.
                                                                                                                        Зоопарк.
                                                                                                                          0

                                                                                                                          Архитектура ядра оказывает влияние на calling convention, но это не единственный определяющий фактор. ОС (если есть) или компилятор (если нет) могут из каких‐то соображений выбрать не самое оптимальное соглашение, которому будут следовать. (В принципе, компилятор может даже проигнорировать соглашение о способе вызова, принятое в определённой ОС, но по понятным причинам (взаимодействие с чужими библиотеками), скорее всего, не будет этого делать.)

                                                                                                                            +1

                                                                                                                            Или наоборот явно будет использовать несовместимый cc при использовании какого-нибудь stdcall или, например, cilkplus. Calling conventions, например, могут зависеть от набора инструкций или выбранного float-abi.

                                                                                                                          0
                                                                                                                          не забывайте, что есть научный софт, софт для анализа больших объемов данных, как пример из собственной практики могу привести обработку СВЧ радиосигналов. Там очень важно, чтобы работало как можно быстрее.
                                                                                                                          –2
                                                                                                                          Для программиста на С нет ответа «Я не знаю». Правильный ответ — на моей платформе (ОС+компилятор) ответ будет конкретно таким и таким.

                                                                                                                          Хотя я и ошибся с promote to int во втором примере )
                                                                                                                            +3
                                                                                                                            На 4-й пример GCC 4.2 -O3 выдает ответ 1, а GCC 4.8 с любой оптимизацией выдает 2.

                                                                                                                            Все такие исключительные ситуации для своей платформы и компилятора и его версии не запомнить (и не узнать) коммент в этом топике — см цитату.

                                                                                                                            Я раньше пользовался… К примеру переполнением uint8_t для счетчика циклического буфера чтобы избавится от всяких if, но чем больше узнаю, тем больше удивляюсь.jpg и стараюсь так не делать.
                                                                                                                              0
                                                                                                                              Наверное имелся в виду 5й. Там правильный ответ — так делать не надо, UB.

                                                                                                                              Но опять же, есть определенный перечень UB, на которые ты знаешь, как будет у тебя, но все равно сознательно пишешь нормально.
                                                                                                                                0
                                                                                                                                Я раньше пользовался… К примеру переполнением uint8_t для счетчика циклического буфера чтобы избавится от всяких if

                                                                                                                                А почему перестали? У uint8_t никаких проблем быть не должно.
                                                                                                                                  +2
                                                                                                                                  Переполнение, кажется самым популярным используемым UB с ожидаемым результатом. И хотя «разработчики компиляторов не звери» и так работает процессор (контроллер), но на всякий случай. Переполнение это UB. А если поведение UB может меняться от версии к версии gcc, то на всякий случай лучше не надо.

                                                                                                                                  Andrey2008 из PVS-Studio утверждает с переполнением лучше не шутить
                                                                                                                                  Обычно глупости выглядят как-то так (собирательный образ):

                                                                                                                                  Ну да, формально переполнение 'int' приводит к неопределенному повреждению. Но это не более чем болтовня. На практике, всегда можно сказать что получится.
                                                                                                                                  (Хотя в статье проблема в неправильном типе, и переполнение работает так как ожидается. И речь в статье не про UB при переполнении, но думаю у него опыта и знаний больше чем у меня.)

                                                                                                                                  Можете считать это религией или стилем. Но мне кажется он хорош. Если нет острой необходимости, то не использую хаки (и goto).
                                                                                                                                    +1
                                                                                                                                    Переполнение, кажется самым популярным используемым UB с ожидаемым результатом. И хотя «разработчики компиляторов не звери» и так работает процессор (контроллер), но на всякий случай. Переполнение это UB. А если поведение UB может меняться от версии к версии gcc, то на всякий случай лучше не надо.


                                                                                                                                    Переполнение uint8_t, равно как и любого другого беззнакового типа, согласно стандартам вообще невозможно, и, соответственно, не является UB.

                                                                                                                                    См. C89 3.1.2.5 Types, или C99 6.2.5:9 Types, или C11 6.2.5:9 Types
                                                                                                                                    A computation involving unsigned operands can
                                                                                                                                    never overflow, because a result that cannot be represented by the
                                                                                                                                    resulting unsigned integer type is reduced modulo the number that is
                                                                                                                                    one greater than the largest value that can be represented by the
                                                                                                                                    resulting unsigned integer type.
                                                                                                                                  0

                                                                                                                                  Очень зря. Свойства uint8_t (если таковой тип предоставляется реализацией) четко оговорены спецификацией языка и никакой неоднозначности не допускают. "Зацикленное" (модульное) поведение беззнаковых типов — очень полезная фича языка, пользоваться которой можно и нужно. Поэтому ваше "чем больше узнаю", похоже, привело лишь к тому, что с водой вы выплеснули и ребенка.

                                                                                                                                    0
                                                                                                                                    Спасибо Вам и jcmvbkbc. Был неправ. «Переполнение» беззнаковых типов безопасно и всегда будет одинаково. Я этого не знал и сильно удивился, чего это вдруг знаковое переполнение — UB, а беззнаковое безопасно. Пытался понять читая стандарт и форумы — не дало много толку, переполнение не только «implementation dependent» как могло бы быть, но и «undefined behavior», а беззнаковое определено. Хотя стандарт написан прямо юридическим языком, трудно понять (а причины и подавно). Картинка удивляюсь.jpg все еще актуальна.

                                                                                                                                    Ну в любом случае — если не знаешь можно перестраховаться, а знаешь четкую границу — можно и делать финт ушами и быть уверенным в безопасности. Спасибо.
                                                                                                                                +1
                                                                                                                                На все вопросы ответ: «Зависит от платформы и компилятора».
                                                                                                                                  0
                                                                                                                                  А первый вопрос любят на собеседованиях спрашивать, только, конечно, пишут, какое выравнивание установлено.
                                                                                                                                    +1
                                                                                                                                    Не знаю С. Ответил на все вопросы правильно.
                                                                                                                                      0
                                                                                                                                      Я тоже самое хотел написать. Слово в слово.
                                                                                                                                      0
                                                                                                                                      Когда смотрел вопросы, в голове был ответ «зависит от платформы и/или реализации компилятора».

                                                                                                                                      Когда смотрел предлагаемые ответы, среди них не было правильного. Потому что ответ «Я не знаю» в данном случае не отражает реалий.

                                                                                                                                      Поэтому это не тест, а подвох из серии «На грядке сидело 3 воробья, одного поймала кошка, сколько воробьёв осталось?».

                                                                                                                                      То, что в С куча тёмных, платформозависимых и компиляторозависимых моментов, совершенно не удивляет, а удивляет, что автор за 15 лет об этом не догадывался…
                                                                                                                                        +1
                                                                                                                                        Но даже так, мы не сравниваем типы, мы сравниваем размеры. И единственная гарантия которую стандарт дает о размерах short int и int в том, что предшествующий не должен быть больше последующего.

                                                                                                                                        В добавок к этому: оператор sizeof возвращает не размер операнда, а его пропорциональное (или как это правильно сказать) отношение к размеру char. То есть sizeof(int) == 4 еще не означает что int равен четырем байтам. Это означает только что в int может вместиться четыре char'а (Wikipedia).
                                                                                                                                          0
                                                                                                                                          по стандарту sizeof(char) = 1
                                                                                                                                            +1
                                                                                                                                            Да, всегда. Вот только количество битов в char не обязательно должно быть равно восьми.
                                                                                                                                              0
                                                                                                                                              потому что количество бит в байте не обязано быть равно 8