Просьба не читать профессиональным Си/ С++ программистам).

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

Что ж, самое время разжечь холивар.

Я думаю все в курсе, что в курсе что в C++ ужасные строки. Особенно, если мы говорим о старом типе, в новом string многое было исправлено и улучшено, но до сих пор нет поддержки юникода(!).

В стандарте C++ 20 вроде как собираются вводить unicode строки.

С++ 20! И это несмотря на то, что С++ существует с 1983 года.

Откроем вашу любимую IDE и попробуем скомпилировать следующий код:

#include <iostream>
#include <cstdio>
 
int main()
{
  char string [256];
  std::cout << "Привет: ";
  gets(string); 
  std::cout << "Вывод: " << string;
  return 0;
}

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

Я компилировал в Dev Cpp, компилятор GCC.

Скомпилируем и видим:



Хороший вывод на экран, да?

А теперь давайте заменим char string[256] на char* string.

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

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

Вот еще прикол:

#include <iostream>
using namespace std;
int main(){
    int arr[100]={};
    cout<<arr[101]<<endl;
    return 0;
}

Что мы ожидаем? Компилятор скажет нам, что нельзя обратиться к 101 элементу массива, раз элементов всего 100. Но компилируем, запускаем и видим… 32765( по крайней мере на моем железе).

Мда.

А теперь давайте протестируем вот этот код:

int i = 5;
i = ++i + ++i;
std::cout<<i;

Как вы думаете, что он выведет?

Правильный ответ — зависит от компилятора.

В GCC это будет 14, но в зависимости от флагов оптимизации.

А в другом компиляторе это легко может быть 12…

Думаю, все знают что в С и в плюсиках куча синтаксического сахара, который далеко не всегда нужен.

Например std::cout<<4["string"]; это валидный код
Он выводит n, так же как и std::cout<<"string"[4];

Здорово, да?

А теперь о больном.
С++ и сеть.
Это 2 ооочень плохо состыкующихся понятия.
Попробуйте просто скачать изображение котика с Вашего любимого сайта с помощью стандартной библиотеки C++.
Это было невозможно до принятия стандарта С++ 17.
В той же стандартной библиотеки нельзя работать с JSON.
Отличный комментарий по этому поводу.
В целом работа с JSON в C++ сродни кошмару.
Источник.
Думаете, условие всегда будет ложно?

if(sizeof ('a') != sizeof (char)){
//do something
}

Нет, Вы ошибаетесь.

Если скомпилировать это как с++ проект то условие скорее всего не выполнится.
Не должно.[1]
А если как Си проект, то в таком случае sizeof('a')==sizeof(int).
Вот такие дела.
[1] Вообще множество разных компиляторов C и C++ это тоже проблема.
Потому что множество решений нестандартизовано и они будут работать только в определенных компиляторах.

Например, 128 битные числа в C++. В gcc и clang есть тип __int128, в то время как в Visual Studio его нет, потому что он не является стандартом. Или, например, строки в Visual Studio.

String^ MyString3 = "Hello, world!"; //попробуйте скомпилировать в GCC

Или, например, в старичке Borland C++ Builder можно код, написанный на Object Pascal.
И таких моментов много.

Особую боль вызывает отсутствие списка пакетов Си и С++.

Что из этого следует? Используйте новую версию C++, например С++ 17 и некоторые проблемы будут решены.

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

А какие проблемы С и С++ знаете Вы?
Пишите в комментариях.

UPD: похоже многие люди неправильно поняли мою статью:
Я ни в коем случаи не хочу раскритиковать си/с++ и сказать пишите на расте.
Просто указываю на недостатки с/с++, т.к они немного достали самого.

У всего есть свои минусы, я просто поделился мыслями.
UPD2:

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

Просто в С/С++ полно легаси решений, которые никто не исправит, потому что это может сломать обратную совместимость.

И да, это мое мнение, оно субъективно.

Если Вы не согласны — лучше прокомментируйте, а не тупо минусуйте, потому что мнение всех нас
субъективно.

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


  1. snuk182
    10.12.2018 19:32
    +19

    Как-то очень толсто даже для нелюбителей си.


    1. Enmar Автор
      10.12.2018 19:34
      -8

      В смысле?


      1. snuk182
        10.12.2018 19:53
        +20

        В самом прямом.

        Требование поддержки JSON в стандартной библиотеке — апофеоз глупости для низкоуровневого языка. Обращение к символам юникодной строки по индексу, равно как определение ее длины по размеру массива — это позор. Наезд на sizeof() — это вообще демонстрация полного непонимания работы платформозависимых типов. Пожалуйста, учите матчасть перед написанием поста.

        По теме. Оба языка достаточно старые, с кучей легаси. Тот факт, что они живы и активны до сих пор, говорит о том, что для этого есть веские причины. Работа с юникодом из коробки — для этих языков далеко не самое важное. Если новый проект в 2019 году стартует на С/С++ — это как минимум понимание бизнес-целей, рисков и стоимости разработки. Я сам отношусь к людям, которые не против видеть Rust в списке технологий разработки вместо C, но могу сказать, что вышеописанным вы оказываете Rust медвежью услугу. Равно как и то, что эти два мамонта из своих ниш выбить крайне маловероятно, и в принципе не очень разумная затея.


        1. Enmar Автор
          10.12.2018 20:10
          -6

          Вот здесь с Вами не согласен.

          Наезд на sizeof() — это вообще демонстрация полного непонимания работы платформозависимых типов.

          Я понимаю, что размер int разный от платформы к платформе.
          Но странно, что в си компиляторе это воспринимается так, а в ++ компиляторе по-другому.
          Равно как и то, что эти два мамонта из своих ниш выбить крайне маловероятно

          Здесь я с Вами согласен.
          Ядро юникса и винды например на сях.


          1. snuk182
            10.12.2018 20:12
            +3

            странно, что в си компиляторе это воспринимается так, а в ++ компиляторе по-другому.

            Потому что это два разных языка.


            1. Enmar Автор
              10.12.2018 20:59
              -8

              Я знаю, что С++ не является разновидностью Си.
              Но несовместимости между 2 этими языками все равно зло.


              1. RussDragon
                10.12.2018 23:38
                +1

                > Я знаю, что С++ не является разновидностью Си
                > несовместимости между 2 этими языками все равно зло
                Видимо не знаете.


            1. NSA
              11.12.2018 01:57
              +1

              Но они же платформозавсимые типы, а не языкозависимые типы ;)


          1. Fails
            10.12.2018 20:15
            +6

            Советую вам почитать книгу Бьярне Страуструпа «Дизайн и Эволюция C++», в которой он поясняет, почему он сделал именно так (а также такие вещи вида «почему для доступа к статическим членам данных используется два двоеточия (::) а не точка и другие интересные вещи).
            Также хочется сказать, чтобы вы никогда не использовали функцию gets(), которая приводит к повреждению памяти при некорректном вводе (если он будет больше, чем размер области памяти, куда вы пишете). Эту функцию удалили из C++14 и из C11 (см. cppreference: en.cppreference.com/w/c/io/gets).


            1. Enmar Автор
              10.12.2018 22:34
              -1

              Судя по всему вы не так меня поняли, раз приводите в пример ::
              Какая разница :: или .?
              Никакой.
              А вот когда компилятор молчит когда не надо, это плохо...


              1. justhabrauser
                11.12.2018 08:38
                +1

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


              1. dlinyj
                11.12.2018 09:28

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

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


  1. Jouretz
    10.12.2018 20:04

    int i = 5;
    i = ++i + ++i;
    std::cout<<i;

    И что конкретно тут удивительного?
    Преинкремент имеет более высокий приоритет чем сложение, и складывается результат.
    ++i в ячейке памяти — 6
    ++i в ячейке памяти — 7
    7+7 = 14.
    В ассемблер оно переводится как

    movl $5, -4(%rbp)
    addl $1, -4(%rbp)
    addl $1, -4(%rbp)
    sall -4(%rbp)

    Компилятор провёл лёгкую оптимизацию за нерадивым программистом и вместо сложения числа с самим собой просто умножил его на два.


    1. Enmar Автор
      10.12.2018 20:19
      -4

      Я это почерпнул из какой-то статьи на хабре.
      Вот из этой кажется:
      habr.com/post/88185


    1. Amomum
      10.12.2018 21:05
      +4

      Это неопределенное поведение, результат может быть вообще любым.


    1. dlinyj
      11.12.2018 09:28

      Это архитектурно зависимо


  1. Tyiler
    10.12.2018 20:11
    +9

    смысл статьи в чем?
    кто вы такой, сколько у вас лет опыта C/C++? — ваше мнение релевантно?

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

    так понимаю очередное проталкивание Rust.
    я вот, например, после таких статей начинаю от него воротить нос, и желание пропадает даже смотреть что там у вас творится в Rust.


    1. Enmar Автор
      10.12.2018 20:13
      -5

      Я ни в коем случаи не хочу раскритиковать си/с++ и сказать пишите на расте.
      Просто указываю на недостатки с/с++, т.к они немного достали самого)


      1. picul
        10.12.2018 20:41

        Вы указываете на особенности языка, не считая нескольких откровенно бредовых выражений (типа «строки в Visual Studio»). А то что это недостатки — это Ваше личное мнение.


      1. besitzeruf
        11.12.2018 02:02
        +1

        Помоему эти «недостатки» являются следствием Вашей некомпетентности.


    1. TargetSan
      10.12.2018 22:57
      +1

      Настолько топорное проталкивание? Сомневаюсь.


    1. RussDragon
      10.12.2018 23:39

      Вообще, о Расте в последнее время и правда много хорошего слышу. Но никак не дойдут руки его посмотреть :(


  1. BkmzSpb
    10.12.2018 20:15
    +2

    String^ MyString3 = «Hello, world!»; //попробуйте скомпилировать в GCC

    А вы в курсе, что это C++/CLI? Ну т.е. это полу-управляемый C++, живущий с .NET? И что ваш String^ это скорее всего дотнетовский System.String (когда-то давно я на этом даже что-то писал, но могу ошибаться)? Более того, у VC++ есть свои инструменты для работы со строками, как часть winapi, wchar и tchar, первый из которых 16-битовый тип, а второй определяется в зависимости от определенного символа #ifdef UNICODE (отсюда).


    1. Enmar Автор
      10.12.2018 20:16
      -1

      Вот и я про тоже.
      Нельзя просто взять любой c++ код и быть уверенным, что он во всех компиляторах будет работать и при этом одинаково


      1. BkmzSpb
        10.12.2018 20:19
        +3

        Да как бы это сказать… На мой взгляд C++/CLI даже плюсами назвать нельзя. Это как… компилировать CUDA без nvcc и жаловаться, что компилятор не понимает function_name<<<n, m>>>(args). Это фактиечски диалекты.


  1. valexey
    10.12.2018 20:18
    +4

    Ну, блин. Ну нельзя же так:

    Откроем вашу любимую IDE и попробуем скомпилировать следующий код:
    #include <iostream>
    #include <cstdio>
     
    int main()
    {
      char string [256];
      std::cout << "Привет: ";
      gets(string); 
      std::cout << "Вывод: " << string;
      return 0;
    }



    ok. Откроем:
    $ cat main.cc 
    #include <iostream>
    #include <cstdio>
     
    int main()
    {
        char string [256];
        std::cout << "Привет: ";
        gets(string); 
        std::cout << "Вывод: " << string;
        return 0;
    }
    

    Теперь попробуем собрать и запустить.

    Итак, во-первых компилятор жутко ругается на этот код, намекая, что он, мягко говоря, плох:
    ругань компилятора
    $ g++ main.cc
    main.cc: In function ‘int main()’:
    main.cc:8:5: warning: ‘char* gets(char*)’ is deprecated [-Wdeprecated-declarations]
         gets(string); 
         ^
    In file included from /usr/include/c++/5/cstdio:42:0,
                     from main.cc:2:
    /usr/include/stdio.h:638:14: note: declared here
     extern char *gets (char *__s) __wur __attribute_deprecated__;
                  ^
    main.cc:8:5: warning: ‘char* gets(char*)’ is deprecated [-Wdeprecated-declarations]
         gets(string); 
         ^
    In file included from /usr/include/c++/5/cstdio:42:0,
                     from main.cc:2:
    /usr/include/stdio.h:638:14: note: declared here
     extern char *gets (char *__s) __wur __attribute_deprecated__;
                  ^
    main.cc:8:16: warning: ‘char* gets(char*)’ is deprecated [-Wdeprecated-declarations]
         gets(string); 
                    ^
    In file included from /usr/include/c++/5/cstdio:42:0,
                     from main.cc:2:
    /usr/include/stdio.h:638:14: note: declared here
     extern char *gets (char *__s) __wur __attribute_deprecated__;
                  ^
    /tmp/ccmI9pjb.o: In function `main':
    main.cc:(.text+0x34): warning: the `gets' function is dangerous and should not be used.
    


    1. Enmar Автор
      10.12.2018 20:22
      -5

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


      1. valexey
        10.12.2018 20:33
        +5

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

        Поэтому критика получается слабой и не убедительной.


        1. Enmar Автор
          10.12.2018 21:01
          -2

          В чем не разобрался?
          Конкретнее пожалуйста.
          Вы говорите так работает ос.
          А я вам, что компилятор си/с++ мог бы не позволять так просто выстрелить в ногу.


          1. geher
            10.12.2018 22:30
            +1

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


          1. kjkjkljklj
            10.12.2018 22:48
            +2

            Перестаньте упоминать C и C++ через /, это два разных языка.


            1. samodum
              11.12.2018 07:16

              Странно, что он ещё C# сюда не приплёл по той же логике :)


          1. dlinyj
            11.12.2018 09:34

            Сложно человеку разъяснить уровень его некомпетентности, если он не готов даже признать то что он не компетентен.

            Как минимум прочитайте про кодировки в различных ОС. Прочитайте про разницу с и с++.

            Почему компилятор «стреляет» в ногу, так это потому что вы не умеете писать. Используйте другие языки. Кстати, бывают компиляторы с защитой. dihalt у себя на сайте давал пример.


      1. berez
        10.12.2018 20:41
        +1

        Преимущество языка С++ в том, что вас никто не заставляет использовать стандартную библиотеку. Хотите истинной кроссплатформенности — используйте кроссплатформенные библиотеки и фреймворки а-ля Qt. Там и юникод, и окошечки, и шашечки, и рюшечки, и сигналы-слоты — все есть.
        А стандартная библиотека — да, местами кривовата и недостаточно полна. Но это не потому, что язык С++ чем-то плох, а потому, что язык не навязывает какую-то одну реализацию.

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


      1. myxo
        10.12.2018 21:36
        +2

        И тут вы ошиблись. Это не «только в винде», а в вашем ненастроенной консоли. Если вы на той же ubuntu, сохраните cpp файл (а значит и вашу строку) в кодировке cp1251, и выведете в консоль с настройкой utf-8, то получите такую же ерунду (точнее не такую же, но ерунду).

        Именно об этом и писал valexey.


  1. vilgeforce
    10.12.2018 20:20

    Если автору не нравится возможность читать/писать произвольную память — это автор до такого недозрел, а не языки плохие. Возможность работать с байтами в памяти «как есть» — огромный плюс.


    1. Enmar Автор
      10.12.2018 20:24

      Это вы про arr[101]?
      Только не говорите, что это фича, я Вас прошу.
      В том же расте или в любом другом более высокоуровневым языке такого не будет.


      1. vilgeforce
        10.12.2018 20:30
        -3

        Конечно фича!
        struct foo{
        unsigned char low[128];
        unsigned char high[128];
        };

        И запись в foo.low[] с явным выходом за его границы — фича. Я в зависимости от старшего бита индекса пишу в один или в другой массивы. БЕЗ ветвлений, сравнения битов и тому подобного.


        1. geher
          10.12.2018 22:21
          +1

          Встречал еще веселее в продуктах IBM (в заголовочных файлах библиотек):
          что-то похожее на такое:


          struct foo{
            int headerField1;
            int headerField2;
            unsigned char data[0]; 
          // тут память подводит, может быть и [1], 
          // но память все равно рисует [0]
          };


          1. vilgeforce
            10.12.2018 22:30

            Ну да, вполне себе определение заголовка для данных переменной длины


          1. picul
            10.12.2018 22:38

            Все таки 1, с 0 не должно скомпилироваться.


            1. argentumbolo
              10.12.2018 22:44
              +1

              Начиная с с99 — должно.


        1. 0xd34df00d
          10.12.2018 23:18
          +1

          На самом деле это не фича. Это UB даже в этом контексте, и это может очень больно выстрелить.

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


          1. vilgeforce
            10.12.2018 23:56
            -2

            Да и ладно :-) Код будет решать задачу на конкретной (программно/)аппаратной платформе корректно и быстро, что и требовалось.


            1. 0xd34df00d
              11.12.2018 00:09
              +1

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


              А вообще, конечно, грустно это. Потом у нас Therac-25 получается, тойоты ускоряются, ещё всякая ерунда происходит. Зато на С и быстро.


          1. 0xd34df00d
            11.12.2018 00:08
            +2

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


            6.7.2.1/14: «There may be unnamed padding within a structure object, but not at its beginning.»


            То есть, как минимум, для начала непонятно, куда именно вы там пишете: никто не мешает реализации вставить произвольный padding между low и high.


            6.5.2.1/2: «The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))).»
            6.5.6/8: «When an expression that has integer type is added to or subtracted from a pointer, the
            result has the type of the pointer operand. If the pointer operand points to an element of
            an array object, and the array is large enough, the result points to an element offset from
            the original element such that the difference of the subscripts of the resulting and original
            array elements equals the integer expression.
            »
            «If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.»


            То есть, когда вы просто даже создаёте указатель внутрь high через low, то у вас уже UB, даже если вы ещё ничего никуда не успели записать или прочитать.


            Писать вне array object'а тоже UB, но это уже чуть больше цитировать надо.


            Так что поздравляю, у вас в таком простом коде три UB.


      1. myxo
        10.12.2018 21:01
        -1

        Это не баг и не фича. Просто так было раньше, никто переделывать не будет (и слава богу).
        Проблема скорее в том, что существует масса старых учебных материалов, в которых новичкам пишут использовать обычные массивы вместо std::array (ex boost::array).


        1. Enmar Автор
          10.12.2018 21:07
          -3

          Просто так было раньше и переделывать не будут.
          Это называется legacy)
          И это баг


          1. myxo
            10.12.2018 21:20

            legacy — это не баг, это объективная реальность мира. Если у вас завалялось несколько лишних десятков миллиардов $, то вы можете их потратить на переписку и тестирование всего существующего софта на с/с++. Тогда можно будет со спокойной душой убирать возможность написать std::cout<<«string»[4]; из стандарта. Оно того стоит?


      1. Alex_ME
        10.12.2018 22:35

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

        Точно так же с неопределенным поведением. Это цена производительности во многом.


        1. dev96
          10.12.2018 22:41

          Автор говорит о том, что в таком compile-time случае компилятор (а не стат. анализатор) по рукам не даст.
          Проверка в рантайме в плюсах итак включается в дебаг сборке или принудительно в релизе.
          Либо, опять-таки статический анализ.
          Хотя, теоретически, std::array во время компиляции может ругаться.


          1. darkxanter
            10.12.2018 23:11

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


            1. dev96
              10.12.2018 23:19

              Абсолютно с вами согласен.
              MSVC тоже предупредит, если настройки подкрутить.
              Причем сейчас активно начинают везде внедряться проверки «C++ Core Guidelines».


      1. Pancir
        11.12.2018 09:22

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


        // Я бы так писать не стал
        // но возможность есть, на случай например
        // использования сторонней библиотеки.
        const std::size_t arr_len = 100;
        int arr[arr_len];
        std::fore_each(arr, arr[arr_len], Sum()); 
        
        // вот так уже сильно лучше
        std::array<int, 100> arr;
        std::fore_each(arr.begin(), arr.end(), Sum()); // ошибка исключена
        // или
        for(const auto & val : arr){
            ... // ошибка исключена
        }
        // или
        ... = arr.at(200) // exception

        Если не уверены используйте метод at для доступа к элементу массива, он выкинет исключение при out of range.


        Я так понимаю с приходом constexpr вы можете сделать свою реализацию статического массива где в операторе [] можете проверить out of range на этапе компиляции и выкинуть error, не могу уверенно сказать т.к. constexpr еще не изучал подробно.


  1. valexey
    10.12.2018 20:22
    +2

    В gcc и clang есть тип __int128, в то время как в Visual Studio его нет, потому что он не является стандартом.

    А у раста вообще НИЧЕГО стандартом не является, так как стандарта на Rust НЕТ. Хотя, быть может я ошибаюсь, и у раста есть ISO стандарт? Можно узнать его номер?

    И еще — у раста ровно один компилятор. Понятно что сам с собой он, в основном, совметим. Хотя, насколько я помню, если использовать только стабильные фичи раста, то половина библиотек просто не соберется, так как они юзают «нестандартные», то есть, не стабильные расширизмы компилятора и языка. И это при одном единственном компиляторе раста!

    Что же будет, когда у раста появится хотя бы три независимые реализации?


    1. Enmar Автор
      10.12.2018 20:26
      +1

      Вроде у них нет ISO.
      Но 128 битные числа будут гарантировано в кажой раст реализации)
      Стандарт раста это доки на их сайте.
      Все что там написано будет работать в любом нормальном компиляторе раста.


      1. valexey
        10.12.2018 20:37

        А сколко их, нормальных компиляторов раста? Ну хотя бы сколько разных фронтендов у него?

        Еще раз — стандарта на Rust нет. Вообще, в мире довольно мало языков на которые есть живой актуальный стандарт. Могу назвать всего 5 штук: Си, С++, Ада, Фортран, Кобол. Все эти языки максимально дотошно описаны и стандартизированы. Стандарты на все эти языки регулярно обновляются.

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


        1. argentumbolo
          10.12.2018 21:04
          +1

          На самом деле их немало — https://en.wikipedia.org/wiki/Category:Programming_languages_with_an_ISO_standard
          Даже на Ruby есть ISO/IEC 30170


          1. valexey
            10.12.2018 21:14

            Там, к ISO стандартам, есть требование, что они должны обновляться каждые N лет (лет 8 вроде), иначе стандарт протухает. В принципе обновление стандарта на ЯП может быть чисто формальным, но оно должно быть. Поэтому например у паскаля сейчас нет ISO стандарта актуального.

            У ECMAScript'а, я смотрю, есть, а вот у Модулы-2, нет. У пролога — тоже нет. Ну и далее, по списку.

            А вот про руби не знал, спасибо. Если про ECMAScript я просто забыл, то про руби я вообще не был в курсе, что там стандарт ISO есть.


        1. a-l-e-x
          10.12.2018 21:54

          Ещё вроде SQL на доске почёта должен быть.


        1. Alex_ME
          10.12.2018 22:39

          ISO/IEC 23270:2006 — C#


        1. TargetSan
          10.12.2018 23:07
          +2

          Вы знаете, я как постоянный крестоносец смотрю на этот стандарт каждые три года, и как-то грустно становится… Зато добавили эллиптические кривые и космолёт...


          1. RussDragon
            10.12.2018 23:57

            Честно говоря, как человек, который писал 3 года на ++, я с радостью с него убежал и в последнее время с ужасом наблюдаю за тем, что добавляется в стандарт. Нет, я не готов критиковать их аргументированно и с позиции «что было бы лучше сделать», но лично меня передёргивает, когда я вижу новые трехсимвольные операторы.


            1. berez
              11.12.2018 00:11
              +1

              лично меня передёргивает, когда я вижу новые трехсимвольные операторы.

              Дайте угадаю: на перле вы никогда не писали? :)


              1. RussDragon
                11.12.2018 00:11

                Вы угадали :)


            1. 0xd34df00d
              11.12.2018 01:02

              Это всяко лучше, чем писать по 6 функций на каждую пару сравнимых типов.

              Триграфы, кстати, из C++17 удалили.


              1. TargetSan
                11.12.2018 01:32

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


                1. 0xd34df00d
                  11.12.2018 02:14

                  Общую проблему решат метаклассами и компилтайм-рефлексией, но до них ещё далеко, да.


  1. picul
    10.12.2018 20:27
    +22

    А какие проблемы С и С++ знаете Вы?
    Самая большая проблема C++ — его постоянно критикуют те, кто в нем не разбирается.


    1. Enmar Автор
      10.12.2018 20:40
      -5

      Пожалуйста, скажите в чем именно я не прав?


      1. Lofer
        10.12.2018 21:04
        +1

        но до сих пор нет поддержки юникода(!).
        Просто указываю на недостатки с/с++, т.к они немного достали самого)

        Пожалуйста, скажите в чем именно я не прав?

        Присоединюсь к предыдущим ораторам, в вопросах кроссплатформености, размеров типов, понимания ОС и т.д. и задам только один вопрос (из двух пунктов): Вы с каким юникодом собирались работать или C++ должен за Вас магическим образом догадаться и сделать всю сервисную работу, которые делает «обычный язык» программирования?


      1. picul
        10.12.2018 21:12

        Про перлы типа любимой IDE и строк в Visual Studio Вам уже сказали, в остальном — Вы не правы в том что это недостатки. Это особенности языка, которые введены не просто так, и большинство из которых до сих пор имеет смысл. То что от них отказывается Rust — это камень в огород Rust'а.


        1. Enmar Автор
          10.12.2018 21:57
          -1

          черт, скажите мне КАКИЕ ЭТО ОСОБЕННОСТИ?
          То, что предпроцессор ужасен и компилятор компилирует программу которая ломается при
          работе, проще говоря runtime error хуже compile error.


          1. picul
            10.12.2018 22:08

            Run-time error — это всегда хуже, чем compile-time, то что Вы это не понимаете — ставит под сомнение Ваш опыт программирования вообще, а не только на C++. И чем ужасен препроцессор?


          1. Lofer
            10.12.2018 22:29

            черт, скажите мне КАКИЕ ЭТО ОСОБЕННОСТИ?

            К примеру:
            1. первый вопрос: размер типов. (тупо сколько занимает в памяти и почему именно так)?
            2. второй вопрос: что такое String?
            3. третий вопрос, вытекающий их первых двух: если юникодов как грязи UTF-8, UTF-16 BE, UTF-16 LE, UTF-32 и т.д. то как С++ должен решить за Вас задачку «Сколько памяти выделить и как кодировать в памяти?» если Вы сами не понимаете что творите?


  1. MaxVetrov
    10.12.2018 20:32

    Ну вообще, это все равно что писать «Что мне не нравится в ассемблере.»

    Зачем смешивать ввод-вывод с и c++?

    #include <iostream>
    #include <cstdio>


    Используйте std::string и будет вам счастье.
    Есть груз прошлого, от него нужно избавляться.

    Нашел проблему — нет before_end(итератор на последний элемент) в forward_list, для быстрого добавления в конец односвязного списка элемента(ов). В принципе можно решать эту задачу на этапе заполнения, но корявое решение конечно же. Возможно это исправят в будущем.


    1. Enmar Автор
      10.12.2018 20:47
      -1

      Смешивать не нужно было, но в принципе это не на что не влияет)


      1. 0xd34df00d
        10.12.2018 23:23
        +1

        Влияет, потому что если вы читаете в std::string, то такой проблемы нет.


  1. NeoCode
    10.12.2018 20:38

    К сожалению, разработчики стандарта очень боятся нарушить обратную совместимость.
    А в случае со строками — правильным решением было бы вообще отвязать строки от кодировки. То есть «Hello» это строка, а кодировка определяется из настроек проекта и компилятора. А вот если требуется явно указать кодировку — то используются префиксы. Причем для однобайтовых кодировок (которые, несмотря на Unicode, иногда все-же нужны) можно было бы указывать кодировку явно. А если она указана обобщенно (т.е. префикс, означающий «текущая однобайтовая кодировка») то брать указанную в настройках проекта или компилятора. Тогда знаменитый вопрос о крякозябрах при выводе русских строк из консольных программ в винде потерял бы актуальность:)
    Примерно аналогично должно быть и с числами. Число 42 — это просто число, а его тип должен выводиться компилятором каждый раз в зависимости от контекста. Это может быть и byte, и int, и unsigned long, и double, и даже какой нибудь mpf_t из GMP.


    1. Enmar Автор
      10.12.2018 20:43

      Да, именно из-а того, что

      разработчики стандарта очень боятся нарушить обратную совместимость
      в си и в С++ много легаси решений.


      1. NeoCode
        10.12.2018 21:32

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


        1. Enmar Автор
          10.12.2018 21:41

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


          1. NeoCode
            10.12.2018 23:13

            Так это на любом языке можно. Однако стремление к созданию «абсолютно безопасного языка» в ущерб всему остальному (а иначе не получится) я не считаю правильным.


  1. myxo
    10.12.2018 21:11

    Попробуйте просто скачать изображение котика с Вашего любимого сайта с помощью стандартной библиотеки C++.
    Это было невозможно до принятия стандарта С++ 17.

    Эм… я что-то пропустил? Networking TS же вообще на с++23 отодвинули.
    С++ и сеть.
    Это 2 ооочень плохо состыкующихся понятия.

    #include <boost/asio.hpp> // можно заменить на вашу любимую библиотеку
    Не мешайте в одну кучу проблемы с++ и проблемы стандартной библиотеки


    1. Enmar Автор
      10.12.2018 21:16
      -2

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


  1. Amomum
    10.12.2018 21:12

    Большая часть вещей, на которые вы жалуетесь, исправляется библиотеками и опциями компилятора.
    На многие проблемы можно получить предупреждение, если компилировать с -Wall -Wextra. Не забываем про статические анализаторы!
    Для проверок в рантайме есть -fsanitize (который прекрасно отлавливает выходы за границы массивов) и valgrind.

    Не поленитесь изучить опции компилятора, которым вы пользуетесь.

    Под спойлером - мой набор ключей для gcc, добавьте -fsanitize по возможности:
    -Wall -Wpedantic -Wextra -Wcast-align -Wcast-qual -Wvla -Wshadow -Wsuggest-attribute=const -Wmissing-format-attribute -Wuninitialized -Winit-self -Wdouble-promotion -Wstrict-aliasing -Weffc++ -Wno-unused-local-typedefs


    1. Enmar Автор
      10.12.2018 21:19
      -4

      Я знаю про опции компилятора)
      Но согласитесь, круто когда все работает без опций.
      Это же основа, не что-то эдакое.
      Когда я в IDE компилирую проект, то очень часто я не могу задать опции компилятора.


      1. Amomum
        10.12.2018 21:21
        +2

        Но согласитесь, круто когда все работает без опций.

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

        Когда я в IDE компилирую проект, то очень часто я не могу задать опции компилятора.

        Это что у вас за IDE такая? О_о


        1. Enmar Автор
          10.12.2018 21:33
          +1

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

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


          1. Amomum
            10.12.2018 21:40

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

            И все же, что у вас за IDE такая? IDE1886 что ли?


  1. DollaR84
    10.12.2018 21:31
    +1

    >>> А теперь давайте заменим char string[256] на char* string.

    Ну знаете ли… Заменить массив символов на указатель на символ и ожидать от такой замены одинакового результата…
    Я вообще думаю, что понимание указателей и работы с ними — это мощный инструмент. И люди, пришедшие в C/C++ из других языков пока не поймут указатели — вообще не знают C/C++.


    1. Enmar Автор
      10.12.2018 21:34

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


      1. myxo
        10.12.2018 21:45
        +2

        Так он и говорит. Разве что нужен современный компилятор, который сразу ругнется на gets и опции санитайзера, который ругнется на использование невыделенной памяти. Почему это не является настройкой по-умолчанию? Потому-что Си — язык для других задач, и тем кто пришел с, например, питона, это не понятно.

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


      1. DollaR84
        11.12.2018 02:51

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


      1. darkxanter
        11.12.2018 09:49

        А теперь давайте заменим char string[256] на char* string.
        Я говорю, что компилятор должен был сказать, что мол ты фигню мне подсунул.

        clang и gcc выдает предупреждение о неинициализированной переменной.
        Нужно просто добавить вывод всех предупреждений флагами компилятора.


  1. dbagaev
    10.12.2018 21:38

    Если скомпилировать это как с++ проект то условие скорее всего не выполнится.
    А если как Си проект, то в таком случае sizeof('a')==sizeof(int).

    А если использовать компилятор FORTRAN, то в таком случае этот код вообще не скомпилируется!

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

    Вместо холивара изучите лучше оба языка, Rust и С++, они оба вам пригодятся.


    1. Enmar Автор
      10.12.2018 21:51
      +1

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


      1. argentumbolo
        10.12.2018 22:48

        Затем, что 16...32-х битный процессор не имеет специальных регистров для чаров.
        Он хранит их в младшей части обычных регистров размером sizeof(int).
        И когда вы загружаете туда чар, вам следует помнить, что при сравнении с другими типами — char(0x32) это абсолютно то же саме, что и int32(0x00000032).


        1. vilgeforce
          11.12.2018 00:05

          Либо я вас не так понял, либо все же «char(0x32) это абсолютно то же саме, что и int32(0x00000032)» — неверно. Сплошь и рядом mov AL, byte ptr[ESI + ECX]; cmp AL,0x32 и подобное наблюдаю. Да, содержимое EAX при таком присвоении уничтожается, но сравнивается все же именно AL


          1. rogoz
            11.12.2018 03:16

            mov AL, byte ptr[ESI + ECX];

            содержимое EAX при таком присвоении уничтожается

            Нет.


            1. vilgeforce
              11.12.2018 10:04

              Да натурально! Лежало в EAX 0x12345678, стало 0x123456MN — значение уничтожено и восстановлению не подлежит, часть регистра перезаписана


      1. dbagaev
        10.12.2018 22:55

        Я утверждаю, что вы не понимаете С++. В С++ согласно последнему стандарту размер char всегда равен 1, а 'a' — это char, поэтому условие всегда будет true. Возможно, это не было верно для каких-то платформ и компиляторов в прошлом, но сейчас все три доступные мне компилятора показывают 1 для обоих sizeof() для 32 и 64 бит.

        В случае с С размер int платформенно-зависимый, и для каких-то платформ действительно может равнятся 1, а не привычному нам 4, или 2 некоторое время назад. И это действительно фича, так как int в С определяется физическим размером регистров процессора целевой платформы.

        Я даже более вам скажу, не во всех архитектурах char может быть 8 бит. Кстати, тут мне стало дейтвительно интересно. Компиляторы С уществуют для огромного количества самых экзотических аппартных платформ, появлявшихся и исчезавших последние 40 лет. И именно поэтому язык допускает такие вольности, а не потому что код должен быть переносим. А Rust, для какого количества платформ существуют компиляторы и на чем они написаны?


        1. argentumbolo
          10.12.2018 23:04

          Не, тут Enmar прав относительно Си.
          В Си sizeof char литерала равен sizeof(int), хотя sizeof char переменной всё ещё равен единице.


          Ну то есть:


          char ch = 'Z';
          assert(sizeof(ch) == 1);
          assert(sizeof('Z') == sizeof(int)); 


      1. Usul
        11.12.2018 07:52

        «Исторически так сложилось»

        Why are C character literals ints instead of chars? (StackOverflow)

        The reason is that the definition of a literal character has evolved and changed, while trying to remain backwards compatible with existing code.

        In the dark days of early C there were no types at all. By the time I first learnt to program in C, types had been introduced, but functions didn't have prototypes to tell the caller what the argument types were. Instead it was standardised that everything passed as a parameter would either be the size of an int (this included all pointers) or it would be a double.

        This meant that when you were writing the function, all the parameters that weren't double were stored on the stack as ints, no matter how you declared them, and the compiler put code in the function to handle this for you.

        This made things somewhat inconsistent, so when K&R wrote their famous book, they put in the rule that a character literal would always be promoted to an int in any expression, not just a function parameter.

        When the ANSI committee first standardised C, they changed this rule so that a character literal would simply be an int, since this seemed a simpler way of achieving the same thing.

        When C++ was being designed, all functions were required to have full prototypes (this is still not required in C, although it is universally accepted as good practice). Because of this, it was decided that a character literal could be stored in a char. The advantage of this in C++ is that a function with a char parameter and a function with an int parameter have different signatures. This advantage is not the case in C.

        This is why they are different. Evolution…


  1. ZaMaZaN4iK
    10.12.2018 21:42
    +4

    Хорошая попытка фарма минусов.


    1. Enmar Автор
      10.12.2018 21:51
      -1

      Ахахаха, действительно.


    1. justhabrauser
      11.12.2018 10:06

      Не попытка, а реальный, качественный фарм.
      Накосил минусов — не унести.


  1. dev96
    10.12.2018 22:30
    +1

    Особенно, если мы говорим о старом типе, в новом string многое было исправлено
    и улучшено, но до сих пор нет поддержки юникода(!).


    Нет адекватной поддержки только UTF8.
    wchar_t (платформенно-зависимый) был всегда.
    В C++11 появились char16_t(UTF16) и char32_t(UTF32) и соответствующие им литералы, которые как-бы ЮНИКОД.
    Причем кроссплатформенную и самую адекватную строку делают на UTF16.
    Да, здесь нам дают выбор. И во многих случаях выбор падет на UTF16 или ASCII (сугубо английский), а не на UTF8 переменного размера.
    Вы используете ASCII-строки (однобайтные), а конкретно CP1251 и возмущаетесь на кракозябры.
    Это самая банальная вещь, на которой вы и посыпались.


    1. marsianin
      10.12.2018 22:37
      +1

      Так-то не все unicode-символы влезают в 16 бит, так что utf16 тоже переменной длины.


    1. acmnu
      11.12.2018 09:08

      Я тут погуглил, и не могу не заметить, что все же char<x>_t являются не вполне переносимыми из-за LE/BE. https://stackoverflow.com/questions/31433324/char16-t-and-char32-t-endianness. Это, в принципе, описано в стандарте.


      Например, с этим можно столкнутся при попытке организовать бинарный протокол между C++ и Java. У последней внутренее бинарное преставление всегда BE (наследие Sun, насколько я понимаю).


  1. LexS007
    11.12.2018 01:04

    Думаю, все знают что в С и в плюсиках куча синтаксического сахара

    Что? Какой в Си сахар-то?


    1. artemisia_borealis
      11.12.2018 01:41
      +1

      Цикл for, наверное, имеется ввиду. Иначе бы пришлось if'ом проверять условие и использовать goto. И среди while и do-while что-то одно (по меньшей мере) является сахаром.

      Ну, и пустая директива, конечно. Хотя это уже не сахар, а скорее амфетамин…


  1. mapron
    11.12.2018 04:09
    +1

    Ну что накинулись-то на автора. Он же в начале предупредил — профессионалам не читать! Читать только тем, кто не разбирается!

    Если серьезно, к автору много вопросов.
    1. Если это просто желание поделиться радостной эйфорией от первых же граблей, на которые наступил в языке — то явно ресурс выбран неудачно. Лучше публиковать на говнокод ру, например. Там еще можно в пост добавить слово «крестобляди», там вообще такое очень любят.
    2. Если это троллинг аудитории хабра, шоб пригорело — то крайне неумелый. Вы пишете такие вещи, от которых фейспалм будет скорее, чем пригорание.
    3. Если это не троллинг а честная агитация за Rust — то правильной статьей было бы «вот смотрите какая тупая конструкция на С, а вот на расте пишешь то-то и граблей не собираешь». Статья вышла бы холиварная, но хоть какое-то признание вы бы получили.

    И да, всевозможные «плюсики» обороты в тексте совсем не приемлемы для нейтрального принятого стиля на Хабре.

    Всего наилучшего.


  1. sami777
    11.12.2018 10:18

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