Соответствуют ли параметры массива VLA?

Стандарт безопасного кодирования CERT включает item (API05-C), который поощряет использование соответствующих параметров массива, что является рекомендацией, которую я реализовал во многих частях моего кода (скрытых за макрос для компиляторов, которые их не поддерживают).

Для тех, кто не знает, соответствующий параметр массива выглядит примерно так:

void foo(int length, char data[length]);

API05-C предоставляет дополнительную информацию.

Многие компиляторы не любят массивы переменной длины (не зря). C11 понижает их уровень с обязательных (как в C99) до необязательных (компиляторы должны определять __STDC_NO_VLA__, если они не реализованы). MSVC категорически не поддерживает их. IAR прячет их за переключателем (--vla). GCC и clang предупредят вас о них, если вы спросите (с -Wvla или -Werror=vla, если вы хотите получить ошибку).

Параметры согласованного массива не страдают от тех же проблем, что и «нормальные» массивы переменной длины; они не приводят к использованию стека переменных. Они просто сообщают компилятору, насколько велик существующий массив, который может находиться в стеке или куче.

Моя проблема в том, что каждый известный мне компилятор обрабатывает параметры согласованного массива как VLA. Это не такая уж большая проблема для таких компиляторов, как MSVC, поскольку я могу просто определить свой макрос до нуля, но для таких компиляторов, как GCC и clang, я хочу использовать соответствующие параметры массива, но не хочу запускать -Wvla диагностику.

Согласно API05-C (выделено автором):

Следовательно, объявление массива, которое служит аргументом функции, может иметь индекс, который является переменной или выражением. Аргумент массива понижается до указателя и, следовательно, не является массивом переменной длины (VLA). Соответствующие параметры массива могут использоваться разработчиками для указания ожидаемых границ массива. Эта информация может использоваться компиляторами или игнорироваться. Однако такие объявления полезны для разработчиков, поскольку они служат для документирования взаимосвязей между размерами массивов и указателями. Эта информация также может использоваться инструментами статического анализа для диагностики потенциальных дефектов.

Я отчаянно хочу, чтобы это было правдой, но я не могу найти соответствующие части C99 или C11.

Итак, основаны ли строго на стандартах C99 / C11, соответствуют ли параметры массива VLA? Или, говоря другими словами, действительно ли передача массива в качестве аргумента понижает его статус до указателя?

Разумеется, просьба указать соответствующие части спецификации.


person nemequ    schedule 22.04.2018    source источник
comment
Если я понимаю ваш вопрос, и вы спрашиваете о такой функции, как type funcname (int a, int array[a]) { ... }, тогда да, array - это VLA (по определению это не может быть ничем другим, 'a' не является константой). Кроме того, вы можете предпочесть стандарт C11 (проект n1570), а не .pdf форма.   -  person David C. Rankin    schedule 22.04.2018
comment
does passing an array as an argument really demote it to a pointer? Да. Было бы очень тяжело этого не сделать.   -  person Alceste_    schedule 22.04.2018
comment
Дэвид: Да, это то, о чем я спрашиваю; отредактирован, чтобы сделать это яснее. Как я уже упоминал (и упоминал Alceste_), неясно, действительно ли это массив, и в этом случае он не может быть массивом переменной длины.   -  person nemequ    schedule 22.04.2018
comment
Alceste_: вопрос здесь не в том, как это реализовано компилятором, а скорее в том, как это обрабатывается спецификацией C.   -  person nemequ    schedule 22.04.2018
comment
Примеры не являются нормативными, но Пример 4 из 6.7.6.2p10 вызывает C в декларации void fvla(int m, int C[m][m]); VLA. (Нормативный) язык в 6.7.6.2 указывает, что да, эти параметры массива считаются массивами переменной длины.   -  person Cornstalks    schedule 22.04.2018
comment
Однако я думаю, что есть разница между разговорным использованием VLA и стандартным использованием этого термина (например, sizeof не работает с этими параметрами VLA, как с обычными VLA, потому что эти параметры VLA на самом деле являются просто указателями).   -  person Cornstalks    schedule 22.04.2018
comment
@Cornstalks Он объявлен как многомерный массив, это другое.   -  person    schedule 22.04.2018
comment
Можете ли вы объяснить, что вы имеете в виду, говоря, что каждый компилятор, о котором я знаю, рассматривает соответствующие параметры массива как VLA?   -  person M.M    schedule 22.04.2018
comment
@ M.M: если вы используете соответствующий параметр массива в IAR, который не поддерживает VLA и определяет __STDC_NO_VLA__, вы получите сообщение об ошибке (если вы не передадите --vla, чтобы включить их). GCC и clang будут предупреждать о CAP, если вы передадите -Wvla.   -  person nemequ    schedule 22.04.2018
comment
Хехе, Брайану Кернигану это не понравилось, эта неудача - самый большой сингл проблема с Паскалем. И мы идем по кругу. Это по-прежнему указатель во время выполнения, вам просто не нужно угадывать, насколько велик массив.   -  person Hans Passant    schedule 22.04.2018
comment
Как бы то ни было, это очень хорошее правило CERT. ИМО - огромная ошибка - делать VLA необязательным в C11, потому что основное использование VLA - это не фактическое распределение массива, а, скорее, указатели на VLA. Комитету не удается понять, как писать современный C, такой как int (*ptr)[y] = malloc( sizeof( int[x][y] ) );, или функции, подобные этим, рекомендованным CERT.   -  person Lundin    schedule 23.04.2018
comment
@HansPassant: Паскаль должен был включать концепцию указателя на массив размера, вычисляемого во время выполнения, и, возможно, указателя на сегмент массива, но с другой стороны, C должен был иметь синтаксис, который указывал бы указатель на start массива по сравнению с указателем на некоторый произвольный элемент в массиве. Учитывая, например, for (int i=0; i<100; i++) a[i] += b[i];, компилятор может безопасно векторизовать цикл, если каждый a и b идентифицируют начало массива, но не если a и b могут указывать на разные элементы одного и того же массива, например, b < a < b+100.   -  person supercat    schedule 23.04.2018


Ответы (2)


Все параметры, объявленные как типы массивов, преобразуются в типы указателей. VLA - не исключение.

N1256 (C99+TC1+TC2+TC3):

6.7.5.3 Деклараторы функций (включая прототипы)

7 Объявление параметра как массива типа должно быть скорректировано для квалифицированного указателя на тип, где квалификаторы типа (если есть) - это те, которые указаны в [ и ] производного типа массива. Если ключевое слово static также появляется в [ и ] в выводе типа массива, то для каждого вызова функции значение соответствующего фактического аргумента должно обеспечивать доступ к первому элементу массива с по крайней мере таким количеством элементов, как указано. по выражению размера.

Функция, объявленная void f(int a[10]);, принимает любые int *. Функция, объявленная void f(int length, int array[length]);, принимает int и int *.

Однако вы пишете:

Моя проблема в том, что каждый известный мне компилятор обрабатывает параметры согласованного массива как VLA. Это не такая уж большая проблема для таких компиляторов, как MSVC, поскольку я могу просто определить свой макрос до нуля, но для таких компиляторов, как GCC и clang, я хочу использовать соответствующие параметры массива, но не хочу запускать -Wvla диагностику.

Что ж, это сложно. Это VLA до того, как он будет преобразован в указатель, а суть -Wvla состояла в том, чтобы предупредить о коде, который не сможет скомпилировать на компиляторах, не поддерживающих VLA. Как вы видели, MSVC не нравится код.

Параметры согласованного массива не страдают от тех же проблем, что и обычные массивы переменной длины; они не приводят к использованию стека переменных. Они просто сообщают компилятору, насколько велик существующий массив, который может находиться в стеке или куче.

Нет, они этого не делают. Учитывая void f(int a[10]);, вполне допустимо вызывать f с нулевым указателем, с указателем на массив длиной 1 и т. Д. Компилятор должен это поддерживать. Они служат только намеком для читателей. То же самое и с преобразованными VLA.

person Community    schedule 22.04.2018
comment
Возможно, вопрос также заключается в том, чтобы спросить, должна ли реализация, не поддерживающая VLA, допускать случай, когда нижний индекс в деклараторе является непостоянным выражением. - person M.M; 22.04.2018
comment
@ M.M Возможно, вы правы, но я перечитал вопрос, и хотя я нашел пропущенные мной вещи, которые мне нужно было включить в свой ответ, я не вижу именно этого. - person ; 22.04.2018
comment
Меня меньше интересует, как ведут себя компиляторы, чем то, как они должны вести себя, учитывая только стандарты C. Например, компилятор, который поддерживает C11, но не поддерживает VLA (и определен __STDC_NO_VLA__) и не может скомпилировать соответствующие параметры массива, по-прежнему строго соответствует стандарту? - person nemequ; 22.04.2018
comment
@nemequ Хорошо. Параметры функции - это VLA до преобразования в типы указателей (непосредственно из определения VLA), поэтому, если реализация отклоняет их в этот момент, похоже, что в C11 нет ничего, что запрещало бы такую ​​реализацию. В этом есть смысл: это недействительный C90, он стал действительным C99 из-за введения VLA, но это изменение C99 стало необязательным для C11 в целом. - person ; 22.04.2018
comment
@hvd: Требует ли Стандарт, чтобы реализация, которая не поддерживает VLA, проверяла аргументы массива в списках параметров, должна гарантировать, что самое внешнее измерение является либо неопределенным, либо постоянным, либо реализация может законно просто игнорировать это измерение (поскольку это не повлияет на поведение программы в любом случае)? - person supercat; 24.04.2018
comment
@supercat Лично я не думаю, что это отличается от того, что void f(void p[10]) является ошибкой, даже если void f(void *p) является допустимым: преобразование параметра функции из массива в указатель требует, чтобы параметр действительно имел допустимый тип массива. В реализациях, которые не поддерживают VLA, VLA не являются допустимым типом массива. - person ; 24.04.2018
comment
@hvd: Одна из целей разработки C состояла в том, чтобы позволить компиляторам быть как можно более легкими, но при этом иметь возможность создавать достойный код. Требование, чтобы компиляторы явно проверяли то, о чем в противном случае у них не было бы причин для беспокойства, похоже, противоречит этой цели. Кроме того, некоторые компиляторы могут внутренне поддерживать указатели на типы VLA, но не поддерживать автоматические объекты такого типа; следует ли запретить компиляторам, которые не могут поддерживать второе, поддерживать первое? - person supercat; 24.04.2018
comment
@supercat Приведенный мною пример представляет собой случай, когда от компиляторов требовалось явно проверять то, о чем в противном случае у них не было бы причин заботиться со времен C90. Что касается реализаций, поддерживающих подмножество функций VLA, им просто нужно будет заявить, что они не поддерживают VLA, они все равно могут реализовать требуемое подмножество в качестве расширения. - person ; 24.04.2018
comment
@hvd: если компилятор проанализировал до void f(int p[, а следующий непробельный символ не является ], существуют ли случаи, когда допустимая программа, не относящаяся к VLA, потребует выполнения чего-либо, кроме поглощения токенов, пока количество ] токенов не превысит количество [ токенов, а затем ведет себя так, как будто до этого типа было *? Я знаю квалификаторы, такие как restrict, входят в [], но есть ли случаи, когда обработка таких директив потребовалась бы для достижения правильной семантики (в отличие от простого облегчения оптимизации)? - person supercat; 24.04.2018

Это не VLA, а просто указатели. Если вы расширите это правило на более чем одно измерение, вы получите изменяемый тип VM, но все же не VLA.

Если вы хотите указать, что указатель не должен быть 0, используется static, как в

void Toto(size_t n, double vec [static n]);

что указывает на то, что vec должен иметь не менее n элементов и, в частности, не может быть нулевым.

person Jens Gustedt    schedule 22.04.2018
comment
Итак, чтобы было ясно, если компилятор, который поддерживает C11, но не VLA (и определяет __STDC_NO_VLA__) и не может скомпилировать соответствующие параметры массива, он не соответствует стандарту C11? - person nemequ; 22.04.2018
comment
Стандарт мне кажется довольно ясным, что декларатор имеет тип VLA перед его настройкой на тип указателя. - person M.M; 22.04.2018
comment
@nemequ, теоретически да, но я не думаю, что такой компилятор существует. - person Jens Gustedt; 22.04.2018
comment
IAR. Он будет вести себя точно так же, если вы не передадите --vla. - person nemequ; 23.04.2018