image

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

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

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

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

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

1


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

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

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

2


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

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

3


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

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

4


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

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

5



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

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

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



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

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

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

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

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

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

Читать еще


Дональд Кнут о своей счастливой жизни, своей смерти и о последних двух проектах
На сколько вопросов вы ответили «я не знаю»

Проголосовало 1274 человека. Воздержалось 243 человека.

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

Поделиться с друзьями
-->

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


  1. pengyou
    11.11.2016 15:52
    -32

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


    1. CodeRush
      11.11.2016 16:17
      +16

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


      1. rstepanov
        11.11.2016 17:25
        +7

        Иногда из инструментов отладки только 1 светодиод. А иногда нет даже его…


        1. vk2
          11.11.2016 18:10
          +3

          … но есть логический анализатор и осциллограф…


          1. stychos
            17.11.2016 02:22

            Это слишком дорого, надо на язык щупать.


        1. michael_vostrikov
          12.11.2016 10:18
          +3

          Писал программки для защищенного режима, в процессе работы отключалась видеокарта, выводил адрес инструкции с ошибкой спикером — два коротких бипа «1», один длинный «0».


      1. VioletGiraffe
        11.11.2016 17:26
        -4

        C++ же.
        Я, наверное, ни за какие деньги не взялся бы писать на чистом С. Это хороший язык, но слишком простой. Я не доверяю софту, написанному на С, потому что понимаю, как сложно писать на этом языке большие системы без ошибок. Это тот же ассемблер, только кросс-платформенный (ну, по большей части).


        1. lorc
          11.11.2016 18:17
          +3

          На самом деле C++ добавляет собственных тараканов к тем, что есть в C. Конечно, если писать на С++ правильно, то код будет намного безопасней. Но если писать на С правильно, то опять же можно бороться со сложностью. Есть куча прекрасных примеров: ядро linux, git, nginx, etc


          1. Ruckus
            14.11.2016 10:47
            -1

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


            1. aso
              14.11.2016 12:33
              -2

              В ядре linux, на сколько знаю, используют «С с классами»


              Но как, сэр?
              Это же чисто эппловская примочка, да ещё и какая-то высокоуровневая…


              1. lorc
                14.11.2016 16:36
                +3

                Элементарно, сэр. Структура, которая хранит указатели на функции — и вот мы получаем абстрактный объект. Заполняем указатели — и вот у нас экземпляр конкретного класса. Заменяем некоторые указатели на свои, немного магии с container_of и вот у нас наследование.

                Ядро Linux объектно-ориентировано, хоть и написано на C. Такие дела.


                1. aso
                  15.11.2016 08:49
                  +1

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


                  1. lorc
                    15.11.2016 19:45

                    Извините, но «Си с классами» сказал не я. Я просто рассказал как ядро устроенно внутри.

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

                    Не обязательно. А ещё для реализации ООП не обязательно иметь ключевое слово class в языке. Более того, язык вообще не может знать про классы. И при этом на нём можно писать объектно-ориентированный код.


                  1. Ruckus
                    17.11.2016 17:37

                    Я имел ввиду совсем не то. Прошу прощения, не углублялся в историю и «трупов» не изучал, потому любую имитацию ООП на чистом С называл «С с классами». Больше не буду, еще раз простите.


              1. stychos
                17.11.2016 02:26

                https://www.cs.rit.edu/~ats/books/ooc.pdf


              1. Ruckus
                17.11.2016 17:42
                -1

                Вы говорите про ObjC, а это совсем другой язык. И еще вам стоит знать, что «компилятор Objective-C входит в GCC и доступен на большинстве основных платформ». То, что это был основной язык программирования (коим возможно остается и сейчас) для устройств фирмы Apple никак не делает его «эппловским».


                1. Ruckus
                  18.11.2016 16:57

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


            1. lorc
              14.11.2016 16:46

              Зависит от задач, как всегда. Если у нас что-то гетерогенное, с базами данных, REST-запросами, гибким логгированием и GUI до кучи, но при этом хочется скорости — надо брать C++ иначе можно рехнуться.
              Если у нас есть конкретное очерченная задача (git) — хватит и C. Если нет rich runtime (linux) — опять С. Если мы максимально экономим ресурсы (nginx) — снова С.

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


              1. 0xd34df00d
                17.11.2016 20:51

                Если мы максимально экономим ресурсы (nginx) — снова С.

                Совершенно неочевидно, почему, кстати.


            1. deviant_9
              14.11.2016 20:37

              В ядре активно используются gcc-шные расширения, но «объектно-ориентированные» фишки реализуются вполне себе штатными средствами чистого Си. Никакого особого «Си с классами» там нет:)


              1. Ruckus
                17.11.2016 17:35

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


                1. lieff
                  18.11.2016 14:06

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


                  • Больший рантайм. Напирмер в андройде libstdc++ есть несколько на выбор Android/Sdk/ndk-bundle/sources/cxx-stl/, подключив любой и хоть немного заюзав — мой apk вырастет. Конечно можно и на урезанном С++ писать, но тут писали якобы таких спецов меньше. Так же если мы скажем что-то оптимизируем и глазами изучаем скажем .map файл, куча мелких с++ функций рантайма начнут мешать.
                  • Маленький компилятор, JIT tcc умещается где-то в 100-200кб. Аналогов тут не так много, разве что lua. Так что если мы хотим вставить в тот же apk и не раздуть его, llvm тут не подойдет.


                  1. Ruckus
                    18.11.2016 16:53

                    Вы правда смотрите на объем apk и вообще скомпилированного кода?
                    Может в этом и есть какой-то смысл, но не в ущерб читаемости, удобству и качеству. По крайней мере на тех платформах, где оперируют уже давно гигабайтами.
                    В ближайший месяц мы выпускаем приложение на Android, в котором часть кода написано на C++ (NDK) с использованием c++_shared (LLVM). Объем библиотек под все платформы вышел на 40-45Мб (7 архитектур), то есть в среднем 6.5Мб на архитектуру. Все приложение весит около 17Мб (apk). Для сравнения то же приложение на iOS весит 15Мб (С и ObjC). Вот уж не вижу смысла бороться ради пары мегабайт.
                    В плане оперативной памяти все аналогично.


                    1. lieff
                      18.11.2016 19:06

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


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


        1. CodeRush
          11.11.2016 18:22

          Непонятно, в чем преимущество C++ в тех нишах, где позиции С по прежнему сильны: embedded, firmware, kernel core, вот это все. Там не нужны исключения, полиморфизм и RTTI, там нет стандартной библиотеки, там мало толку от шаблонов, там часто вообще нет кучи, т.е. единственный доступный operator new — размещающий, и т.п.
          Для «нормального» C++ нужен гораздо более жирный рантайм, а специалистов, умеющих писать на «обрезаном» С++ — намного меньше, чем специалистов по С, т.к. такому «ограниченному» использованию С++ почти нигде не обучают.


          1. DeadKnight
            11.11.2016 18:54
            +3

            Основное преимущество C++ перед C в перечисленных нишах — метапрограммирование.


            1. VioletGiraffe
              11.11.2016 18:59
              +3

              Наверное, если система очень небольшая и влазит в считаные килобайты, от С++ пользы нет, один вред.Я под такие системы использовал компилятор С++, но писал, по сути, на С. А вот под STM32 d проекте на 5000 строк кода я использовал и шаблоны, и полиморфизм. Можно ли без них? Конечно, можно. Хочу ли я не пользоваться этими средствами? Нет, не хочу, они позволяют мне писать понятный, легко расширяемый и корректный код.


              1. aso
                14.11.2016 12:05

                Я как-то делал библиотечку, которая напрямую компилировалась в простые выражения присваивания или чтения битов ввода-вывода.
                Ну, типа — там несколько связанных битов, конфигурирующих ввод-вывод и сам I/O pin.
                Разумеется, это было несколько инлайновых шаблонов с наследованием — выглядело, в общем, неплохо.
                А биты адресовать невозможно (т.е.Си с ними вообще с ума сходил).
                Ну и конструкторы-деструкторы — штука няшная.


                1. Akon32
                  14.11.2016 12:15

                  А биты адресовать невозможно (т.е.Си с ними вообще с ума сходил).

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


                  1. aso
                    14.11.2016 12:32

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


                1. jcmvbkbc
                  14.11.2016 13:10

                  А биты адресовать невозможно

                  std::vector<bool> смотрит на это заявление с недоумением.


                  1. aso
                    14.11.2016 13:16
                    -1

                    И хде там биты?


                    1. jcmvbkbc
                      14.11.2016 13:19

                      Внутри. Это битовый вектор.


                      1. aso
                        14.11.2016 13:33
                        +1

                        Внутри.


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

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


                        1. jcmvbkbc
                          14.11.2016 13:42

                          поверх всей этой радости предлагается присобачивать

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

                          И, да, правила.


                          1. aso
                            14.11.2016 15:16

                            Это эмуляция битовой адресации, а не.
                            И да, в треде утверждалось, что «С++ нафик не нужен в эмбеде со своими шаблонами, наследованием и т.д.».
                            Например, вот tyt.
                            Ваш тезис ничего не добавляет к моему оппонированию этому.


                          1. haqreu
                            18.11.2016 09:25

                            Попробуйте сделать std::swap на двух элементах std::vector<bool>.


                            1. jcmvbkbc
                              18.11.2016 19:39
                              -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;
                              }
                              

                              Учитывая то, что исключений здесь быть не может, в чём подвох?


                              1. haqreu
                                18.11.2016 20:05

                                А скажите, зачем вы переопределили std::swap?


                                1. jcmvbkbc
                                  18.11.2016 20:13

                                  Готовый std::swap ожидает, что ссылочный тип оканчивается на '&', что, конечно же, не всегда правда. Это же, вроде, известная ловушка шаблонных функций?


                              1. TrueBers
                                18.11.2016 23:41

                                В современных компиляторах, поддерживающих C++11 и выше, swap давно уже как перегружен для

                                vector<bool>::reference
                                
                                и всё отлично работает.


                                1. jcmvbkbc
                                  19.11.2016 00:08

                                  Да, верно. Перегрузка нужна для С++98. Тем более непонятно, в чём подвох.


            1. Antervis
              12.11.2016 18:04

              а есть и недостатки. Например, подключение библиотек STL, и программа разрастается до таких размеров, что не влазит в контроллер. В итоге придется писать на подмножестве, одинаковом для с и с++.


          1. a-tk
            12.11.2016 02:37
            +4

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


            1. TheCalligrapher
              14.11.2016 01:08
              -3

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


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


              1. Antervis
                14.11.2016 05:17

                например, неявный каст const T* к T*


                1. TheCalligrapher
                  14.11.2016 06:06

                  Это откуда? В языке С никогда не было неявного приведения `const T*` к `T*`. Разумеется с того момента, комитет X3J11 ввел в язык С `const`.


              1. deviant_9
                14.11.2016 21:48
                +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. TheCalligrapher
                  14.11.2016 22:15
                  -1

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


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


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


                  1. deviant_9
                    14.11.2016 22:43
                    +1

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

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


                    1. TheCalligrapher
                      15.11.2016 20:21
                      -1

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


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


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


                      1. deviant_9
                        15.11.2016 22:28

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

                        Приведу пример. Допустим, есть у нас некая функция, которая по каким-то причинам (крайне устаревший код, некомпетентность автора, потенциальное наличие заведомо нереализуемых в данной ситуации особых вариантов поведения и т. д.) принимает параметр типа 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 ему действительно ничем не помогут. Но контроль типов — это немного про другое.


                  1. Siemargl
                    14.11.2016 23:19

                    Собственно, warning's не имеют никакого отношения к корректности языковых конструкций (соответствия синтаксису).
                    Это всего лишь дружеская необязательная помощь компилятора.


                    1. deviant_9
                      14.11.2016 23:57

                      Таки имеют.

                      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. 0xd34df00d
                        17.11.2016 20:54
                        +1

                        Но из этого не следует, что каждый warning приходится на одно такое требование стандарта.


                    1. TheCalligrapher
                      15.11.2016 20:11
                      -2

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


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


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


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


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


                  1. Siemargl
                    14.11.2016 23:24

                    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;
                    ^


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


                    1. TheCalligrapher
                      15.11.2016 20:27

                      Неверно. И 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;
                                 ^~~
                      


                      1. Siemargl
                        15.11.2016 22:14

                        Я прекрасно понимаю как оно скомпилируется и как работает.
                        Например вижу ошибку выхода за границу массива в коде выше, о которой НЕТ варнинга.

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

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

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

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


                        1. TheCalligrapher
                          15.11.2016 22:47

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


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


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


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


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


                          1. Siemargl
                            15.11.2016 22:57

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

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

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


                            1. TheCalligrapher
                              16.11.2016 00:35
                              -2

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


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


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


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


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


                              1. Siemargl
                                16.11.2016 00:45
                                +2

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

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


                                1. TheCalligrapher
                                  16.11.2016 01:27
                                  -2

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


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


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


                        1. deviant_9
                          15.11.2016 23:07
                          +1

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

                          То есть вы ещё не перешли на utf-8?


                          1. Siemargl
                            15.11.2016 23:26

                            Нет. И если верить стандарту С11, надо было писать u8«Нет»


                            1. deviant_9
                              20.11.2016 10:18

                              В цивилизованном мире UTF-8 — кодировка по умолчанию. Так что если вы сохранили исходник в UTF-8, то и результирующая строка в норме должна быть в UTF-8, даже в отсутствие префикса u8 (как минимум, у меня под линуксом в gcc дела обстоят именно так). Это не требуется стандартом (source character set и execution character set, в принципе, могут быть разными), но и не противоречит ему.

                              Чтобы (достоверно) увидеть ошибку выхода за границы массива в вашем примере, надо знать компилятор и кодировку исходника. Без этой информации отсутствие выхода за границы более вероятно.


                              1. Siemargl
                                20.11.2016 16:11

                                В цивилизованном мире UTF-8 — кодировка по умолчанию.
                                У меня — нет, на гитхабе я тоже встречал в основном ascii.

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


                                1. grossws
                                  20.11.2016 18:03
                                  +3

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


                      1. Siemargl
                        15.11.2016 22:24

                        Собственно, поскольку грубую ошибку вы не заметили, а придираетесь к пуговицам, возвращаю вам «что вы пока не умеете „на глаз“ отличать „безобидные“ диагностические сообщения от серьезных ошибок, я бы посоветовал вам» =)

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

                        Кстати именно буквоедство свойственно многим новичкам — это к посту ниже по некоторых индивидуумов.


                        1. TheCalligrapher
                          15.11.2016 23:00

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

                          Нет.


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


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


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


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


                          1. Siemargl
                            15.11.2016 23:21

                            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, но иногда при работе с железом так и нужно делать. Не запрещено.


                            1. TheCalligrapher
                              15.11.2016 23:57

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


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


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


                              1. Siemargl
                                16.11.2016 00:10
                                +2

                                Вот уж нет, приведение указателей на разные типы и конверсия из int это как раз даже не UB, я преувеличил — implementation defined, так написано в 6.3.2.3 пункт 5 стандарта.

                                5 An integer may be converted to any pointer type. Except as previously speci?ed, the
                                result is implementation-de?ned, might not be correctly aligned, might not point to an
                                entity of the referenced type, and might be a trap representation.
                                56)

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

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

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


                                1. TheCalligrapher
                                  16.11.2016 01:16
                                  -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. Siemargl
                                    16.11.2016 01:54
                                    -1

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

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

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

                                    За сим откланиваюсь.


                                    1. TheCalligrapher
                                      16.11.2016 02:03
                                      -1

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

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

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

                                      Ну что ж — и наше вам с кисточкой.


                  1. deviant_9
                    14.11.2016 23:48
                    +1

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

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

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

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


                    1. TheCalligrapher
                      15.11.2016 21:24
                      -1

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


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


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


        1. akastargazer
          11.11.2016 19:46
          +1

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


          1. TheCalligrapher
            14.11.2016 01:11

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


            1. TheCalligrapher
              14.11.2016 06:52

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

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

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


        1. polifill
          11.11.2016 21:12
          -4

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

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

          Например,
          https://github.com/cocaine/cocaine-core
          Это одна из базовых технологий Яндекса, сделанная не самыми глупыми программистами.


          1. VioletGiraffe
            11.11.2016 21:39
            +6

            Я разрабатываю кросс-платформенные приложения под Винду, Линукс, Андрои, иОС и Мак. Ваше заявление, мягко говоря, не соответствует действительности. Яндекс где-то накосячил.


            1. polifill
              12.11.2016 08:07
              -2

              Это можно — кто ж спорит.

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

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

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

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

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

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

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

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

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


              1. VioletGiraffe
                12.11.2016 10:42
                +5

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


                1. polifill
                  12.11.2016 11:37
                  +2

                  Возьмите и откопилируйте программу по ссылке выше.

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

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

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

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

                  Вплоть до багов в такой казалось бы базово-универсальной вещи как Boost, не говоря уже о более специфичных библиотеках…


                  1. VioletGiraffe
                    12.11.2016 12:06
                    +3

                    Бибилиотек у меня около 15, от exiv2 до libusb и libftdi. Естественно, используем только кросс-платформенные библиотеки.
                    Буст не использую, он слишком большой и сложный в интеграции.


                    1. polifill
                      12.11.2016 12:43

                      Алогичность детектед:

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

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

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

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


                      1. VioletGiraffe
                        12.11.2016 13:04
                        +4

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

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

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


                        1. polifill
                          12.11.2016 13:23
                          -1

                          Вы вновь повторяете алогичность.

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

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

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

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

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

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

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

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

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

                          И, очень интересно, а что вы называете кросс-платформенностью своей системы? Где именно возможна ее работа, на каких ОС?


                          1. VioletGiraffe
                            12.11.2016 13:53
                            +3

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

                            О чём мы вообще спорим? На С++ можно писать непортируемый код? Да, можно. И на Java можно. Банально, вызвали WinAPI функцию — всё, ваш код стал непортируемым. На С++ вызывать сторонние компоненты проще, и для меня это преимущество, а не недостаток. Правильное использование фич всегда на совести програмиста, а не языка.
                            Зато писать портируемый код на современном С++ намного проще, чем на С (кроме самых embedded платформ, наверное).
                            4. GUI на основе другой технологии — лишняя головная боль. Всякие прослойки-адаптеры., через которые надо правильно протащить каждый чих.
                            5. Я уже говорил, что почти все мои программы работают на Linux, Windows и OS X, а некоторые из них также на Android и iOS.


                            1. polifill
                              12.11.2016 14:40
                              -2

                              Доказательством может быть только опыт.
                              Лично вы работали в окружении опытных спецов — вам очень повезло.
                              Нет, не так. ВАМ ОЧЕНЬ СИЛЬНО ПОВЕЗЛО, ЕСЛИ ВОКРУГ ВАС ОДНИ ПРОФИ.
                              В реальной жизни это большая и большая редкость.
                              В среднем человек любой квалификации довольно посредственнен как профессионал.
                              То, что вы еще не сталкивались с огромным числом не профи — говорит о вашем малом жизненном опыте.
                              И только.


                              1. VioletGiraffe
                                12.11.2016 14:44
                                +1

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


                                1. polifill
                                  12.11.2016 15:24
                                  -1

                                  > что я работал в окружении обычных хороших програмистов, которые тоже понаписывали непортируемого кода

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

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

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

                                  С C++ этой работы слишком много, вот в чем дело.


                                  1. VioletGiraffe
                                    12.11.2016 16:00
                                    +1

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

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


                                    1. polifill
                                      12.11.2016 16:20
                                      -1

                                      > Я так не считаю. Единственная серьёзная сложность была — избавиться от анонимных union.

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

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

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

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

                                      Не должно от «анонимных union» ничего зависеть. Это бага дизайна языка


                                      1. VioletGiraffe
                                        12.11.2016 16:41

                                        Нет. Это бага компилятора, что он компилит нестандартный код.
                                        Библиотеки изначально брались только кросс-платформенные. Никакого везения, холодный расчёт.


                                        1. polifill
                                          12.11.2016 17:49
                                          -2

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

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

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

                                          Вместо того, чтобы сосредоточиться на задаче — ты тратить часть своего времени на борьбу со своим инструментом.


                          1. 0xd34df00d
                            12.11.2016 20:22

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

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


                            1. polifill
                              12.11.2016 20:38
                              -2

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

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

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

                              По сравнению с Go, Java, Python…


                      1. Antervis
                        12.11.2016 18:29
                        +1

                        там, где надо писать на с/с++, уже не подойдут питон, го и джава


      1. DeadKnight
        11.11.2016 18:51
        +1

        На C++?
        Правда там ответ на эти вопросы будет таким же.


    1. Dark_Purple
      11.11.2016 18:56
      -3

      За такие комментарии надо с HABRы выпиливать.


    1. Akon32
      11.11.2016 20:40
      +3

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


      1. polifill
        11.11.2016 21:20
        +1

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

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


        1. Labunsky
          11.11.2016 23:05
          +2

          Чем си не хорош-то? Хоть сам на него редко сажаюсь, но его лаконичность и простота каждый раз действуют на мозг очень приятно после какой-нибудь джавки. Наверное, лучше него с KISS ничто не сочетается, но это сугубое ИМХО


          1. quwy
            12.11.2016 03:13
            +2

            А огромного количества UB вам мало?


            1. Labunsky
              12.11.2016 03:22
              -1

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


              1. polifill
                12.11.2016 12:50
                +4

                Си — это такой над-ассемблер. Таким его задумали, так надо было. И до тех пор пока на Си писали ядра операционных систем да драйвера — эти самые UB можно пережить.

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

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

                Если под «любым языком» вы понимаете C++ — тогда конечно, да, я с вами соглашусь.


                1. Labunsky
                  12.11.2016 14:10
                  -1

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

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


                1. akzhan
                  12.11.2016 23:24

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


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


                  1. Antervis
                    13.11.2016 00:19

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


                    1. akzhan
                      13.11.2016 00:39

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


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


                      1. lieff
                        13.11.2016 01:11

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


                        1. akzhan
                          13.11.2016 01:20

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


                          1. lieff
                            13.11.2016 02:22
                            +1

                            Ну выше было что в стандарте нет, тем не менее в стандарте он все же есть, его может не быть на платформе и стандарт это предусматривает. Можно написать корректный код без UB, который работает и в случае наличия точного типа и когда его нет. Можно или 2 варианта кода или универсальный, который закладывается только на least. Так же в случае использования точного типа и его отсутствия — код просто не соберется. Так что все же имхо Antervis прав.


                        1. deviant_9
                          13.11.2016 13:19
                          +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).


          1. 0xd34df00d
            12.11.2016 05:56
            +2

            Лаконичность и простота — это вам в хаскель, а не си.


          1. VioletGiraffe
            12.11.2016 13:44

            Главная проблема Си для меня — отсутствие классов = нет простого и понятного механизма управления сложностью проекта через абстракции.


            1. Labunsky
              12.11.2016 14:17

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


              1. VioletGiraffe
                12.11.2016 18:37

                Логично. Лучше, чем ничего.


    1. NeoCode
      11.11.2016 21:00
      +3

      Зря вас минусуют. Я разрабатываю на С и С++ уже около 15 лет, начиная от микроконтроллеров и заканчивая сложными распределенными системами — и могу сказать — да, жаль.
      И нет, я не предлагаю перейти всем на javascript:) Но необходимость создания универсального языка программирования для замены С и С++ назрела уже давно. А не происходит это из-за инертности мышления и так называемого груза унаследованного кода, который якобы никто не хочет переписывать. Господа, переписать его не такая уж и проблема, было бы на что. Программисты так или иначе постоянно переписывают код, адаптируют его, переписали бы и на единую стандартизированную модификацию Си, заодно кучу древних багов выловили бы. Ах да, бизнес же не хочет тратить лишнее время и платить лишних денег за непонятную работу… им же надо чтобы тяп ляп и в продакшен. Ничего, не обеднели бы.

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


      1. iCpu
        11.11.2016 22:37
        +7

        Позвольте мне только один нескромный вопрос: а возможно ли в принципе свести воедино все зоопарки устройств, под которые писали и пишут на си, в один единый строгий стандарт?

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

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

        Единственное бесспорное место — макросы, заменить бы их более удобоваримым метаинструментом.


        1. NeoCode
          11.11.2016 22:58

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


          1. iCpu
            11.11.2016 23:20
            +1

            Вы же понимаете, что вы не избавляетесь от проблемы, а перекладываете её в другой карман. Я не спорю, в стандарте явно не хватает встроенных инструментов получения некоторых гарантий, хоть он и сделал не так давно приятный шаг вперёд, как с теми же типами.
            Но, позвольте, разве модульность не устанавливается на уровне компилятора? Тот же AVR GCC проглатывает далеко не весь код, хотя бы по причинам организационным, 32 килобайта, все дела. Все ваши модули уже спрятаны в нём. в компиляторе, и иначе быть не может, а доступ к нестандартным методам всё равно получается после подключения платформозависимых заголовочников, к которым есть ассемблерный код. То есть вы ни капли не добавите ничего нового.

            То, что вы предлагаете, оно должно продумываться на этапе разработки библиотеки. Конечно, есть проблема сужающихся возможностей, когда наши амбиции придавливает sizeof(int), но ведь есть ровно обратные ситуации. Это вопрос говнокода, не языка. Но если мы строим библиотеку, способную работать на разных архитектурах, то нам над ними придётся воспарить в некоторой, возможно. параметризуемой, абстракции. А иначе как?


            1. NeoCode
              11.11.2016 23:51
              +1

              Все верно, параметризируемая абстракция должна быть.
              В простейшем случае если нам нужен просто int и все равно сколько там байт, так и пишем int. А если нужно что-то конкретное (для сетевого пакета или двоичного файла) то пишем int8 или int16 или int32.
              Но параметризация должна быть явная, как и модульность. Вообще все должно быть явное, стандартизированное и общее для всех компиляторов.


              1. iCpu
                12.11.2016 05:08

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


          1. polifill
            12.11.2016 08:35
            -1

            За 40 лет проблему не решили. Хотя ей занимались люди поумнее и вас и меня.

            Уже не решится проблема. Очевидно.


        1. polifill
          12.11.2016 08:27
          +1

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

          В Си же можно накосячить «втихую».


          1. iCpu
            12.11.2016 08:50
            -1

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

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

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


            1. polifill
              12.11.2016 11:42
              +2

              Си был создан для замены ассемблера при создании операционной системы.

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

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

              Зачем мы пишем прикладное ПО на «почти ассемблере» до сих пор?


              1. iCpu
                12.11.2016 16:37

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

                Или вы из «новой волны», которая пишет под Arduino на JavaScript?


      1. Karpion
        12.11.2016 09:19
        -1

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

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

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

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


  1. aol-nnov
    11.11.2016 15:55
    +4

    в 4 вопросе не хватает ответа «42», так как блока с вопросом там нет, видимо, это вопрос жизни, вселенной и всего такого…


    1. zag2art
      11.11.2016 15:59
      +1

      Чукча не читатель, чукча писатель


  1. ARyndin
    11.11.2016 16:04

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

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


    1. m0sk1t
      11.11.2016 16:08
      +1

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


      1. asdf87
        11.11.2016 16:27

        Он видимо для этого и задумывался. Чтобы счет был хотя бы 1 из 5.


    1. Siemargl
      11.11.2016 20:35
      -1

      Откуда операция сравнения даст 4 ???
      Все там верно


  1. Amomum
    11.11.2016 16:19
    +8

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

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

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


    1. jcmvbkbc
      12.11.2016 02:26
      +2

      5) Это не «я не знаю», это называется «неопределенное поведение».

      Вопрос был не «что это», а «что вернёт программа».


  1. ColdPhoenix
    11.11.2016 16:23
    +9

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

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

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

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


    1. Zoberg
      11.11.2016 16:56
      +4

      Так это не понурые «нуу я не знаю...», а уверенные — «я не знаю, потому что».


      1. Randl
        16.11.2016 01:52

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


  1. barker
    11.11.2016 16:24
    +9

    Какой простой тест, я на все вопросы верно ответил как оказалось.


  1. lair
    11.11.2016 16:24
    +27

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

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


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


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


  1. Alert123
    11.11.2016 16:25

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


    1. TheCalligrapher
      14.11.2016 01:14
      -3

      Видимо поэтому вам никогда не доведется принимать такие решения.

      Это не трюки, а фундаментальные свойства С, как платформы. Это атомы, из которых состоит С. Абсолютно любая программа на языке С опирается на эти «трюки».


      1. Antervis
        14.11.2016 05:18
        +3

        Нет. Ни одна программа не должна опираться на неопределенное поведение


        1. TheCalligrapher
          14.11.2016 06:08

          В моем комментарии нет ни слова о том, что программы «должны опираться на неопределенное поведение».


          1. Antervis
            14.11.2016 06:52
            -1

            все эти трюки являются примерами неопределенного поведения. Или, как минимум, неоднозначно/контекстуально определенного/платформозависимого поведения.


            1. TheCalligrapher
              14.11.2016 06:57

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


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


              1. Antervis
                14.11.2016 07:20
                +1

                вы написали:

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

                Часть «трюков» — примеры неопределенного поведения, программы без ошибок его не содержат. Остальная часть — неоднозначности, закладываться на которые в общем случае нельзя.


  1. RomanArzumanyan
    11.11.2016 16:28
    +3

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


    1. vvzvlad
      11.11.2016 20:10

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


      1. RomanArzumanyan
        11.11.2016 22:04
        +1

        Стандарт Си в области целочисленной арифметики — это большая боль. Он слишком поздно появился для такого важного языка.


        1. Siemargl
          11.11.2016 23:44

          В этом направлении боли не видел я, кроме тривиального 3/2.

          Есть другие прорехи, конечно.


  1. lieff
    11.11.2016 16:31
    +3

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


  1. ns5d
    11.11.2016 16:44
    +2

    Варианты ответов составлены плохо, а именно «Я не знаю». Я трактовал его как признание, что я не знаю С. Правильно было бы написать «Неопределенное поведение». Тогда результаты ответов были бы другими.


    1. ZyXI
      12.11.2016 18:42

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


      1. ns5d
        12.11.2016 19:29

        это не меняет сути моей притензии


  1. stepanp
    11.11.2016 16:59
    -6

    Где ответы то?


    1. ns5d
      11.11.2016 17:07
      +1

      Ctrl+F «Итак верные ответы»


    1. savostin
      11.11.2016 17:58
      +11

      я не знаю


  1. GREGOR_812
    11.11.2016 17:22
    -4

    В последнем случае на самом деле всё понятно
    Рассмотрим два пути:

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

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

    и результат равен 2, и значение i равно 2


    1. LynXzp
      11.11.2016 17:38
      +3

      В зависимости от компилятора (хотя могут все и вычислять одинаково, но не обязаны). Это неопределенное поведение по стандарту «Если программа пытается модифицировать одну переменную дважды не пересекая точку следования, то это ведет к undefined behavior. Так говорит Стандарт.» http://alenacpp.blogspot.com/2005/11/sequence-points.html


      1. GREGOR_812
        11.11.2016 18:09

        LynXzpSingerofthefall как это противоречит моему утверждению?


        1. LynXzp
          11.11.2016 19:26

          Это говорит о том что Ваше утверждение не обязательно верное, точнее результат выполнения программы такой, но только сегодня, и только в этой версии компилятора (может еще в других). Сегодня такой код выдает 2. А завтра благодаря side-effects оптимизаций компилятора он может выдать и 1 и 3 (а может еще что-то). Разработчики компиляторов не обязаны делать так чтобы результат получался 2. И если это будет выгодно в угоду другим оптимизациям они изменят логику и результат будет другой.

          1
          (i++) + (++i) =(компилятор параллельного мультискаляного процессора считает что можно вычислить результат скобок независимо, а потом сложить) = 0 + 1 = 1
          Как получить три, не придумал, но в более сложных и запутанных ситуациях может получится и больше разных ответов. Причем они все не будут багами, потому что UB.


          1. GREGOR_812
            11.11.2016 19:39

            Да, спасибо, уже понял)


    1. Singerofthefall
      11.11.2016 17:48
      +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.


    1. SBKarr
      11.11.2016 18:46
      +4

      А вот GCC 4.2 (если не ошибаюсь) с -03 выдавал 1. Объясняю на пальцах.

      Логика такая: компилятор принял i за константу внутри точки следования и оптимизировал левый инкремент до возврата нуля, а сам инкремент поставил в конец точки следования (и отбросил за ненадобностью, ведь дальше тела функции i не используется). После чего выполнил правый инкремент, сложение и вернул результат.


      1. TheCalligrapher
        14.11.2016 06:25
        +1

        Это не более чем частный случай проявления неопределенного поведения.


  1. TrueBers
    11.11.2016 17:24
    +2

    Существуют такие расширения как GCC атрибуты aligned и packed которые позволяют вам получить некоторый контроль над этим процессом, но они не стандартизированные.

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

    C11 ISO Standard, 6.7.5 Alignment specifier.
    April 2011.


  1. slonm
    11.11.2016 17:40
    +2

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


  1. dnf
    11.11.2016 17:57
    -1

    Ответил «я не знаю» на первые два вопроса — интуитивно отгадал правильный ответ на остальные три, даже не читая вопросов =) Итого 5.


  1. vastzp
    11.11.2016 18:30
    +7

    Ваш тест пройдет даже моя бабушка!


    1. armature_current
      11.11.2016 19:28
      +2

      Я бы рискнул сделать ставку на то, что 99% бабушек смогут пройти этот тест!


      1. vvzvlad
        11.11.2016 20:11

        Вывод: все проблемы от излишней уверенности, нанимайте бабушек!


  1. SBKarr
    11.11.2016 18:31
    +6

    Вапще когда я вижу int, short, long и прочее подобное у меня уже автоматически возникает вопрос: а архитектура то какая?

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

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

    Беда только в том, что теперь процессорного времени на человека есть целая гора, это позволяет нам запускать тяжеленные софтины на прожорливой Java, обрабатывать запросы к веб-серверам на интерпретируемых языках и делать много других странных вещей, недопустимых во времена создания C. Возможно, стоит сместить фокус системного программирования с оптимальности на безопасность?


    1. AVI-crak
      12.11.2016 02:38

      Очевидно что Intel. Эта зараза от туда тянется.
      Я уже давно привык юзать конкретные чёткие типы, как например uint32_t, int8_t и так далее.
      Для математики тоже есть свои типы с чёткими размерами, от float64_t до float32_t, мелочь не используется -потому как считается в тиках намного дольше.

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

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


      1. ZyXI
        12.11.2016 18:58

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


        1. grossws
          16.11.2016 23:57
          +1

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


    1. Antervis
      12.11.2016 18:21

      не забывайте, что есть научный софт, софт для анализа больших объемов данных, как пример из собственной практики могу привести обработку СВЧ радиосигналов. Там очень важно, чтобы работало как можно быстрее.


  1. Siemargl
    11.11.2016 18:37
    -2

    Для программиста на С нет ответа «Я не знаю». Правильный ответ — на моей платформе (ОС+компилятор) ответ будет конкретно таким и таким.

    Хотя я и ошибся с promote to int во втором примере )


    1. LynXzp
      11.11.2016 19:39
      +3

      На 4-й пример GCC 4.2 -O3 выдает ответ 1, а GCC 4.8 с любой оптимизацией выдает 2.

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

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


      1. Siemargl
        11.11.2016 20:34

        Наверное имелся в виду 5й. Там правильный ответ — так делать не надо, UB.

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


      1. jcmvbkbc
        12.11.2016 02:30

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

        А почему перестали? У uint8_t никаких проблем быть не должно.


        1. LynXzp
          12.11.2016 13:20
          +2

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

          Andrey2008 из PVS-Studio утверждает с переполнением лучше не шутить

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

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

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


          1. jcmvbkbc
            12.11.2016 13:39
            +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.


      1. TheCalligrapher
        13.11.2016 22:52

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


        1. LynXzp
          14.11.2016 01:13

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

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


  1. Dark_Purple
    11.11.2016 18:59
    +1

    На все вопросы ответ: «Зависит от платформы и компилятора».


  1. crea7or
    11.11.2016 19:27

    А первый вопрос любят на собеседованиях спрашивать, только, конечно, пишут, какое выравнивание установлено.


  1. dimka665
    11.11.2016 20:02
    +1

    Не знаю С. Ответил на все вопросы правильно.


    1. aqec
      11.11.2016 20:34

      Я тоже самое хотел написать. Слово в слово.


  1. demitel
    11.11.2016 20:02

    Когда смотрел вопросы, в голове был ответ «зависит от платформы и/или реализации компилятора».

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

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

    То, что в С куча тёмных, платформозависимых и компиляторозависимых моментов, совершенно не удивляет, а удивляет, что автор за 15 лет об этом не догадывался…


  1. Crypt0r
    11.11.2016 20:30
    +1

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

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


    1. Antervis
      12.11.2016 18:17

      по стандарту sizeof(char) = 1


      1. Crypt0r
        12.11.2016 18:38
        +1

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


        1. Antervis
          12.11.2016 20:25

          потому что количество бит в байте не обязано быть равно 8


    1. deviant_9
      13.11.2016 13:03

      То есть sizeof(int) == 4 еще не означает что int равен четырем байтам


      Тут два момента.

      С одной стороны, означает:
      The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type.


      С другой стороны, стандарт Си описывает всё в терминах абстрактной машины, не привязываясь ни к каким свойствам реальных машин. В Си своя модель памяти, которая не обязана сколько-нибудь явно соответствовать организации памяти той реальной среды, в которой программа будет выполняться (собственно, и не соответствует: компиляторы одни переменные помещают в регистры, другие полностью выкидывают, переупорядочивают операции чтения/записи и т. д.). Поэтому и понятие «байт» в стандарте Си, строго говоря, не обязано совпадать с понятием байта в «железе».


  1. Siemargl
    11.11.2016 20:39

    На самом деле, по теме статьи можно расширить свой список «я этого не знал» на сайтике тестов http://www.quizful.net.

    Так сколько то в день попыток бесплатных, хватит чтобы оценить уровень пола.


  1. ittakir
    11.11.2016 21:00

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

    Так и С. К черту все эти undefined behaviour, #pragma pack, ++i++ + i++, sizeof(**&(*i)->j), const * const * int.
    Нужно заниматься делом! Придумывать стартапы, накидать на Ruby бэкэнд с интеграцией в соцсети, мобильное приложение под все телефоны. И не забыть добавить Bluetooth, потому что с ним всегда лучше.
    2016 год уже заканчивается…


    1. CodeRush
      11.11.2016 21:22
      +2

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


      1. ittakir
        12.11.2016 09:00

        Это само собой. Но Winsock была написана сто лет назад. Работа с файлами тоже. Все, это больше не надо писать. Используйте результат труда тех людей, кто писал на С. Но сами пишите на более высокоуровневых языках. Иначе не видать нам технологической сингулярности, как своих ушей!

        Единственное место, где стоит писать на С сейчас — это микроконтроллер ATTiny13A. Дешевый, маленький. 1КБ Flash, 64 байта RAM. Для остального С++ или еще более высокоуровневые языки.


        1. Khasuist
          12.11.2016 17:44

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


          1. ittakir
            12.11.2016 19:06
            -1

            Ассемблер у каждого микроконтроллера разный. Лучше использовать универсальный ассемблер — язык С.
            Поверьте, поддерживать старый проект на С намного проще, чем на ассемблере.
            Но чистый С использовать уже не имеет смысла. Компиляторы поддерживают С++, некоторые даже С++11. И не обязательно использовать динамическое выделение памяти, порой очень не хватает простого синтаксического сахара. Например, в С меня жутко выбешивает необходимость объявлять все переменные строго в начале функции. Какого черта я не могу написать for(int i=0; i < c; ++i)?


            1. Antervis
              12.11.2016 20:28
              +2

              а разве это требование не было убрано в стандарте с89?


              1. ittakir
                13.11.2016 08:09
                -1

                Может быть, но Freescale CodeWarrior IDE не в курсе этого.


                1. TheCalligrapher
                  13.11.2016 19:59

                  Во-первых, при чем здеcь "IDE" не совсем ясно. Все таки это определяется компилятором, а не IDE.


                  Во-вторых, вы фантазируете. В CodeWarrior нет и никогда не было требования "объявлять все переменные строго в начале функции".


              1. jcmvbkbc
                13.11.2016 10:15

                В с99 так можно, в с89 — нельзя.


            1. TheCalligrapher
              13.11.2016 19:57

              В языке С нет и никогда не было требования объявлять переменные строго в начале функции. Откуда вы это вязли?


              Даже K&R С (не говоря уже о стандартизованном С) переменные всегда объявлялись в начале блока, а не в начале функции. Однако и это — далекое прошлое. В языке С переменные уже 17 лет как можно объявлять где угодно.


              Другими словами вас "жутко выбешивает" вам же выдуманная чепуха, не имеющая никакого отношения к реальности.


        1. 0xd34df00d
          12.11.2016 20:30
          -1

          Я под него вполне успешно писал на плюсах, кстати.


    1. unabl4
      11.11.2016 23:32

      Это всё прекрасно, только стандартный руби до сих пор на шее С сидит и в обозримом будущем с него слезать вообще не собирается :) Как, впрочем, и некоторые другие интерпретируемые языки. А делами нужно заниматься, да, только каждый своим — кто руби, кто явой, а кто и си. Главное в гармонии сосуществовать :)


    1. Siemargl
      11.11.2016 23:46

      Да, но твоя пила, стул и ты, все написано на С =)


    1. Antervis
      12.11.2016 18:16

      да вам в жизни не понадобится #pragma pack в том, что можно написать на питоне/джаве/руби. Остальное является головоломками и/или примерами ub, хотя в реальном коде встречается раз в декаду и то скорее в виде ошибки.


  1. u_w
    12.11.2016 00:13
    +4

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


    1. SBKarr
      12.11.2016 00:52
      -2

      Ну, например, вот в таких вариантах: https://github.com/cocos2d/cocos2d-x/blob/v3/cocos/renderer/CCTexture2D.cpp#L131 (Вопрос к публике, а вы с какого раза въехали в порядок операций в выражении?). Добавить инкремент по другую сторону присваивания, и будет практически вариант за номером 5.

      А что-то вроде ((void (*)(int))8)(42) довольно часто случается в микроконтроллерах.


      1. Siemargl
        12.11.2016 02:19

        Про https://github.com/cocos2d/cocos2d-x/blob/v3/cocos/renderer/CCTexture2D.cpp#L131 очень примитивный подкол.
        Это стандартное обращение, начиная с 1го класса изучения С: *pc++=c;


  1. third112
    12.11.2016 14:46
    +1

    С большим интересом прочел статью и все комметы (на данный момент). ИМХО сделано много верных замечаний про зоопарки (платформ, систем, компиляторов), сказано много правильных слов про необходимость знать стандарты языка, особенности железа и компилятора, на котором работаешь. Но как быть с переносом кода? Где-то, когда-то, кто-то на антикварных сейчас железе, системе, компиляторе (ЖСК) написал нужный мне код, но я могу не знать то ЖСК, тогда вероятно, что используя тот код отгребу кучу багов, которые мне трудно будет фиксить. Чем дальше в лес — тем больше дров: зоопарк ЖСК растет. Для долговременной преемственности кода нужен достаточно строгий язык типа стандартного Паскаля. Пусть ОО Паскаля. Конечно, за все приходится платить. Си гораздо более гибкий чем Паскаль, и даже турбо, и даже Дельфи. Многие трюки будут невозможны. Но м.б. стоит заплатить отказом от трюков? А когда они очень нужны, то тогда и только тогда использовать Си и/или ассемблер?


    1. Siemargl
      12.11.2016 15:18

      >Си гораздо более гибкий чем Паскаль, и даже турбо, и даже Дельфи.
      В целом, это неверное утверждение.

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


      1. third112
        12.11.2016 15:51

        Использование С/С++ тоже не всегда способствует экономии ресурсов.
        Нпр., недаром появление общедоступных многоядерных CPU вызвало появление TBB, которое не очень экономично по скорости.


        1. 0xd34df00d
          12.11.2016 20:32

          Потому что TBB вынуждено быть универсальным паттерном, а в отдельных приложениях специальные паттерны могут быть быстрее.


      1. polifill
        12.11.2016 16:25
        +2

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

        Экономика — она прагматична.
        Она выбирает более дешевый путь.

        В стародавние времена — когда железо было несопоставимо с трудом программистов дорогое — тогда и были нужны все эти трюки, край как нужны.


        1. Siemargl
          12.11.2016 19:22

          А не потому ли, что экономике выгоднее _продать_ еще раз зайца в новой одежке?


          1. polifill
            12.11.2016 20:43

            При этом лично ты как программист получаешь деньги. И профессия программиста не самая низкооплачиваемая.

            Так что это выгодно тебе как программисту. Выгодно лично тебе.
            А не какой-то там абстрактной экономике.


            1. Siemargl
              12.11.2016 22:34

              Извини, я не программист. Про экономику ты начал


        1. Antervis
          12.11.2016 20:29

          а теперь представьте лишние затраты на электроэнергию, связанные с неэффективными алгоритмами.


          1. polifill
            12.11.2016 20:48

            Ты не понял что такое «практичная экономика»

            Голые абсолютные цифры — не важны. Какими бы страшными они не были.

            Имеют значение только цифры в сравнении: «если мы пойдем таким путем — будут такие затраты, а если мы пойдем другим путем — будут иные затраты.»

            Если бы затраты электроэнергии были бы выше, чем зарплата программистов — программистов бы заставили оптимизировать.

            В этом и смысл саморегуляции в экономике.


            1. ZyXI
              12.11.2016 23:59

              Если бы затраты электроэнергии были бы выше, чем зарплата программистов — программистов бы заставили оптимизировать.

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


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


      1. pengyou
        12.11.2016 17:50
        -3

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

        Так и будете дальше жонглировать символами пунктуации, пока с одной стороны нормальные люди напишут vhdl-схему и скомпилируют rt-код прямо в железо, а с другой стороны толпа юннатов напишет за три дня такой интерфейс для юзеров, что вам и не снилось, ни по срокам ни по качеству.

        А пока в вашем т.н. простом языке т.н. программирования ловят undefined behavior светодиодами, на других простых языках, типа Оберона пишут операционные системы для того чипа, что чуть выше описали в vhdl-схеме таким кодом и с такими подходами, которые понятны пятикласснику. Страшовато, если пятиклассники поймут, что никакой магии нет.

        О боже мой, в мире столько архитектур, и только язык Си может покрыть их все? Да хер там, нет никакого языка Си, нет никакой обещанной кроссплатформенности, есть только лицемерие и затыкание ртов. Жаль, что эволюции в ИТ нет, и она не отправила вас в Верхнюю Тундру.


        1. Siemargl
          12.11.2016 19:18

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

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

          >на других простых языках, типа Оберона…
          На Обероне и прочих языках (c#, d, rust,...) вполне себе можно писать системы — см.выше про Паскаль. Но пока что мы все пользуемся теми, что написаны на С.

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

          >Жаль, что эволюции в ИТ нет, и она не отправила вас в Верхнюю Тундру
          Я там был, там нефть и газ добывают. И софт там совсем не на Яве ))))


  1. Khasuist
    12.11.2016 16:58
    -1

    Подавляющее большинство любителей сишарперов и джав НЕ представляют себе что делает в компе их программа в действительности. Доказательство — малое количество неглючных программ на этих языках. Зато быстро в разработке, пусть и жрёт ресурсы — не свои же они, а пользователей… Вы только представьте себе, сколько электричества ВПУСТУЮ сжирает каждый запуск долбаного софта на сишарпе (компиляция псевдокода) в масштабах планеты!!!
    А пример кода от Яндекс по ссылке для компиляции… вы же не знаете наверняка, что код не был написан СПЕЦИАЛЬНО так, чтобы не компилироваться где попало. Или есть доказательство обратного? Нету — значит пример совершенно не подходит. А в результате — все выводы на этом примере построенные — ошибочны.


    1. polifill
      12.11.2016 17:54

      По Яндексу:

      Это ПО управления кластером контейнеров. В этих контейнерах запускается много чего уже прикладного яндексовского.

      Работает это нормально с какой-то из несвежих версий Ubuntu (не помню с какой точно).

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

      Вы утверждаете, что яндексовцы намерено заложили невозможность эксплуатации своего кластера на обновленных версиях все той же Ubuntu? Зачем?


      1. Antervis
        12.11.2016 18:08

        при чем тут вообще ubuntu? На запускаемость программ на с++ на линуксе влияют в первую очередь версия libstdc++, во вторую — версия ядра.


        1. polifill
          12.11.2016 18:32

          Речь о https://github.com/cocaine/cocaine-core

          Вы не в теме совершенно, если считаете, что сложный софт имеет столь незначительную зависимость как libstdc++.

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

          Хорош теоретизировать — скомпилируйте хотя бы на свежем Debian, он ближайший родственник Ubuntu.


          1. Antervis
            12.11.2016 20:54

            Речь о том, что от языка (точнее gcc реализации) зависит только libstdc++, которая уже 10 лет как backward compatible, всё остальное — стороннее ПО. Не компилится код яндекса или еще чей-то там? Язык то тут при чем?


            1. polifill
              12.11.2016 21:00

              Под языком я подразумеваю всю инфраструктуру использования языка — включая библиотеки и компиляторы и синтаксис и различные стандарты…

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

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

              Борьба со своим собственным инструментом — вот это и плохо.

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

              Если интересно почему именно, то попробуйте самостоятельно скопилировать.

              Меня не нужно спрашивать — я уже не помню точно на чем оно спотыкалось. Но помню, что не в одном и даже не в трех местах проблемы. Проблем непереносимости (даже на соседнюю версию Ubuntu) там — множество.


              1. iCpu
                13.11.2016 00:03
                +1

                Пардонте, вы выше голосовали за python, java и иже с ними, но… А как там зоопарк у питона? Я уже могу скачать .py файл и запустить его, или мне нужно проверить, это второй или третий? Как там у интерпретаторов второго фланга с совместимостью? Я могу скачать openBLAS и запустить его на любом интерпретаторе? Нет?
                А как там у Java дела, оракловские машины виртуальные не сильно расходятся показаниях? Классы на PC и Android ведут себя одинаково?
                А голанг, он уже вышел за приделы уютных x86-64 в народ? Он уже породил собственные системы окружения, или всё ещё «хороший-симпатичный», но чуть что — линкует плюсовые библиотеки и такой весь не при делах? «Это не мои ошибки, я не причём!»

                Вы меня прям убиваете своим портированием. Назовите мне способ написать независимую от операционной системы прослойку для обращения к функционалу, специфичному для конкретной системы? Нет его.
                На GoLang ещё не написали своих WxWidgets или Qt, а как только напишут, появятся такие же возмущения, что программа не собирается на разных версиях ОС, что ведёт себя криво и тп. Либо так, либо он навсегда останется любимой принцессой фриков, на которую можно только вожделенно смотреть, и которой из-за этого суждено умереть девственницей.


              1. Antervis
                13.11.2016 00:34

                Без конкретных примеров сложно даже понять в чем суть проблемы. Обычно когда я пытаюсь написать что-то непереносимое, это начинается либо с -std=gnu++11/14, либо с #include <windows.h>


          1. jcmvbkbc
            13.11.2016 05:28
            +1

            Хорош теоретизировать — скомпилируйте хотя бы на свежем Debian, он ближайший родственник Ubuntu.

            Скомпилировал интереса ради, Debian 8 stable. Заняло 10 минут — 3 на то чтобы установить недостающие хедеры, 7 на то чтобы собственно скомпилировать, откачав по пути в swap 2Г данных из памяти.
            Лог сборки
            [0][jcmvbkbc@octofox build]$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=`pwd`/root…
            — The C compiler identification is GNU 4.9.2
            — The CXX compiler identification is GNU 4.9.2
            — Check for working C compiler: /usr/bin/cc
            — Check for working C compiler: /usr/bin/cc — works
            — Detecting C compiler ABI info
            — Detecting C compiler ABI info — done
            — Check for working CXX compiler: /usr/bin/c++
            — Check for working CXX compiler: /usr/bin/c++ — works
            — Detecting CXX compiler ABI info
            — Detecting CXX compiler ABI info — done
            — Found libbfd: /usr/lib/libbfd.so
            — Boost version: 1.55.0
            — Found the following Boost libraries:
            — filesystem
            — program_options
            — system
            — thread
            — Found libltdl: /usr/lib/x86_64-linux-gnu/libltdl.so
            — Found libmsgpack: /usr/lib/libmsgpack.so
            — Found libmhash: /usr/lib/x86_64-linux-gnu/libmhash.so
            — Found libuuid: /usr/lib/x86_64-linux-gnu/libuuid.so
            — Performing Test HAVE_CXX_FLAG_STD_CXX11
            — Performing Test HAVE_CXX_FLAG_STD_CXX11 — Success
            — Performing Test HAVE_CXX_FLAG_WALL
            — Performing Test HAVE_CXX_FLAG_WALL — Success
            — Performing Test HAVE_CXX_FLAG_WEXTRA
            — Performing Test HAVE_CXX_FLAG_WEXTRA — Success
            — Performing Test HAVE_CXX_FLAG_WERROR
            — Performing Test HAVE_CXX_FLAG_WERROR — Success
            — Performing Test HAVE_CXX_FLAG_WOVERLOADED_VIRTUAL
            — Performing Test HAVE_CXX_FLAG_WOVERLOADED_VIRTUAL — Success
            — Performing Test HAVE_CXX_FLAG_PEDANTIC
            — Performing Test HAVE_CXX_FLAG_PEDANTIC — Success
            — Performing Test HAVE_CXX_FLAG_PEDANTIC_ERRORS
            — Performing Test HAVE_CXX_FLAG_PEDANTIC_ERRORS — Success
            — Configuring done
            — Generating done
            — Build files have been written to: /home/jcmvbkbc/tmp/toster/cocaine/cocaine-core/build
            [0][jcmvbkbc@octofox build]$ time nice make
            Scanning dependencies of target cocaine-core
            [ 3%] Building CXX object CMakeFiles/cocaine-core.dir/src/actor.cpp.o
            [ 6%] Building CXX object CMakeFiles/cocaine-core.dir/src/actor_unix.cpp.o
            [ 9%] Building CXX object CMakeFiles/cocaine-core.dir/src/api.cpp.o
            [ 12%] Building CXX object CMakeFiles/cocaine-core.dir/src/chamber.cpp.o
            [ 16%] Building CXX object CMakeFiles/cocaine-core.dir/src/cluster/multicast.cpp.o
            [ 19%] Building CXX object CMakeFiles/cocaine-core.dir/src/cluster/predefine.cpp.o
            [ 22%] Building CXX object CMakeFiles/cocaine-core.dir/src/context.cpp.o
            [ 25%] Building CXX object CMakeFiles/cocaine-core.dir/src/context/config.cpp.o
            [ 29%] Building CXX object CMakeFiles/cocaine-core.dir/src/context/mapper.cpp.o
            [ 32%] Building CXX object CMakeFiles/cocaine-core.dir/src/crypto.cpp.o
            [ 35%] Building CXX object CMakeFiles/cocaine-core.dir/src/defaults.cpp.o
            [ 38%] Building CXX object CMakeFiles/cocaine-core.dir/src/dispatch.cpp.o
            [ 41%] Building CXX object CMakeFiles/cocaine-core.dir/src/dynamic.cpp.o
            [ 45%] Building CXX object CMakeFiles/cocaine-core.dir/src/engine.cpp.o
            [ 48%] Building CXX object CMakeFiles/cocaine-core.dir/src/errors.cpp.o
            [ 51%] Building CXX object CMakeFiles/cocaine-core.dir/src/essentials.cpp.o
            [ 54%] Building CXX object CMakeFiles/cocaine-core.dir/src/gateway/adhoc.cpp.o
            [ 58%] Building CXX object CMakeFiles/cocaine-core.dir/src/header.cpp.o
            [ 61%] Building CXX object CMakeFiles/cocaine-core.dir/src/logging.cpp.o
            [ 64%] Building CXX object CMakeFiles/cocaine-core.dir/src/repository.cpp.o
            [ 67%] Building CXX object CMakeFiles/cocaine-core.dir/src/service/locator.cpp.o
            [ 70%] Building CXX object CMakeFiles/cocaine-core.dir/src/service/locator/routing.cpp.o
            [ 74%] Building CXX object CMakeFiles/cocaine-core.dir/src/service/logging.cpp.o
            [ 77%] Building CXX object CMakeFiles/cocaine-core.dir/src/service/storage.cpp.o
            [ 80%] Building CXX object CMakeFiles/cocaine-core.dir/src/session.cpp.o
            [ 83%] Building CXX object CMakeFiles/cocaine-core.dir/src/storage/files.cpp.o
            [ 87%] Building CXX object CMakeFiles/cocaine-core.dir/src/trace.cpp.o
            [ 90%] Building CXX object CMakeFiles/cocaine-core.dir/src/unique_id.cpp.o
            Linking CXX shared library libcocaine-core.so
            [ 90%] Built target cocaine-core
            Scanning dependencies of target cocaine-runtime
            [ 93%] Building CXX object CMakeFiles/cocaine-runtime.dir/src/runtime/logging.cpp.o
            [ 96%] Building CXX object CMakeFiles/cocaine-runtime.dir/src/runtime/pid_file.cpp.o
            [100%] Building CXX object CMakeFiles/cocaine-runtime.dir/src/runtime/runtime.cpp.o
            Linking CXX executable cocaine-runtime
            [100%] Built target cocaine-runtime

            real 7m43.287s
            user 5m54.896s
            sys 0m14.752s
            [0][jcmvbkbc@octofox build]$ uname -a
            Linux octofox.metropolis 3.16.0-4-amd64 #1 SMP Debian 3.16.36-1+deb8u2 (2016-10-19) x86_64 GNU/Linux
            [0][jcmvbkbc@octofox build]$ cat /etc/debian_version
            8.6


      1. Khasuist
        12.11.2016 19:29

        Я утверждаю, что я не знаю мотивов яндексовцев на то или иное действие, а по сему допускаю возможность. И откуда вы знаете, что у них работает именно этот код? Слишком много вариантов! Вы же ни того ни другого не знаете, и не докажете. А причин может быть гораздо больше.


        1. polifill
          12.11.2016 20:54

          Я просто спросил знакомых ребят из Яндекса, они как раз имеют касательство к эксплуатации этого кода.

          У них работает именно этот код. И именно на Ubuntu (версию уже не помню).

          Завязывай беспочвенно фантазировать — твое бесполезное словоблудие не интересно.


    1. lair
      13.11.2016 13:34

      Вы только представьте себе, сколько электричества ВПУСТУЮ сжирает каждый запуск долбаного софта на сишарпе (компиляция псевдокода) в масштабах планеты!!!

      Сколько?


      (да, кстати, а сколько электричества впустую жрет компиляция никогда не использованных методов в языках без JIT? сколько теряется на недостаточной оптимизации без знания call path?)


  1. Shamov
    12.11.2016 17:53

    По ходу я лучше всех знаю Cи. Прочитав только первый вопрос, я уже знал, что на все вопросы будет ответ «не знаю». Остальные вопросы я прочитал просто из любопытства.


  1. Antervis
    12.11.2016 18:11

    насколько я знаю, размер char фиксирован стандартом, а int'ом с размером 4 может быть литерал типа 'a' в си. (не с++)


    1. TheCalligrapher
      13.11.2016 18:37

      Ширина типа 'char' — то есть количество значащих битов в нем — не оговаривается стандартом языка. Результат же 'sizeof(char)' — гарантированно 1. То есть тип 'char' — это единица измерения размеров всех остальных типов, это «байт» в понимании языка С. Однако диапазон значений этого типа не фиксирован.


      1. ZyXI
        13.11.2016 20:06

        Оговаривается минимальное значение бит: во?первых, char должен вмещать все символы из основного набора, при чём все эти символы должны представляться положительными числами независимо от знака char (C99, 6.2.5/3). Во?вторых, размер типа в битах должен быть не меньше 8 (C99, 5.2.4.2.1).


        Также в 5.2.4.2.1 оговаривается минимальный диапазон значений: [-127, 127] для signed char, [0, 255] для unsigned char; границы диапазонов допустимо раздвигать, но не сужать.


  1. FGV
    12.11.2016 18:26
    +1

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


    1. Antervis
      13.11.2016 00:36

      осмысленное применение ub? Я даже не могу представить почему это звучит сомнительно…


    1. iCpu
      13.11.2016 08:07

      main(){return 3[«Нет»];}


    1. TheCalligrapher
      13.11.2016 21:13
      -1

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


      1. FGV
        15.11.2016 16:31

        Проведу аналогию с электроникой. В книжке «Искусство схемотехники» после описания соответствующего элемента приводится два набора примеров «Удачные схемы» и «Негодные схемы». И те и те отражают «возможности» и самое главное — реализуют некую задумку (подписано что эта схема делает и для чего предназначена), только вторые показывают как делать не надо.
        А тут смысла применения конструкций подобных 1-5 вообще не видно.


        1. TheCalligrapher
          15.11.2016 20:16
          -1

          Вы по прежнему не понимаете, о чем речь. Конструкции 1-5 в таком чистом виде не имеют никакого применения. Они лишь демонстрируют важные фундаментальные факты и языке. На эти фундаментальные факты опираются все программы на С без исключения. Это не "схемы", удачные или неудачные, это иллюстрации Закона Ома.


  1. ZyXI
    12.11.2016 18:29
    +1

    Относительно задания с


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

    : здесь не только уже обсуждённые проблемы вроде


    • Не определённый размер типа в битах.
    • Не определённый в стандарте код пробела (Vim до сих пор содержит код, который призван компилироваться в системе с EBCDIC в качестве кодировки, и там точно есть такая проблема).

    Есть ещё одна: main() может и имеет тип int, но реально возвращается не он, а uint8_t. По крайней мере, в linux:


    echo $'main() {return (-96);}' | tcc -run - ; echo $?

    покажет 160. Поэтому ещё возникает вопрос к смыслу задания: что имеется ввиду под «значением возврата»? Стандарт (C99) по этому поводу говорит что, во?первых, возврат из main() эквивалентен вызову exit(); во?вторых, что вызов exit() с нулём или EXIT_SUCCESS вызывает определённую реализацией (implementation-defined) форму завершения со статусом «успех», вызов с EXIT_FAILURE — определённую реализацией форму завершения со статусом «провал», а вызов с любым другим аргументом вызывает завершение со статусом, определяемым реализацией. Т.е. если под «возвращаемым значением» имеется ввиду что?либо отличное от «значения аргумента return», то ответ на все вопросы, где это значение аргумент не 0, EXIT_SUCCESS или EXIT_FAILURE (последние два — только и исключительно в виде макросов либо каких?либо выражений, получающих значение этих макросов, а не конкретных значений) — «я не знаю».


  1. deviant_9
    13.11.2016 09:19

    Третий вопрос полностью о темных углах. Начиная с того, что ни переполнения integer


    Переполнение возникнет лишь если результат умножения не помещается в int.

    Преобразование между целочисленными типами данных (в данном случае от int к char) — немного другая статья. Если результирующий тип — беззнаковый, то гарантируется модулярная арифметика по модулю 2^<число бит>; в противном случае результат будет implementation-defined (в поздних стандартах на этот случай появилась фраза "...or an implementation-defined signal is raised", но это всё равно не undefined behavior).

    Более того, размер типа char в битах не определен. Существовали платформы где он был по 6 бит (помните триграфы?) и существуют платформы где все пять целочисленных типов по 32 бита.


    Стандарт требует, чтобы CHAR_BIT был не меньше 8 бит.


  1. TheCalligrapher
    13.11.2016 18:33

    В первую очередь стоит заметить, что язык С уже давно не поддерживает объявлений функций без явного указания типа возвращаемого значения. Правило неявного int было отменено ещев 1999 году. Поэтому использованное в примерах 'main ()' без явного 'int' — это уже не С.


    1. iCpu
      14.11.2016 06:13

      https://ideone.com/mCJZ2r


      1. TheCalligrapher
        14.11.2016 06:20

        Напоминаю, что мы говорим про язык С.

        Во-первых, язык С для компилятора GCC, который скрывается за ideone, начинается с флагов `-std=...` и `-pedantic-errors`. Без этих флагов компилятор GCC является компилятором развеселого студенческого языка для пивных вечеринок, никакого отношения к С не имеющего.

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

        Научитесь пользоваться хотя бы coliru

        http://coliru.stacked-crooked.com/a/aef58ce98b518d02

        Существуют и другие уважаемые онлайновые фронтэнды. Но не ideone.


        1. iCpu
          14.11.2016 06:23
          +1

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


        1. Siemargl
          14.11.2016 11:00

          Новее -std=c90 модификация языка это уже избыточные финтифлюшки, а в C90 еще можно не объявлять тип.

          Вот С++ изначально ответственнее относится к контролю типов.


          1. TheCalligrapher
            14.11.2016 11:19

            Во-первых, ни в коем случае. Язык С — это язык не ниже С99. Формально С11 — нынешний стандарт, но его можно назвать достаточно минорной модификацией С99 (ака "финтифлюшками"). Но С99 обсуждению не подлежит.


            Во-вторых, как уже говорил выше, контроль типов всегда был строго идентичным в С и С++, за исключением возможности неявной конверсии из void * в С. Да, современный С++ усилил контроль за конверсиями (за счет запрета narrowing conversions в ряде контекстов), но это ведь не то, что вы имели в виду, правда?


  1. TheCalligrapher
    13.11.2016 19:41
    +1

    "Существовали платформы где он был по 6 бит (помните триграфы?)..." — это замечание, конечно, совершенно "мимо кассы". Особенности символьного набора (execution character set) конкретной платформы формально влияют на размет типа 'char' только тем, что обязаны в него помещаться. Не более того.


    Даже в стариннейшем "C Reference Manual" тип 'char' уже постулировался, как знаковый 8-битный тип в формате 2's-complement. А дальше его спецификация становилась лишь еще более абстрактной и свободной.


  1. Sun-ami
    14.11.2016 12:05
    -1

    C и C++ действительно имеют довольно много элементов, порождающих межплатформенную несовместимость, наличие которых не оправдано с точки зрения оптимизации. Если платформозависимый размер int еще оправдан, когда заведомо достаточно 8 бит, то short и long только привносят несовместимость. Выход из этой ситуации на мой взгляд — не переход на новые языки, а эволюция C++. Нужно добавлять новые конструкции, имеющие более четко определенное поведение на всех платфотмах, а старые объявлять не рекомендованными к применению, оставленными только для совместимости. Например, вместо int, short, long и long long, (а во многих случаях и char), ввести int, где n-гарантированное минимальное число бит в платформозависимом типе — поведение станет детерминированным, а поле для оптимизации только увеличится. Вместо int8_t, int16_t и т.д. — ввести похожую конструкцию, но с чётко фиксированным размером в битах. Способ упаковки структур и классов лучше рекомендовать явно указывать в определении — например struct<pack,1> — упакованная по границе байта, struct<pack,1b> — упакованная по границе бита, struct — не упакованная, но с сохранением порядка полей, struct — компилятор может изменять порядок полей, например заполняя пробелы, оставленные при выравнивании. Объединив первое и второе получим новый, более прозрачный способ объявления битовых полей в структуре — битовые поля не привязанные к границам байтов, что может быть полезно при передаче данных, и при этом не так уж затратно.
    Что касается использования C вместо С++ — если для платформы доступен компилятор C++ — я не вижу в этом смысла, лучше выделить некоторые подмножества С++, такие как например, Embedded C++, или более близкие к С, но позволяющие уйти от #define, а в случае необходимости — перейти к более расширенному подмножеству.


    1. Sun-ami
      14.11.2016 12:29

      Проблемы с форматированием:
      — вместо int, short, long и long long ввести int<«N»>где «N»-гарантированное минимальное число бит в платформозависимом типе;
      — не упакованная структура с сохранением порядка полей:

      struct<ordered>
      

      — структура, в которой компилятор может изменять порядок полей:
      struct<auto>
      


      1. deviant_9
        14.11.2016 22:34
        +1

        вместо int, short, long и long long ввести int<«N»>где «N»-гарантированное минимальное число бит в платформозависимом типе;


        Во-первых, гарантии там и так есть (исходя из <limits.h>):
        • char, signed char, unsigned char — не меньше 8 бит;
        • short, unsigned short, int, unsigned int — не меньше 16 бит;
        • long, unsigned long — не меньше 32 бит;
        • long long, unsigned long long (since C99) — не меньше 64 бит.


        Во-вторых, начиная с C99 есть <stdint.h> с его [u]int_least<N>_t и [u]int_fast<N>_t (соответственно «самые маленькие» и «самые быстрые» типы с минимальной заданной битностью).

        Вместо int8_t, int16_t и т.д. — ввести похожую конструкцию, но с чётко фиксированным размером в битах.


        У них-то как раз фиксированные размеры — ровно столько значащих бит, сколько указано. Более того, для них гарантируется также:
        • отсутствие битов-заполнителей;
        • кодирование при помощи двоичного дополнительного кода (в то время как для остальных знаковых типов допускается любая pure binary system — как минимум прямой код и обратный код, а не только двоичный дополнительный).

        Нефиксированным остаётся лишь порядок байтов.

        Если платформозависимый размер int еще оправдан


        На практике он, как ни странно, оказался относительно платформонезависимым — 32 бита что на 32-битных, что на 64-битных платформах:) В отличие от long, который (в беззнаковом варианте), например, в ядре Linux используется чуть ли не как синоним void*.


        1. Sun-ami
          15.11.2016 12:58

          >гарантии там и так есть
          Это, конечно, хорошо, но половинчато и не универсально. В некоторых процессорах (DSP) промежуточные результаты вычислений хранятся в регистрах размером 18, 20, 24 или 48 бит. В компиляторах для них есть соответствующие типы, но с переносимостью кода есть проблемы. Для 8-битных микроконтроллеров были бы полезны типы с разрядностью 24 и 40 бит. А кое-где уже поддерживается 128-разрядная архитектура. Вместо того чтобы каждый раз менять стандарт, лучше 1 раз ввести универсальную конструкцию, её поддержка компиляторами потребует не слишком много усилий.


        1. Sun-ami
          15.11.2016 19:34

          >>Вместо int8_t, int16_t и т.д. — ввести похожую конструкцию, но с чётко фиксированным размером в битах
          >У них-то как раз фиксированные размеры
          Я имел в виду конструкцию, похожую на предложенную мной выше int<«N»>, с произвольной фиксированной разрядностью. Например int<16f>, int<18f>. А за int_fast_t спасибо, не знал, буду использовать.