Pull to refresh

Comments 119

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

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

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

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

Здесь я с Вами согласен.
Ядро юникса и винды например на сях.
странно, что в си компиляторе это воспринимается так, а в ++ компиляторе по-другому.

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

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

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

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

Компилятор провёл лёгкую оптимизацию за нерадивым программистом и вместо сложения числа с самим собой просто умножил его на два.
Я это почерпнул из какой-то статьи на хабре.
Вот из этой кажется:
habr.com/post/88185
Это неопределенное поведение, результат может быть вообще любым.
Это архитектурно зависимо
смысл статьи в чем?
кто вы такой, сколько у вас лет опыта C/C++? — ваше мнение релевантно?

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

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

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

Вообще, о Расте в последнее время и правда много хорошего слышу. Но никак не дойдут руки его посмотреть :(
String^ MyString3 = «Hello, world!»; //попробуйте скомпилировать в GCC

А вы в курсе, что это C++/CLI? Ну т.е. это полу-управляемый C++, живущий с .NET? И что ваш String^ это скорее всего дотнетовский System.String (когда-то давно я на этом даже что-то писал, но могу ошибаться)? Более того, у VC++ есть свои инструменты для работы со строками, как часть winapi, wchar и tchar, первый из которых 16-битовый тип, а второй определяется в зависимости от определенного символа #ifdef UNICODE (отсюда).
Вот и я про тоже.
Нельзя просто взять любой c++ код и быть уверенным, что он во всех компиляторах будет работать и при этом одинаково
Да как бы это сказать… На мой взгляд C++/CLI даже плюсами назвать нельзя. Это как… компилировать CUDA без nvcc и жаловаться, что компилятор не понимает function_name<<<n, m>>>(args). Это фактиечски диалекты.
Ну, блин. Ну нельзя же так:
Откроем вашу любимую 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.


А во-вторых запустим программу:
$ ./a.out 
Привет: привет!
Вывод: привет!

Я понимаю, что это стандартом не гарантировано, но прочитав текст, создается впечатление, что автор твердо уверен, что крякозябры будут у всех и всегда («Откроем вашу любимую IDE»), а это не так.

Более того, вот так, как у меня, будет и на linux (если локаль utf8, что обычно именно так) и на macOS и еще в куче каких осей.

Где будут проблемы с гарантией — это в Windows. Но проблемы там будут не из за поддержки юникода, проблемы там были всегда потому, что гуйны текстовы редакторы, как и вся система (до юникодизации) использовала cp1251 кодировку (если локаль русская), а виндовозная консоль — кодировку DOS866. Итого, набранную строку в кодировке cp1251 пытаемся вывести в консоль, которая воспринимает эту строку как DOS866, в итоге — крякозябры.

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

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

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

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

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

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

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

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

Именно об этом и писал valexey.
Если автору не нравится возможность читать/писать произвольную память — это автор до такого недозрел, а не языки плохие. Возможность работать с байтами в памяти «как есть» — огромный плюс.
Это вы про arr[101]?
Только не говорите, что это фича, я Вас прошу.
В том же расте или в любом другом более высокоуровневым языке такого не будет.
Конечно фича!
struct foo{
unsigned char low[128];
unsigned char high[128];
};

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

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


struct foo{
  int headerField1;
  int headerField2;
  unsigned char data[0]; 
// тут память подводит, может быть и [1], 
// но память все равно рисует [0]
};
Ну да, вполне себе определение заголовка для данных переменной длины
Все таки 1, с 0 не должно скомпилироваться.
UFO just landed and posted this here
Да и ладно :-) Код будет решать задачу на конкретной (программно/)аппаратной платформе корректно и быстро, что и требовалось.
UFO just landed and posted this here
UFO just landed and posted this here
То есть, как минимум, для начала непонятно, куда именно вы там пишете: никто не мешает реализации вставить произвольный padding между low и high.

А как в этих условиях работает #pragma pack (1)?
Насколько понимаю, она в теории полностью снимает вопрос с "произвольным padding" (по крайней мере в жизни не встречал примеров обратного).

UFO just landed and posted this here
Это не баг и не фича. Просто так было раньше, никто переделывать не будет (и слава богу).
Проблема скорее в том, что существует масса старых учебных материалов, в которых новичкам пишут использовать обычные массивы вместо std::array (ex boost::array).
Просто так было раньше и переделывать не будут.
Это называется legacy)
И это баг
legacy — это не баг, это объективная реальность мира. Если у вас завалялось несколько лишних десятков миллиардов $, то вы можете их потратить на переписку и тестирование всего существующего софта на с/с++. Тогда можно будет со спокойной душой убирать возможность написать std::cout<<«string»[4]; из стандарта. Оно того стоит?

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

Точно так же с неопределенным поведением. Это цена производительности во многом.
Автор говорит о том, что в таком compile-time случае компилятор (а не стат. анализатор) по рукам не даст.
Проверка в рантайме в плюсах итак включается в дебаг сборке или принудительно в релизе.
Либо, опять-таки статический анализ.
Хотя, теоретически, std::array во время компиляции может ругаться.
Статическим анализом в любом случае хорошо пользоваться.
clang выдает предупреждения на выход за границы массива в приведенном примере в статье.
Абсолютно с вами согласен.
MSVC тоже предупредит, если настройки подкрутить.
Причем сейчас активно начинают везде внедряться проверки «C++ Core Guidelines».

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


// Я бы так писать не стал
// но возможность есть, на случай например
// использования сторонней библиотеки.
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 еще не изучал подробно.

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

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

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

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

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

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

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

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

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

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

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

Дайте угадаю: на перле вы никогда не писали? :)
UFO just landed and posted this here

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

UFO just landed and posted this here
А какие проблемы С и С++ знаете Вы?
Самая большая проблема C++ — его постоянно критикуют те, кто в нем не разбирается.
Пожалуйста, скажите в чем именно я не прав?
но до сих пор нет поддержки юникода(!).
Просто указываю на недостатки с/с++, т.к они немного достали самого)

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

Присоединюсь к предыдущим ораторам, в вопросах кроссплатформености, размеров типов, понимания ОС и т.д. и задам только один вопрос (из двух пунктов): Вы с каким юникодом собирались работать или C++ должен за Вас магическим образом догадаться и сделать всю сервисную работу, которые делает «обычный язык» программирования?
Про перлы типа любимой IDE и строк в Visual Studio Вам уже сказали, в остальном — Вы не правы в том что это недостатки. Это особенности языка, которые введены не просто так, и большинство из которых до сих пор имеет смысл. То что от них отказывается Rust — это камень в огород Rust'а.
черт, скажите мне КАКИЕ ЭТО ОСОБЕННОСТИ?
То, что предпроцессор ужасен и компилятор компилирует программу которая ломается при
работе, проще говоря runtime error хуже compile error.
Run-time error — это всегда хуже, чем compile-time, то что Вы это не понимаете — ставит под сомнение Ваш опыт программирования вообще, а не только на C++. И чем ужасен препроцессор?
черт, скажите мне КАКИЕ ЭТО ОСОБЕННОСТИ?

К примеру:
1. первый вопрос: размер типов. (тупо сколько занимает в памяти и почему именно так)?
2. второй вопрос: что такое String?
3. третий вопрос, вытекающий их первых двух: если юникодов как грязи UTF-8, UTF-16 BE, UTF-16 LE, UTF-32 и т.д. то как С++ должен решить за Вас задачку «Сколько памяти выделить и как кодировать в памяти?» если Вы сами не понимаете что творите?
UFO just landed and posted this here
Смешивать не нужно было, но в принципе это не на что не влияет)
UFO just landed and posted this here
К сожалению, разработчики стандарта очень боятся нарушить обратную совместимость.
А в случае со строками — правильным решением было бы вообще отвязать строки от кодировки. То есть «Hello» это строка, а кодировка определяется из настроек проекта и компилятора. А вот если требуется явно указать кодировку — то используются префиксы. Причем для однобайтовых кодировок (которые, несмотря на Unicode, иногда все-же нужны) можно было бы указывать кодировку явно. А если она указана обобщенно (т.е. префикс, означающий «текущая однобайтовая кодировка») то брать указанную в настройках проекта или компилятора. Тогда знаменитый вопрос о крякозябрах при выводе русских строк из консольных программ в винде потерял бы актуальность:)
Примерно аналогично должно быть и с числами. Число 42 — это просто число, а его тип должен выводиться компилятором каждый раз в зависимости от контекста. Это может быть и byte, и int, и unsigned long, и double, и даже какой нибудь mpf_t из GMP.
Да, именно из-а того, что
разработчики стандарта очень боятся нарушить обратную совместимость
в си и в С++ много легаси решений.
Это решается какой нибудь #pragma version 2.0 в начале каждого файла. Кому лень переписывать — пускай мучаются на старом, кому не лень — пользуются современным языком с исправленными ошибками дизайна.
Прагмы ни для всего придумали)
Как можно было выстрелить себе в ногу, так и сейчас можно.
Так это на любом языке можно. Однако стремление к созданию «абсолютно безопасного языка» в ущерб всему остальному (а иначе не получится) я не считаю правильным.
Попробуйте просто скачать изображение котика с Вашего любимого сайта с помощью стандартной библиотеки C++.
Это было невозможно до принятия стандарта С++ 17.

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

#include <boost/asio.hpp> // можно заменить на вашу любимую библиотеку
Не мешайте в одну кучу проблемы с++ и проблемы стандартной библиотеки
Вроде как приняли в 17.
Но я могу ошибаться, суть в том, что это появилось совсем недавно или не появилось еще даже.
А проблемы стандратной библиотеки языки разве не относятся к проблемам языка?
Если в языке плохая стд либа, то плохо язык.
Большая часть вещей, на которые вы жалуетесь, исправляется библиотеками и опциями компилятора.
На многие проблемы можно получить предупреждение, если компилировать с -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
Я знаю про опции компилятора)
Но согласитесь, круто когда все работает без опций.
Это же основа, не что-то эдакое.
Когда я в IDE компилирую проект, то очень часто я не могу задать опции компилятора.
Но согласитесь, круто когда все работает без опций.

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

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

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

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

И все же, что у вас за IDE такая? IDE1886 что ли?
>>> А теперь давайте заменим char string[256] на char* string.

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

Вы вполне можете использовать указатель, не выделяя под него память и писать туда что-то. И это не будет ошибкой, если вы, например, направили указатель в буфер видеокарты. Компилятор не может (и не должен) знать о том, что определенная область в адресном пространстве — это мапа в видеопамять и писать туда можно. И Си нужен был именно для этого, это его дефолтовое поведение.
Ну myxo вам подробно ответил, мне и добавить нечего. Использование указателей не подразумевает обязательное выделение памяти.
А теперь давайте заменим char string[256] на char* string.
Я говорю, что компилятор должен был сказать, что мол ты фигню мне подсунул.

clang и gcc выдает предупреждение о неинициализированной переменной.
Нужно просто добавить вывод всех предупреждений флагами компилятора.
Если скомпилировать это как с++ проект то условие скорее всего не выполнится.
А если как Си проект, то в таком случае sizeof('a')==sizeof(int).

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

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

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

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

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

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

Нет.
Да натурально! Лежало в EAX 0x12345678, стало 0x123456MN — значение уничтожено и восстановлению не подлежит, часть регистра перезаписана
Сплошь и рядом mov AL, byte ptr[ESI + ECX]; cmp AL,0x32 и подобное наблюдаю.

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

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

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

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


Ну то есть:


char ch = 'Z';
assert(sizeof(ch) == 1);
assert(sizeof('Z') == sizeof(int)); 
«Исторически так сложилось»

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…
Ахахаха, действительно.
Не попытка, а реальный, качественный фарм.
Накосил минусов — не унести.
Особенно, если мы говорим о старом типе, в новом string многое было исправлено
и улучшено, но до сих пор нет поддержки юникода(!).


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

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


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

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

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

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

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

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

Всего наилучшего.
Я ж написал, что не агитация на Rust)
Просто троллинг, возможно не удачный.
Помню, когда выбирал для себя ООП язык, то остановился на плюсах. Но, когда посмотрел сколько под них сред и когда оказалось, что интернет помогает всегда неправильно, то в итоге ушел на шарп, о чем абсолютно не сожалею.
Насчет Си все намного легче. Под встроенные системы альтернативы нет.
UFO just landed and posted this here
Sign up to leave a comment.

Articles