← На главную

Автор предлагает упростить C копированием массивов через `@` из GDB

25.05.2026 05:05 · hackernews

Массивы в C — странная штука. Технически тип T[n] — это не указатель, а последовательность значений. Но на практике любое выражение с таким типом мгновенно превращается в указатель на первый элемент T*. Индексация arr[ix] на самом деле работает с указателями — это просто *(arr + ix). Исключение — оператор sizeof: sizeof arr вернёт реальный размер массива, а не указателя.

Дальше — веселее. В аргументах функций T arr[n] — это иллюзия. Компилятор всё равно читает это как T*, а размер n просто выкидывает. Внутри функции sizeof(buf) уже вернёт размер указателя, а не массива. Можно написать char buf[static 8], но это лишь обещание компилятору, что длина будет минимум 8 — нарушение приводит к неопределённому поведению.

Есть обходной путь: передавать указатель на массив целиком, T (*)[n]. На уровне кода это то же самое, но длина сохраняется. Правда, синтаксис получается жутковатый.

Интересно, что функции ведут себя похоже, но гораздо понятнее. Имя функции тоже превращается в указатель, но &fn и fn — одно и то же. С массивами не так: &arr даёт указатель на массив, а не на первый элемент.

Автор предлагает мысленный эксперимент. Было бы гораздо проще, если бы C строго разделял массивы и указатели. Массивы работали бы как структуры: передача массива в функцию копировала бы его целиком. Чтобы получить указатель на первый элемент, пришлось бы писать &arr[0]] явно. Это сделало бы язык понятнее для новичков — не было бы сюрприза, когда изменение массива внутри функции меняет оригинал. Да, массивы бы копировались чаще, но компилятор мог бы оптимизировать это за кулисами.

Для сборки массива из указателя автор вспоминает оператор @ из отладчика GDB. Например, *ptr@10 создаёт массив из десяти элементов, начиная с того места, куда указывает ptr. При этом ptr должен быть именно выражением-местом (lvalue), а не просто числом. Такой оператор мог бы естественно описывать срезы: iota[1]@2 даёт массив из двух элементов. Не хватает только удобного способа сдвинуть начало массива без явного указания новой длины — автор оставляет этот вопрос открытым.

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

Читать оригинал →