Массив, объявленный с отрицательным буфером, работает, если выполняется с переменной

#arrays #c

#массивы #c

Вопрос:

Я новичок в программировании на C, и я изо всех сил пытаюсь понять, почему это работает

 #include <stdio.h>


int main() {
    int l = -5;
    char arr[l];

    printf("content: %s; sizeof: %dn", arr, sizeof(arr));
}
 

с выводом:

content: ; sizeof: -5

и это не значит:

 #include <stdio.h>


int main() {
    char arr[-5];

    printf("content: %s; sizeof: %dn", arr, sizeof(arr));
}
 

с выводом:

 name.c:6:10: error: size of array ‘arr’ is negative
    6 |     char arr[-5];
      |          ^~~
 

Я ожидал ошибки и в первом примере, но я действительно не знаю, что здесь происходит.

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

1. Спецификатор формата для результата sizeof (который есть size_t ) есть %zu . Это прояснит одну из загадок здесь.

2. о, я понял, спасибо. Кстати, это выглядит как огромное число, но проблема все еще не решена

3. Я думаю l , что from char arr[l] преобразуется в size_t который является типом без знака. Для печати sizeof вам необходимо %zu получить правильный вывод из printf

4. @bolov Мне просто интересно, почему литерал -5 также не преобразуется. Вероятно, потому, что это определение, а не утверждение «а».

5. Ах, наверное, потому -5 , что это не литерал, а скорее выражение с унарным - оператором и литералом 5

Ответ №1:

Ни одна из версий программы не соответствует спецификации языка C (даже после исправления спецификаторов формата для правильного соответствия size_t аргументу). Но эти два случая семантически различны, и они нарушают разные положения спецификации языка.

Сначала рассмотрим это:

     char arr[-5];
 

Выражение -5 является целочисленным постоянным выражением, поэтому это объявление обычного массива (не массива переменной длины). Это подпадает под действие пункта 6.7.6.2 / 1 спецификации языка C17, в котором частично говорится:

В дополнение к необязательным определителям типа и ключевому static слову, [ и ] может ограничивать выражение или * . Если они ограничивают выражение (которое определяет размер массива), выражение должно иметь целочисленный тип. Если выражение является постоянным выражением, оно должно иметь значение больше нуля.

(Курсив добавлен.)

Это является частью языкового ограничения, что означает, что компилятор обязан выдавать диагностическое сообщение при обнаружении нарушения. В принципе, реализации не обязаны отклонять код, содержащий нарушения ограничений, но если они принимают такие коды, то язык не определяет результаты.

С другой стороны, рассмотрим

     int l = -5;
    char arr[l];
 

Поскольку l не является постоянным выражением (и не было бы, даже если l бы было объявлено const ), обсуждаемое выше положение не применяется и, отдельно, arr является массивом переменной длины. Это регулируется пунктом 6.7.6.2 / 5 спецификации, соответствующей части, требующей выражения размера, которое:

каждый раз, когда он вычисляется, он должен иметь значение больше нуля

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

Почему эта программа генерируется -5 при компиляции и запуске ее с вашей конкретной реализацией C на вашем конкретном оборудовании, не установлено C. Это может быть указано вашей реализацией, а может и нет. Небольшие изменения в программе или разные версии вашей реализации на C могут привести к разным результатам.

В целом, это еще один пример того, как C отказывается держать вас за руку. Новые программисты и те, кто привык к интерпретируемым языкам и виртуальным машинам, похоже, часто ожидают, что какой-то компонент системы сообщит им, когда они напишут плохой код. Иногда это так, но в других случаях он просто делает что-то с этим плохим кодом, который может напоминать или не напоминать то, что имел в виду программист. Эффективное программирование на C требует внимания к деталям.

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

1. «еще один пример того, как C отказывается держать вас за руку». —> Да, случайный пользователь, добро пожаловать в программирование без обучающих колес .

2. Большое вам спасибо за ваше объяснение, это было действительно полезно! Также спасибо за «жизненный урок» о C, я знал, что это будет сложно, и я знаю, что много лет назад я поступил неправильно, начиная с Python.