С/С++ Portal | Программирование
16.2K subscribers
1.14K photos
217 videos
24 files
833 links
Присоединяйтесь к нашему каналу и погрузитесь в мир для C/C++-разработчика

Связь: @devmangx

РКН: https://clck.ru/3Foc4d
Download Telegram
This media is not supported in your browser
VIEW IN TELEGRAM
Чувак написал UI-библиотеку на C, которая умеет раскладывать 95 000 элементов на 60 FPS.

Сама библиотека занимает 566 строк C, а код конкретного приложения — 998 строк. Работает на Mac, Windows и Linux.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥165😁1
Введение в кэш-память RAM и CPU: https://en.algorithmica.org/hpc/cpu-cache/

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

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
10👍7🔥2😁1
Что каждый программист должен знать о памяти: читать

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍124🔥3😁1
Anthropic запустили команду из 16 агентов Opus 4.6, чтобы с нуля написать C-компилятор на Rust, и итоговый компилятор смог собрать ядро Linux

Все 16 агентов работали параллельно над общей кодовой базой, без активного участия человека. Ни одного инженера по компиляторам в процессе не было, а вся работа обошлась всего в $20 000 затрат на API. И всё.

В результате команда агентов выдала компилятор примерно на 100 000 строк кода, способный собрать Linux 6.9.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
😁17🤯72👍1🔥1
Нам не нужно ждать, пока «defer» добавят в стандарт C. У нас уже есть defer

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

В C ситуация другая. В 2021 году был предложен N2895 Дженсом Густедом и Робертом Сикордом, но он не прошёл в C23. Сейчас есть новый N3734 от ЖанХейда Менейда, который, скорее всего, примут в следующей версии стандарта.

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

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8💊21😁1🥱1
Большинство разработчиков пользуются всего парой Git команд.

Вот еще 15, которые сэкономят вам кучу времени.

git stash → Сохранить работу без коммита. Можно прыгать по веткам, не теряя правки.

git reflog → Находит потерянные коммиты. Спасательный круг, если что-то пошло не так с git reset.

git bisect → Двоичный поиск по коммитам, чтобы вычислить баг. Гораздо быстрее ручной проверки.

git rebase -i → Причесывает историю перед пушем. Сквош, перестановка или правка коммитов.

git cherry-pick → Применяет нужные коммиты в другую ветку. Без полноценного merge.

git diff --staged → Показывает, что именно пойдет в коммит. Помогает поймать косяки заранее.

git commit --amend → Поправить последний коммит или добавить забытые файлы.

git reset HEAD~1 → Откатывает последний коммит, но оставляет изменения. Начинаешь заново, ничего не теряя.

git clean -fd → Удаляет все неотслеживаемые файлы и папки. Полная очистка, когда нужно начать на чистую.

git log --oneline --graph → Компактная визуализация истории. Ветки и слияния видно как на ладони.

git blame → Показывает, кто и когда писал каждую строку. Удобно искать момент появления бага.

git show → Детальная инфа по любому коммиту. И изменения, и метаданные.

git remote -v → Список всех удаленных репозиториев. Полезно, чтобы проверить, куда вы пушите.

git fetch --prune → Обновляет информацию о ремоутах и удаляет устаревшие ветки.

git diff branch1..branch2 → Сравнивает две ветки. Видно, что именно в них различается.


👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
18👍5🔥3😁1
🤣🤣

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
😁4110🤣5
This media is not supported in your browser
VIEW IN TELEGRAM
Знакомьтесь: zlob.h — библиотека для Zig, C и Rust, на 100% совместимая с POSIX и glibc и при этом быстрее glibc в 1.2–2.5 раза.

Внутри: SIMD, прямые syscalls, ассемблер, оптимизация горячих путей на уровне алгоритмов и плюс реально более внятный и полезный API как расширение POSIX-интерфейса.

Есть и наглядные сравнения: как работает поиск файлов через glob в Rust, find из glibc и демонстрационный CLI от zlob.

И да, zlob не просто быстрее. Он поддерживает ВСЕ форматы, которые есть в glibc, Rust и bash, и при этом предлагает куда более продуманный публичный API: матчинг путей прямо в памяти, интерфейс на bitflags для тонкой настройки производительности и управления памятью и ещё много всего.

Короче, это не просто ещё одна реализация, а попытка сделать POSIX glob таким, каким он давно должен был быть.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8🤔43😁3
Проектирование и реализация хеш-таблиц в C: от хеш-функций до разрешения коллизий

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔73👍2😁1
chmod - Linux syscall #90

Мы привыкли смотреть на расширения и по ним понимать, что файл делает (.sh, .py). Но ядру Linux плевать на расширение. Оно смотрит на mode bits и решает, можно ли файл запускать. Но одних битов тоже мало: ядро еще заглядывает внутрь файла (например, ищет #! shebang или ELF magic), чтобы понять, КАК именно его запускать.

Это зона ответственности syscall chmod: на x86_64 это #90.

Он управляет битами, которые по сути задают “личность” файла. Отделяет “данные” от “программы”.

▪️Включил execute bit и обычный текстовый файл становится командой.
▪️Включил SUID bit и эта команда будет выполняться от root, даже если ты не root.

Так sudo и получает свою мощь: бинарь sudo установлен с SUID-битом, поэтому при запуске через execve права поднимаются прямо на старте.

Вот как можно дергать эти биты напрямую из C. Прога создает простой скрипт и программно “включает Execute”:

#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main() {
const char *path = "demo.sh";

// 1. Создаем файл только с Read/Write (0600)
// Ядро воспринимает это как данные, не как программу
int fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0600);
if (fd < 0) { perror("open"); return 1; }

write(fd, "#!/bin/sh\necho 'Hello from Syscall 90!'\n", 40);
close(fd);

printf("Создан %s. Попробуй запустить: Permission denied.\n", path);

// 2. Через chmod (Syscall 90) ставим Read + Write + Execute (0700)
// S_IRWXU = Read, Write, Execute для владельца
if (chmod(path, S_IRWXU) == 0) {
printf("Ок: mode bits обновлены через syscall 90.\n");
printf("Теперь запускай так: ./demo.sh\n");
} else {
perror("chmod");
}

return 0;
}


Отличный пример того, что в Linux поведение определяет метадата (права/биты), а не “тип файла по содержимому” или расширение.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
21👍7😁1
symlink - Linux syscall #88

В Linux ФС это огромная паутина редиректов.

Когда ты запускаешь python на современном дистрибутиве, ты вполне можешь попадать в симлинк на python3, который дальше может вести на python3.12. На глаз это мгновенно, но по факту ядро “прыгает” по цепочке файлов, пока не дойдет до конечной цели.

Как Linux делает это без убийства производительности? Для начала разберемся, что такое симлинк на самом деле.

Симлинк это отдельный файл. В отличие от hard link (который просто еще одно имя для того же inode), у симлинка свой inode. А его “содержимое” это обычная строка с путем до цели. Когда ядро во время path lookup натыкается на симлинк, оно читает эту строку и перезапускает поиск уже по новому пути.

Но чтение файла обычно означает две отдельные дисковые операции:

1. Прочитать inode, чтобы понять, где лежат данные.
2. Сходить в data block и прочитать реальный контент.

Для крошечной строки типа /usr/bin/zsh выделять целый 4KB блок тупо расточительно, а лишний seek это еще и медленно. Тут и появляется оптимизация “Fast Symlink”. Если путь до цели достаточно короткий (ровно 60 байт на ext2/ext3/ext4), Linux хранит этот текст прямо внутри структуры inode. Он просто переиспользует место, которое обычно занято указателями на data blocks. Итог простой: как только ядро прочитало метадату файла, оно уже знает target path. Никаких дополнительных disk seek’ов, чтобы разрулить ссылку. Вот эта “секретная приправа” и позволяет Linux держать сложные деревья версий библиотек и алиасы команд почти без оверхеда.

Ниже полный, запускаемый пример на C, который программно создает симлинк:

#include <unistd.h>
#include <stdio.h>

int main() {
const char *target = "/etc/os-release";
const char *linkpath = "os_info_link";

// Создаем симлинк с именем "os_info_link"
// который указывает на системный файл с инфой о релизе
if (symlink(target, linkpath) == 0) {
printf("Success! Created %s -> %s\n", linkpath, target);
printf("Проверь так: ls -l %s\n", linkpath);
} else {
perror("Failed to create symlink");
return 1;
}

return 0;
}


👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
112🔥6👍3😁1🤔1
Чувак написал самохостящийся компилятор C, который назвал 8cc, за 40 дней. Это журнал о том, как он писал его с нуля в одиночку в 2012 году. Код и его история доступны на GitHub.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍224😁2
readlink - Linux syscall #89

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

Как программе найти путь к своему исполняемому файлу, например чтобы подгрузить конфиг, лежащий рядом? Она спрашивает ядро через syscall readlink: #89 на x86_64.

Ты, возможно, знаешь readlink как механизм, который ls -l использует, чтобы показать, куда ведет символическая ссылка. Он читает целевой путь, записанный внутри symlink. В Linux самый известный кейс с небольшой магией это /proc/self/exe. Это символическая ссылка, которую поддерживает ядро, и она всегда указывает на бинарник текущего запущенного процесса на диске. Прочитав эту ссылку, программа узнает, кто она такая.

Но в отличие от большинства функций в C, которые возвращают строки, readlink не добавляет нулевой терминатор в выходной буфер. Он просто копирует сырые байты пути в твой буфер и говорит, сколько байт записал. Если попытаться передать этот буфер в printf, не добавив вручную \0 в конец, ты выведешь мусор из памяти или уронишь приложение.

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

#include <unistd.h>
#include <stdio.h>
#include <limits.h>

int main() {
char buf[PATH_MAX];

// Syscall 89: readlink
// Оставляем 1 байт под нулевой терминатор
ssize_t len = readlink("/proc/self/exe", buf, sizeof(buf) - 1);

if (len != -1) {
// ВАЖНО: вручную ставим нулевой терминатор
buf[len] = '\0';
printf("I am running from: %s\n", buf);
} else {
perror("readlink");
}

return 0;
}


👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
14😁2
This media is not supported in your browser
VIEW IN TELEGRAM
Ты знал, что SSD используют квантовое туннелирование, чтобы хранить данные? Или что RAM приходится полностью перезаписывать каждые 30 мс, иначе данные просто “рассеиваются”?

Если да, то, скорее всего, можешь смело пропустить эту статейку про то, как компы хранят данные 😁

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥224👍3😁3
В C можно определить две любопытные макросы: likely() и unlikely(). Вот что они делают и как могут ускорять код.

Эти макросы обычно оборачивают GCC-функцию __builtin_expect(), чтобы подсказывать компилятору, какая ветка в условии более вероятна. Когда ты пишешь if(likely(x > 0)), ты по сути говоришь компилятору: "это условие почти всегда будет истинным".

Компилятор использует эту подсказку для оптимизаций, связанных с предсказанием ветвлений. Он раскладывает ассемблер так, чтобы вероятный путь выполнялся без перехода (jump), сохраняя пайплайн процессора ровным и улучшая локальность по кэшу.

В ядре Linux это используется очень широко: likely() встречается в кодовой базе больше 3 000 раз, а unlikely() больше 14 000 раз. Трассировщик Linux ftrace фиксировал прирост производительности до 20% после добавления корректных подсказок likely/unlikely.

На практике ты можешь увидеть 5–15% ускорения на hot path, где ветвления действительно предсказуемые.

Но современные CPU и так неплохо предсказывают ветки сами. Если промахнуться и пометить что-то как likely, когда это не так, можно сделать только хуже.

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

Используй это редко, профилируй код и применяй только там, где вероятность ветки реально сильно перекошена (90%+ в одну сторону).

Надеюсь, пригодится.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2611🔥4
cCppProgrammingIn2050

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
34🤔5😁4
Мы можем реализовать алгоритм рисования линии Брезенхэма, используя только целочисленную арифметику и сложение.

Он вычисляет координаты пикселей для растеризации линий без операций с плавающей точкой и без деления.

void bresenham(int x0, int y0, int x1, int y1,
void (*plot)(int, int)) {

int dx = abs(x1 - x0);
int dy = abs(y1 - y0);

int sx = x0 < x1 ? 1 : -1;
int sy = y0 < y1 ? 1 : -1;

int err = dx - dy;

while (1) {
plot(x0, y0);

if (x0 == x1 && y0 == y1)
break;

int e2 = 2 * err;

if (e2 > -dy) {
err -= dy;
x0 += sx;
}

if (e2 < dx) {
err += dx;
y0 += sy;
}
}
}


👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
17👍6🔥4🌚1🌭1
Что выделяет современные системные языки? Много чего, но аллокаторы это одна из ключевых тем.

Давай посмотрим, как устроены аллокаторы в Rust, Zig, Odin, C3 и Hare, а потом соберём свой аллокатор на C 👍

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍4😁1
NASA пишет mission-critical софт для полётов на C. И правила там просто жесточайшие.

> Никакой рекурсии. Вообще никогда.
> У каждого цикла должна быть доказуемая верхняя граница по числу итераций.
> Никаких динамических аллокаций памяти после этапа инициализации.
> Максимум примерно 60 строк на функцию.
> Минимум 2 assert на функцию.
> Каждый return value обязан быть проверен.
> Ноль предупреждений компилятора. Вообще.
> Ежедневный статический анализ. И там тоже ноль предупреждений.
> Никаких указателей на функции.
> Ограниченное разыменование указателей.

Вот так они пишут код в NASA / JPL для критичных к миссии систем.

Все 10 правил перечислены здесь, можно глянуть: https://spinroot.com/gerard/pdf/P10.pdf

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4512👍5😁2🥱1
This media is not supported in your browser
VIEW IN TELEGRAM
Разработчик создал C-библиотеку для сокетов, которая покрывает весь стек: от сырого TCP до gRPC поверх HTTP/3.

У ядра стека нет внешних зависимостей. Поддерживаются HTTP/1.1/2/3, QUIC, WebSocket, TLS 1.3, DNSSEC, DEFLATE, io_uring, пул соединений.

Это единственная C-библиотека с нативной поддержкой gRPC поверх HTTP/3.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍369🔥3😁2🤣1
Хватит тратить байты на булевы флаги.

В C есть очень аккуратный способ упаковать их максимально плотно через битовые поля (bit fields). Идеально для флагов, статус-регистров, заголовков протоколов и всего, где важен каждый бит.

Вместо 4 отдельных int можно запихнуть до 32 флагов всего в 4 байта.

Но есть нюанс: padding и раскладка битов зависят от реализации компилятора (implementation-defined). Так что обязательно проверяй на целевой платформе.

#include <stdio.h>

struct user_flags {
unsigned int is_active : 1; // just one bit each
unsigned int is_admin : 1;
unsigned int is_banned : 1;
unsigned int email_verified : 1;
unsigned int has_two_factor : 1;
unsigned int is_premium : 1;
unsigned int can_post : 1;
unsigned int can_comment : 1;
unsigned int reserved : 24; // unused space for now
};

int main() {
struct user_flags current_user = {0};

current_user.is_active = 1;
current_user.is_admin = 1;
current_user.email_verified = 1;
current_user.is_premium = 1;

printf("this struct only needs %zu bytes (fits in 4!)\n", sizeof(current_user));

if (current_user.is_admin && current_user.is_premium) {
printf("user has admin and premium access\n");
}

// flip the banned status
current_user.is_banned = !current_user.is_banned;

return 0;
}



👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
30👍2