this->notes.
4.54K subscribers
35 photos
1 file
374 links
О разработке, архитектуре и C++.

Tags: #common, #cpp, #highload и другие можно найти поиском.
Задачки: #poll.
Мои публикации: #pub.
Автор и предложка: @vanyakhodor.
GitHub: dasfex.
Download Telegram
#cpp

Day 8.

В C тоже есть массивы. И работягам тоже хочется знать, сколько в этих массивах элементов. Стандартного решения у ребят там нет, но есть общий подход, перетекающий из кодовой базы в кодовую базу. Зовётся ARRAY_LENGTH/ARRAY_LEN/ARRAY_SIZE/COUNTOF/...
Выглядит так:

#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0]))


Тут мы полагаемся на sizeof, который, согласно стандарту C99 (моя вольная интерпретация):

sizeof возвращает размер операнда (в байтах). Размер зависит от типа операнда. Результат — int. Обычно результат не evaluated и является integer constant.


Another use of the sizeof operator is to compute the number of elements in an array:

sizeof array / sizeof array[0]


Так что sizeof(x) вернёт кол-во байт типа массива (если x — массив int[3], то можем получить (в зависимости от системы) 12). sizeof((x)[0]) вернёт размер типа одного элемента (в нашем случае 4). Вот и получаем 3.

На собесах могут спрашивать вопросы с подвохом вида:

int x = 10;
sizeof(x++);
std::cout << x; // result?

Конечно, вы на такое не попадётесь и скажете 10, ведь sizeof интересует тип. Он не evaluatит свой аргумент. Но всегда ли это так?

Конечно нет!
Если ваш аргумент — Variable Length Array, то sizeof придётся вычислить аргумент. Мы можем запруфать это через наличие сайдэффекта:


int f() {
printf("called\n");
return 10;
}

int main() {
sizeof(int[f()]);
}



Увидим called в output.
https://godbolt.org/z/8G1soa8T1

Теперь срочно требуйте оффер х3 от вашего текущего дохода, ведь собеседующий почти наверняка этого не знает.

@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.
👍22🔥97🤯3😭2
#cpp

Day 9.

До сегодняшнего дня мы были где-то на уровне 1. Сегодня делаем шаг на следующую ступеньку (вниз или вверх, это как посмотреть).

Подстановка макросов (expansion) не являются рекурсивной.


#define A(x) A(x x)
A(x) // A(x x)

#define B(x) C(x x)
#define C(x) B(x x)
B(x) // B(x x x x)


Выстрелить себе в ногу становиться чуть сложнее. Или проще. Это опять как посмотреть.

@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.
14👍10🔥2
#cpp

Day 10.

Когда вы работаете с макросами, вам может пригодиться stringification operator.

#define STR(x) #x

Он позволяет превратить аргумент макроса в строку:

STR(123 foo real) // "123 foo real"


Причём аргумент в таком случае не expandится:

#define FOO 123
#define STR(x) #x

STR(FOO) // "FOO"


Если хотите раскрыть, нужен ещё один уровень:

#define STR2(x) #x
#define STR(x) STR2(x)

STR(FOO) // "123"


# почти сохраняет исходный текст (может поудалять лишние пробелы):

STR( a + b ) // "a + b"

Так что некоторые разные входные данные могут давать одинаковый результат. Надо быть осторожными.

Вы можете использовать # для дебажных утилиток:

#define PRINT(expr) \
std::cout << #expr << " = " << (expr) << '\n';

PRINT(x + y) // x + y = 12

Эта же фича используется в assert (вы видите упавшее условие в ошибке).

@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.
👍275🔥3❤‍🔥2
#cpp

Day 11.

## — token pasting operator или token concatenation operator.

Используется в макросах для склеивания двух токенов (на этапе препроцессинга конечно же).

#define MAKE_VAR(x) var_##x

int MAKE_VAR(test) = 42; // int var_test = 42;


Склеиваются именно токены. После препроцессинга должен получиться валидный токен:

#define BAD(a, b) a ## b

BAD(+, +) // ++
BAD(x, *) // x* ⛔️


Используется для генерации чего угодно. Часто думают только про имена функций/переменных/классов, но можно и для операторов, ключевых слов, литералы.

@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.
👍142