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

Связь: @devmangx

РКН: https://clck.ru/3Foc4d
Download Telegram
C++ совет дня

Наследование от стандартных типов это отличный способ реализовать аккуратные extension-методы.

Вот небольшой utility для std::string_view, который можно использовать, чтобы декларативно обрезать префиксы и суффиксы.

https://godbolt.org/z/TTxjaMK6o

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
20🤔4💊2
Команда гениев собрала браузер на С++ вообще с нуля.

❤️ Ни строчки кода из Chrome. Ни из Firefox. Ничего не переиспользовано.

Ladybird Browser это:

→ на 100% независимый движок браузера
→ написан на C++ с нуля
→ не форк Chromium, как почти любой "новый" браузер, который ты видел
→ полностью open source, контрибьютить может кто угодно

Chrome сейчас по-тихому крутится под капотом у 95% интернета.

И это open source проект, который пытается это изменить.

59 200+ звезд на GitHub.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯2610🔥8👎2
getuid: Linux syscall #102

В Linux "кто ты" и "что тебе можно" часто разные вещи. У процесса есть две ключевые идентичности:

- Real UID (RUID): кто запустил процесс (ты)
- Effective UID (EUID): чьи права сейчас действуют (с привилегиями)

Это завязано на syscall getuid (№102 на x86_64).

Зачем нужны два UID?

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

У бинарника passwd включен специальный бит Set-UID. Когда ты его запускаешь:

- Effective UID временно становится 0 (root), чтобы дать доступ на запись в файл.
- Real UID остается 1000 (ты).

Дальше программа вызывает getuid(), чтобы проверить Real UID. По нему она понимает, root ты или обычный пользователь, и если ты обычный пользователь, по умолчанию выбирает твой аккаунт и гарантирует, что ты можешь менять только свою запись пароля. Без getuid() Set-UID программа имела бы root-права, но не имела бы способа определить, кто именно ее запустил.

Ниже полный, запускаемый пример на C, который показывает идентичности текущего процесса. Если запустить обычно, оба UID совпадут. Если сменить владельца на root и выставить Set-UID бит (chmod u+s), увидишь, что они расходятся.

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main() {
// Syscall 102 (x86_64): getuid (Кто меня запустил?)
uid_t ruid = getuid();

// Syscall 107 (x86_64): geteuid (От чьего имени я действую?)
uid_t euid = geteuid();

printf("Real UID (Identity): %d\n", ruid);
printf("Effective UID (Powers): %d\n", euid);

if (ruid != euid) {
printf("Process is running with borrowed privileges!\n");
} else {
printf("Process is running with standard privileges.\n");
}

return 0;
}


getuid состоит в маленьком элитном клубе системных вызовов: он принимает 0 аргументов, гарантированно никогда не падает, и никогда не меняет errno.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
8
Если тебе нравится перегрузка операторов в C++, то тебе зайдет и перегрузка пробелов! -

уверяет Страуструп 🤭

На самом деле, это первоапрельский прикол от Страуструпа, замаскированный под “предложение для C++2000”. Он сам прямо пишет, что это April Fool’s Joke.

Глянуть: https://www.stroustrup.com/whitespace98.pdf

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
😁246
Не верь байкам.

C++ жив как никогда.

Вот реализация алгоритма решета для простых чисел на современном C++

#include <algorithm>
#include <print>
#include <ranges>
#include <vector>
#include <unordered_set>

// Returns primes in range: 3...2 * n + 2
std::vector<int> sieve_sundaram(int n) {
auto sieve =
std::views::iota(1, n + 1)
| std::ranges::to<std::unordered_set>();

auto sieved_out =
std::views::iota(1, n + 1)
| std::views::transform([n](int j) {
return
std::views::iota(j, n + 1)
| std::views::transform([j](int i) { return i + j + 2 * i * j; })
| std::views::take_while([n](int x) { return x <= n; });
})
| std::views::join;

for (int out : sieved_out)
sieve.erase(out);

auto primes = sieve
| std::views::transform([](int x) { return x * 2 + 1; })
| std::ranges::to<std::vector>();

std::ranges::sort(primes);

return primes;
}

int main() {
std::println("Primes: {}", sieve_sundaram(16));
// Primes: [3, 5, 7, 11, 13, 17, 19, 23, 29, 31]
}


👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥205💊1
Собрал JavaScript-рантайм на C, чтобы реально понять, как устроены Node.js, Bun и Deno

Jade Runtime покрывает:

▪️интеграцию движка JavaScriptCore
▪️архитектуру event loop на libuv
▪️биндинги к нативному API
▪️реализацию асинхронного I/O

Полное руководство с кодом - https://devlogs.xyz/blog/building-a-javaScript-runtime

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍82🔥2👎1
This media is not supported in your browser
VIEW IN TELEGRAM
Большинство людей каждый день пользуются Linux.

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

Вот какая последовательность шагов проходит Linux, прежде чем ты увидишь экран логина:

Нажали power → BIOS или UEFI инициализирует железо и гоняет POST.

Прошивка находит на диске загрузчик (bootloader).

Bootloader грузит в RAM Linux kernel и initramfs.

Ядро распаковывается и забирает управление CPU.

Инициализируются менеджер памяти и планировщик (scheduler).

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

Монтируется временная корневая файловая система из initramfs.

Стартует PID 1 как systemd, начинается userspace.

Системные сервисы и демоны запускаются по порядку.

Становится доступен login prompt или GUI.

Ты запускаешь команду, и она становится процессом с PID.

Процесс работает в user mode с ограниченными правами.

Когда нужен доступ к железу, делается системный вызов (system call).

CPU переключается из user mode в kernel mode.

Ядро валидирует запрос и выполняет его через драйверы.

Результат возвращается обратно в userspace.

Планировщик постоянно раздаёт процессам кванты CPU.

Виртуальная память изолирует и защищает процессы.

Файловая система даёт абстракцию данных поверх хранилища.

Сетевой стек обрабатывает пакеты внутри ядра.

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

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍175
Пример на C, как сымитировать "class"

#include <stdio.h>

typedef struct Counter Counter;

struct Counter {
int a;
int b;
void (*add_and_print)(Counter *);
};

void counter_add_and_print(Counter *c) {
c->a += c->b;
printf("значение = %d (добавили %d)\n", c->a, c->b);
}

int main(void) {
Counter c = { .a = 0, .b = 3, .add_and_print = counter_add_and_print };
c.add_and_print(&c);
c.add_and_print(&c);

return 0;
}


👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
25👎5👍1
Классный инсайт про хеш-функцию djb2 из модуля Hash, который идёт в комплекте с Jai.

djb2_hash :: (s: string) -> u32 {
// Хеш djb2.
// Людям он, похоже, нравится, но для строк ассетов, которые мы часто видим,
// он выглядит довольно последовательным. Например, если у тебя имена ассетов
// "foo1", "foo2", "foo3", они все захешируются в последовательные слоты,
// потому что отличаются только последним символом, а эти символы идут подряд!

hash := HASH_INIT;

for 0..s.count-1 #no_abc #no_aoc {
hash = ((hash << 5) + hash) + s[it]; // hash * 33 + c
}

return hash;
}


👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
8
Люди думают, что C это строгий язык

А C позволяет между делом вплести switch внутрь while, поломать пространство-время, и оно спокойно скомпилится.

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

#include <stdio.h>

int main() {
int i = 0;

switch (i) {
case 0:
while (i < 3) {
printf("%d", i);
case 1:
i++;
}
}

return 0;
}


👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔28👍95🤣3🔥1
setgid - системный вызов Linux #106

Идентичность процесса в Linux это иллюзия.
Ядро видит потоки.
POSIX ожидает процессы.

Возьмем setgid (syscall #106 на x86_64).

Он выставляет effective group ID (эффективный GID) у вызывающего процесса. Если вызывающий привилегирован (имеет capability CAP_SETGID), он также выставляет real GID (реальный GID) и saved set-group-ID (сохраненный setgid). Чаще всего это видно, когда сервер вроде Nginx сбрасывает привилегии с root на менее привилегированную группу ради безопасности. Обычно ты предполагаешь, что это относится ко всему процессу. Но вот подвох: в Linux ядро ведет учет credentials (учетных данных) на уровне потока.

Если определить "процесс" как группу потоков, которые шарят память, ядро не обязано заставлять их шарить credentials. Если обойти стандартную библиотеку и дернуть сырой syscall в многопоточном приложении, изменится только вызывающий поток. В итоге получишь процесс с раздвоенной идентичностью: один поток уже ограничен, а фоновые worker-потоки продолжают работать со старыми групповыми привилегиями.

Чтобы это исправить, реализация потоков glibc NPTL делает синхронизационный танец. Когда ты вызываешь setgid(), обертка не просто вызывает сырой syscall. Она шлет сигнал каждому другому потоку в процессе. Обработчик сигнала в каждом потоке затем вызывает тот же syscall для самого себя, обновляя свои credentials, чтобы они совпали. glibc ждет, пока все потоки закончат обновление, и только потом возвращает управление. Так она выполняет семантику POSIX, где требуется, чтобы все потоки в процессе имели одинаковые credentials.

Ниже C-программа, которая демонстрирует вызов raw syscall.

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <errno.h>

int main() {
// We will try to switch to GID 1000
gid_t target_gid = 1000;

printf("Current GID: %d\n", getgid());

// We use the raw syscall (setgid)
// On a multi-threaded app, this is dangerous!
// It only updates the credential for the CURRENT thread.
printf("Calling raw setgid syscall...\n");
long ret = syscall(SYS_setgid, target_gid);

if (ret == 0) {
printf("Success! New GID: %d\n", getgid());
} else {
perror("Syscall failed (are you root?)");
}

return 0;
}


Это идеальный пример того, как механизм в ядре ОС отличается от спецификации POSIX , и какой “клей” нужен, чтобы все это склеить.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
10
macrosAreRarelyUsed

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
😁215
Сеньор: нам нужен service layer, repository layer и controller
C-разраб: у нас есть это

#include <stdio.h>

int get_status(void) {
return 200; // репозиторий, видимо
}

void handle_request(void) {
printf("%d OK\n", get_status()); // сервис + контроллер
}

int main(void) {
handle_request();
return 0;
}


👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
😁256👎2
Только что выяснил, что в C есть нативный HTTP-роутинг.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
😁677🤣7💊2👍1
Да, в C есть указатели на массивы, размер которых известен только во время выполнения.

#include <stdio.h>

int main(void) {
int n = 5;
int (*arr)[n] = &(int[]){1, 2, 3, 4, 5};

for (int i = 0; i < n; i++)
printf("%d\n", (*arr)[i]);

return 0;
}


👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
19🔥4💊4
Если ты платишь за девтулы, сохрани это: http://github.com/ripienaar/free-for-dev

Один из самых больших каталогов бесплатных тарифов в интернете.

- 85K+ звезд на GitHub
- покрывает облака, базы данных, CI/CD, API и хостинг
- комьюнити-кураторский список 100% бесплатных тиров
- экономит тебе тысячи долларов на сайд-проектах
- постоянно обновляется сообществом

Лучший читлист для бутстрапа 🐐

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍2🔥2😁1
Появился новый порт быстрого float-парсера на чистом C.

Речь про библиотеку для парсинга чисел с плавающей точкой в стиле std::from_chars: из строки вроде "3.1416" сразу в double. В реальных задачах (например, при разборе JSON с кучей чисел, типа geojson) именно это часто становится главным триггером, поэтому быстрый парсер реально влияет на общую скорость.

Ранее были реализации/порты на C++, Java, C#, Rust, но прямого “чистого C” варианта не хватало. Для проектов вроде Redis это особенно важно: не хочется тащить C++ компилятор только ради парсинга.

Теперь есть аккуратный ручной порт от Koleman Nix: он проходит жёсткие тесты и при этом очень быстрый. Официального релиза пока нет, но код уже можно посмотреть:

https://github.com/kolemannix/ffc.h

По бенчмаркам местами получается топ по скорости: до 100 млн float в секунду (примерно 2 GB/s).

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
10👍4
Почему массивы в C начинаются с нуля

Я пишу for(i=0; i<n; i++) с 11 лет, и ни разу не остановился, чтобы спросить: а почему вообще так. Это просто мышечная память, ритуальное заклинание, которое набираешь не глядя. Но на прошлой неделе кто-то спросил меня, почему массивы начинаются с нуля, и я открыл рот, чтобы объяснить, и вдруг понял, что вообще не знаю. Ноль неестественен. Никто не считает яблоки начиная с нулевого яблока. Это компьютерная штука, сказал я, туманно помахав в сторону адресов памяти. Но это звучало как отмазка, так что я полез копать, и, честно, стало понятнее, когда я посмотрел на PDP-11.

Вот что обычно никто не говорит: arr[i] - это ложь. Это синтаксический сахар. Под капотом там просто арифметика указателей. Когда ты пишешь так:

int arr[5] = {10, 20, 30, 40, 50};
int x = arr[2];


компилятор на самом деле делает:

int x = *(arr + 2);


Квадратные скобки просто наряжают математику. arr - это указатель на базовый адрес, а 2 - это смещение. Если arr живет по адресу 1000, а int занимает 4 байта, ты берешь то, что лежит по адресу 1008. Ноль начинает выглядеть логично, потому что первый элемент находится по базовому адресу, то есть со смещением ноль.

Но представь на секунду, что C использовал бы индексацию с 1, как какой-нибудь цивилизованный язык. Ты бы писал arr[i], и под капотом компилятору нужно было бы делать:

*(arr + i - 1)


Каждый. Отдельный. Доступ. Этот - 1 - это лишняя инструкция вычитания. На PDP-11, этой красивой, тесной маленькой машинке, на которой родился C, циклы CPU были на вес золота. Ты чувствовал каждую инструкцию. Одно лишнее вычитание на каждый доступ к массиву, умноженное на каждый цикл, каждую строковую операцию, каждую заливку буфера - это реальные деньги.

Так что они выбрали ноль, потому что экономия одного CPU-цикла в 1972 году стоила того, чтобы путать новичков следующие 50 лет.

Я все время думаю про это вычитание. Это i - 1 компилилось бы во что-то вроде:

MOV i, R0
SUB #1, R0 ; вот оно. вот этот налог.
MOV (arr,R0), x


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

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

В общем, я все равно буду писать for(i = 0; i < n; i ++). Но теперь я знаю, что считаю с нуля не потому что это логично. Я считаю с нуля потому, что вычитать единицу когда-то было слишком дорого.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
30🔥7
[Нужен Godbolt]. Да, для сложных типов это иногда верно, но для сценариев, прозрачных для оптимизатора, слово «всегда» здесь просто не подходит.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
7
Браузеры на Chromium открывают JSON-RPC интерфейс через Chrome DevTools Protocol. Это программный доступ к инструментированию, удалённой инспекции, отладке и автоматизации.

Браузер тут по сути превращается в playground для API, попробуй сам.

http://chromedevtools.github.io/devtools-protocol/

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
4