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

Связь: @devmangx

РКН: https://clck.ru/3Foc4d
Download Telegram
Люди думают, что 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
😁225
Сеньор: нам нужен 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
😁697🤣7💊3👍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
20🔥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👍5
Почему массивы в 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
34🔥7
[Нужен Godbolt]. Да, для сложных типов это иногда верно, но для сценариев, прозрачных для оптимизатора, слово «всегда» здесь просто не подходит.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
11
Браузеры на 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
7
Очень часто мне нужно временно отключить какой-то кусок кода. Я его закомменчиваю, но потом сталкиваюсь с проблемой: раз этот код вообще не компилируется, со временем он «протухает». Какие-то функции, которые он использует, могли измениться, а сам код при этом даже не проходит проверку типов. В итоге, когда я снова его включаю, он уже не компилируется, и мне приходится тратить кучу времени на починку.

Решение, до которого я пока дошёл, это «закомментировать» код через runtime if (0). Такой код никогда не выполнится, оптимизатор, скорее всего, вообще его полностью выкинет, но перед этим компилятор всё равно проверит его типы и сразу заставит меня исправить проблемы на месте.

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍304🤔2
Твой CPU спекулятивно выполнит ветку, которая вообще не должна была выполниться, подтянет секретные данные в кэш, а потом сделает вид, что ничего не произошло.

По сути, Spectre это: а что, если просто измерить по времени последствия этой лжи?

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
9🤔2
Люди из мира OOP: «мы используем DI-фреймворк»
Люди из мира C: «мы передаём struct»

> и то и другое это dependency injection

#include <stdio.h>

struct services {
void (*log)(const char *);
};

static void stderr_log(const char *msg) {
fprintf(stderr, "%s\n", msg);
}

int main(void) {
struct services s = { .log = stderr_log };
s.log("hello from my DI container");
}



👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥95💊1
assumeTPoseForDominance

👉 @Cpportal
Please open Telegram to view this post
VIEW IN TELEGRAM
😁14
🔥 Подписка на easyoffer PRO на 1 год со скидкой 70%

easyoffer – сайт для подготовки к собеседованию на программиста, тестировщика и другие IT-профессии становится еще доступнее со скидкой 70% до 10 марта.

⚙️ Актуальные функции:
1. База вопросов из реальных технических собеседований с вероятностью встречи и примерами ответов.
2. База задач с этапа live-coding.
3. База видеозаписей 1100+ реальных собеседований, в том числе в топовые компании (Сбер, Авито, Яндекс, WB, OZON, МТС и др.) на позиции Junior/Middle/Senior.
4. База 400+ тестовых заданий от компаний.
5. Аналитика ТОП-требований из вакансий для лучшего написания резюме по ключевым словам.
6. Тренажеры для подготовки к собеседованию. В том числе тренажер «Реальное собеседование» со сценарием вопросов под конкретную компанию.

Акция до 10 марта (включительно) на PRO-тариф.
– Подписка действует 1 год
– Доступ ко всем профессиями сразу

👉 Смотри подробности тарифа и покупай на easyoffer
💊1