На языке, который был написан для описания стандарта, не было бы проблем с вызовом такой функции, как:
void print_array(double *d, int rows, int cols)
{
int r,c;
for (r = 0; r < rows; r++)
{
printf("%4d: ", r);
for (c = 0; c < cols; c++)
printf("%10.4f ", d[r*cols+c]);
printf("\n");
}
}
для double[10][4]
, double[50][40]
или любого другого размера,при условии, что общее количество элементов в массиве было меньше rows*cols
.Действительно, гарантия того, что шаг строки для T[R][C]
будет равен C * sizeof (T)
, была разработана, среди прочего, чтобы можно было писать код, который мог бы работать с многомерными массивами произвольного размера.
НаС другой стороны, авторы Стандарта признали, что когда реализациям присваивается что-то вроде:
double d[10][10];
double test(int i)
{
d[1][0] = 1.0;
d[0][i] = 2.0;
return d[1][0];
}
, позволяющее им генерировать код, который предполагает, что d[1][0]
будет по-прежнему держать 1.0 при выполнении return
, илипозволяя им генерировать код, который будет перехватывать, если i
больше 10, позволит им быть более подходящим для некоторых целей, чем требование, чтобы они молча возвращали 2.0
, если вызвано с i==10
.
Ничегов стандарте проводится различие между этими сценариями.Хотя для Стандарта было бы возможно включить правила, в которых говорилось бы, что второй пример вызывает UB, если i >= 10
без влияния на первый пример (например, скажем, что применение [N]
к массиву не приводит к его затуханию доуказатель, но вместо этого выдает N-й элемент, который должен существовать в этом массиве), вместо этого стандарт опирается на тот факт, что реализациям разрешено вести себя полезным образом, даже если это не требуется, и авторы компилятора должны, вероятно, иметь возможностьраспознавание ситуаций, подобных первому примеру, когда это пойдет на пользу их клиентам.
Поскольку стандарт никогда не стремился полностью определить все, что программистам нужно было бы делать с массивами, не следует искать руководство относительно того, какие конструкциикачественные реализации должны поддерживать.