# Си должен умереть?
hugeping(ping,1) — All
2021-11-09 17:14:26


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

https://www.yodaiken.com/2018/12/31/undefined-behavior-and-the-purpose-of-c/

http://cmustdie.com/

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

Да, неопределённое поведение -- это угроза. И угроза вдвойне, когда по мере развития компиляторов, это поведение воспринимается как территория, на которой можно "срезать углы". При этом мы получаем мину замедленного действия. Старый софт, собранный новым компилятором может "бахнуть" когда и где угодно... Так что с точки зрения индустрии, нужен простой (не надо много платить за кадры), дубовый и предсказуемый язык. Правда, при этом, он должен быть ещё и системным. Совместить эти противоречивые требования нелегко...

Как программист на Си, я понимаю, что по современным меркам этот язык нарушает все мыслимые правила приличия. Но при этом я действительно НЕ ВИЖУ адекватной замены в той сфере где он применяется. Это как с самолётами. Неустойчивый самолёт -- маневренный и опасный. Устойчивый -- безопасный и неманевренный. Что выбрать? Что-то среднее? Чем мы готовы пожертвовать?

Представим себе, что нам нужно написать ядро ОС. Какой язык выбрать? C, C++... Rust?

Мои знания о Rust ограничены чтением книги, поэтому я недостаточно подкован для того, чтобы адекватно оценить его кандидатуру как замену Си. Субъективно, мне кажется, что Rust не сможет стать таким же массовым языком. В нём нет простоты Си. Его синтаксис многословен, а в некоторых задачах он выкручивает программисту руки. Я надеюсь, что идеи заложенные в Rust найдут реализацию в каком-то другом ЯП. Но это моё личное, субъективное мнение. Конечно, если индустрия перейдёт на Rust, я буду его использовать в любом случае. А пока, можно посмотреть на пример реализации драйвера на Си и Rust: https://lwn.net/Articles/863459/ который меня совсем не впечатляет.

Си был моим любимым языком с самого начала профессиональной деятельности. Но с годами я стал замечать, что программирование на Си всё чаще воспринимается как рутина. Я перестал чувствовать удовольствие от программирования и изучения чужого кода. Работал скорее как "ремесленник". К счастью, были и другие ЯП, которые мне было интересно изучить, так что выгорания я избежал.

Но не так давно я открыл для себя Си заново. Помогло мне в этом осознание:

> Код на Си прекрасен, когда он прост!

Но что значит простой код? Программисты всегда пытаются писать просто. Или нет? И можно ли писать современное ПО просто?

Посмотрите. Это реализация cat в Plan9: https://github.com/0intro/plan9/blob/master/sys/src/cmd/cat.c

А это, для сравнения, cat из coreutils: https://github.com/coreutils/coreutils/blob/master/src/cat.c

Я отдаю себе отчёт в том, что современное ПО так не пишется. Поддержка локалей и gettext. Учёт особенностей системных вызовов на разных ОС. Поддержка множества параметров. И вот, объём и сложность кода растут как снежный ком. А если вспомнить про полную поддержку Unicode (со всеми этими проклятыми эмодзи и сменой направления вывода), то становится совсем уж плохо. Мы так привыкли к универсальности "правильного" подхода, что когда ты видишь "наивный" код Plan9, то испытываешь шок. Что, так можно было?

Но если по чесноку, разве cat не должен быть именно таким, каким он остался в Plan9? :) Думаю, каждый программист чувствует здесь какую-то правду.

Си -- это портативный ассемблер. В этом его основная миссия, сила и слабость. И сегодня, когда разрыв между низким и высоким уровнем многократно увеличился, я не могу не признать, что он мало пригоден для написания сложного прикладного ПО. Но что с системным?

Системное ПО тоже стало сложнее. Как системный программист, я начинал с ядра Linux 2.2. Читать и понимать код современного ядра стало намного сложнее.

В качестве примера, посмотрите реализацию системного вызова open в ядре 2.2.
https://elixir.bootlin.com/linux/2.2.26/source/fs/open.c#L757
asmlinkage int sys_open(const char * filename, int flags, int mode)
{
	char * tmp;
	int fd, error;

	tmp = getname(filename);
	fd = PTR_ERR(tmp);
	if (!IS_ERR(tmp)) {
		lock_kernel();
		fd = get_unused_fd();
		if (fd >= 0) {
			struct file * f = filp_open(tmp, flags, mode);
			error = PTR_ERR(f);
			if (IS_ERR(f))
				goto out_error;
			fd_install(fd, f);
		}
out:
		unlock_kernel();
		putname(tmp);
	}
	return fd;

out_error:
	put_unused_fd(fd);
	fd = error;
	goto out;
}
Довольно наглядно и просто, не правда ли?

А теперь начните своё путешествие по ядру 5.10.

https://elixir.bootlin.com/linux/v5.10.78/source/fs/open.c#L1200

Вам придётся пройтись по цепочке: do_sys_open -> do_sys_openat2

https://elixir.bootlin.com/linux/v5.10.78/source/fs/open.c#L1193
https://elixir.bootlin.com/linux/v5.10.78/source/fs/open.c#L1164

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
	if (force_o_largefile())
		flags |= O_LARGEFILE;
	return do_sys_open(AT_FDCWD, filename, flags, mode);
}

long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
	struct open_how how = build_open_how(flags, mode);
	return do_sys_openat2(dfd, filename, &how);
}

static long do_sys_openat2(int dfd, const char __user *filename,
			   struct open_how *how)
{
	struct open_flags op;
	int fd = build_open_flags(how, &op);
	struct filename *tmp;

	if (fd)
		return fd;

	tmp = getname(filename);
	if (IS_ERR(tmp))
		return PTR_ERR(tmp);

	fd = get_unused_fd_flags(how->flags);
	if (fd >= 0) {
		struct file *f = do_filp_open(dfd, tmp, &op);
		if (IS_ERR(f)) {
			put_unused_fd(fd);
			fd = PTR_ERR(f);
		} else {
			fsnotify_open(f);
			fd_install(fd, f);
		}
	}
	putname(tmp);
	return fd;
}

Необходимость изменений очевидна. Появилась подсистема fsnotify. Добавили новые системные вызовы -- пришлось нарезать функции и сводить open к openat...

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

Да, работая на индустрию не удастся вернуться в прошлое. Однако, Plan9 научил меня тому, что во многих случаях у меня, как у программиста, всё-таки есть свобода избежать сложного кода, в том числе ценой выбора более подходящей архитектуры. Например, написав небольшое ядро приложения на Си и функциональную часть на Lua. Если я вижу, что код на Си становится сложным, это повод остановиться. Стоп! Что-то не так!

Что касается судьбы Си... Я тоже хотел бы увидеть современного приемника, но в Rust я не смог его узнать. Возможно, это моя ошибка. Поживём -- увидим.

Интересно, сколько лет ещё продержится старичок Си? А то ведь и 70-летие не за горами. :)

P.S. Ещё одна "эмоциональная" статья на тему C и C++: Почему я всё ещё люблю C, но при этом терпеть не могу C++?

https://habr.com/ru/company/ruvds/blog/562530/

# Re: Си должен умереть?
vvs(ping,12) — hugeping
2021-11-12 21:29:54


> У тебя не первый раз проходит эта тема и я уже один раз ответил. Мне неудобно опять навязывать свои интересы, но если тебя это так волнует, то может всё же поделишься своим мнением о https://ziglang.org?

Вот как раз @nvkv этот язык использовал. Можно его попросить рассказать. :)

А я только читал про него, не не использовал. В целом, я воспринял его как "современный вариант Си". Но без каких-то характерных и принципиальных "киллер-фич". В Rust, как бы я к нему не относился, такая киллер фича есть. А в ziglang смотрю: defer, слайсы, типы опциональные и т.д. Есть элементы современных языков, но я не вижу принципиального какого-то отличия.

Впрочем, я плохой теоретик. Познакомиться поближе хотел, но пока на это не хвататет времени.

Кстати, есть ещё новый язык от Drew DeVault: https://harelang.org
Его вообще не смотрел.

# Re: Си должен умереть?
hugeping(ping,1) — vvs
2021-11-13 07:08:34


Ох, я кажется по ошибке нажал редактировать вместо ответа, прошу прощения @vvs!

# Re: Си должен умереть?
vvs(ping,12) — hugeping
2021-11-13 09:38:41


Ха-ха. Вот я обалдел, когда увидел что я там написал :)

# Re: Си должен умереть?
vvs(ping,12) — vvs
2021-11-13 09:46:09


vvs> Есть элементы современных языков, но я не вижу принципиального какого-то отличия.

vvs> Впрочем, я плохой теоретик. Познакомиться поближе хотел, но пока на это не хвататет времени.

Потому и интересно мнение именно практика. Возможности кросс-компиляции и прямая совместимость с существующим кодом на C - это выглядит круто. Может это и есть его "киллер-фича"? Теоретически он действительно интереса для меня не представляет.

С другой стороны, какие новаторские идеи можно _сейчас_ найти в C?

# Re: Си должен умереть?
nvkv(ping,35) — vvs
2021-11-13 10:57:02


Так, про Зиг. Я его не то чтобы сильно использовал, но использовал.
Это совершенно замечательный язык, который чувак конструировал именно как "C without a wart".

Киллер-фич зига три, на мой взгляд

1. Тотальный контроль работы с памятью, намного более гранулярный, чем в C (аллокатор из коробки не один, их много, они разные, можно делать свои и передавать их другим программам)
2. Минимум имплицитного поведения, то есть всё максимально явно описывается в языке
3. Compile Time metaprogramming, то есть компилятор во время компиляции может исполнять код, при этом само понятие "тип" это просто объект языка, с которым можно поступать так же как с числами, или структурами. Просто гляньте как в Zig сделаны дженерики https://ziglang.org/documentation/master/#Generic-Data-Structures

В общем, на мой взгляд Zig это лучшее, что случалось с этим классом языков (C/C++/Rust) за много-много лет.

# Re: Си должен умереть?
hugeping(ping,1) — nvkv
2021-11-13 11:07:23


nvkv> В общем, на мой взгляд Zig это лучшее, что случалось с этим классом языков (C/C++/Rust) за много-много лет.

Всё, надо смотреть. Убедил. :) А harelang не смотрел?

# Re: Си должен умереть?
vvs(ping,12) — nvkv
2021-11-13 11:08:01


nvkv> В общем, на мой взгляд Zig это лучшее, что случалось с этим классом языков (C/C++/Rust) за много-много лет.

Интересно. В общем это то же, что и мне показалось, но своему мнению я в этом вопросе не доверяю, поскольку я ни разу не практик.

Пётр, ты явно непоследователен. Сначала ищешь C лучше, чем C, а потом говоришь, что он тебе скучен :)

P.S. А-аа! Вижу ты изменил своё мнение :)
P.S. Edited: 2021-11-13 11:08:33

# Re: Си должен умереть?
nvkv(ping,35) — hugeping
2021-11-13 13:33:04


hugeping> Всё, надо смотреть. Убедил. :) А harelang не смотрел?

Руки не дошли пока, не знаю про него ничего, кроме того, что его Девальт пилит