Как стать автором
Обновить

Комментарии 17

_countof — микрософтовское расширение. Начиная с C++17 есть стандартный std::size()
Спасибо! Честно говоря не знал. Но _countof() MinGW тоже поддерживает.
На сегодня, увы, std::size не является полной заменой _countof в MSVC.
Компилятор MSVC не умеет заглядывать внутрь constexpr функций и подставлять значение.

Поэтому тут компилятор не видит проблемы
int a[10];
unsigned char x = 10 + (sizeof(a) / sizeof(a[0]));

// и даже так
 constexpr size_t a_number_of_elements = sizeof(a) / sizeof(a[0]);
unsigned char y = 10 + a_number_of_elements


А здесь видит уменьшение размера size_t => unsigned char
int a[10];
unsigned char x = 10 + std::size(a);
int b1[0];

Помнится, данное объявление порой использовалось когда-то в С++ (но уже не припомню, в какой именно реализации С++) в структурах для обозначения произвольных данных неопределенной длины (от нуля и больше) в конце структуры.
И оно даже работало.

Оно и сейчас используется, правда я видел в варианте char data[]; // вот прямо так, без размера.
Компилятор выдаёт предупреждение, если это окажется не последнее поле в структуре. В том числе при наследовании.
Попробовал в MSVS 2019 standard C++17.
struct S
{
	int x;
	int z[];
};

Действительно компилируется, предупреждение Warning C4200 nonstandard extension used: zero — sized array in struct / union
Но память под такой массив не выделяется. Попытка доступа компилируется, но при выполнении ошибка: выход за границу стека. При попытке сделать такую структуру базовой или членом возникает ошибка. Не понятно, как это можно использовать (ну может быть только в union).

Раньше это обычно использовали для обработки потоков данных, предваряемых заголовочными структурами, примерно так: получали откуда-то буфер (чтение из файла, вызов функции) и присваивали указатель на него указателю на структуру. Или наоборот, указателю на структуру выделяли требуемую память, а потом ее подсовывали в качестве буфера какой-нибудь функции, это дело заполняющей.
Если память не изменяет, подобный фокус применялся даже в заголовочных файлах библиотеки какой-то СУБД.
Иногда использовали грязный хак, когда за структурой (или даже за массивом нулевой длины) объявляли различные переменные, и обращались потом к ним как к массиву байт. Понятно, что это UB, и оно работало стабильно корректно только при отключенной оптимизации и только в некоторых компиляторах.

Спасибо! Сейчас вспоминаю, что и я когда-то с такими штучками сталкивался, правда довольно давно.
Да в C++ позволяет много интересного делать с массивами:
#include <stdio.h>

struct A { int x=1; };
struct B : A { int y=2; };

int main() {
	A* a=new B[2];
	printf("%d %d\n",a[0].x,a[1].x);
	delete[] a;
	return 0;
}
// outputs: 1 2
В этом примере наглядный пример, как нельзя обращаться к памяти интерфейсами разных классов. Но пример интересный, да
Это же UB — мы не привели a к B* в третьем аргументе printf.
Это иллюстрация довольно известного совета — никогда не работайте с массивами в полиморфном стиле, через базовый класс.
std::array<int, 4> a{1, 2, 3, 4};

C++17 позволяет проще:
std::array a{1, 2, 3, 4};


См. en.cppreference.com/w/cpp/language/class_template_argument_deduction

К сожалению, std::array не полностью аналогичен массиву C.
Размер std::array может быть больше аналогичного массива C, потому как это структура и можем иметь дополнительное выравнивание.

Спасибо, что обратили внимание. Вообще автоопределение типа по списку в фигурных скобках может дать сюрпризы, здесь правила несколько запутанны. Например массив нельзя объявить auto, я писал об этом. Надо каждый раз проверять, что результат будет ожидаемый. Ну а печалится, что std::array побитово не совпадает с массивом, по моему, не стоит. Мы не должны программировать полагаясь на знание внутренней структуры объекта, мы должны использовать только интерфейс.
Не должны, но не хочется иметь пессимизацию на ровном месте.

std::array всем хорош, кроме конструкции многомерного массива - вот там нечитаемо получается.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории