Плохо ли указывать размер массива с помощью переменной вместо `#define` в C++? (Ошибка C: изменяемая в области видимости файла)

В C, объявление размера массива с помощью переменной, даже если это const переменная, НЕ допускается. Пример: это не компилируется в C:

#include <stdio.h>

const int SIZE = 2;
int a[SIZE];

int main()
{
    a[0] = 1;
    a[1] = 2;
    printf("%i, %i", a[0], a[1]);       
    return 0;
}

Запустите этот код на C.

Выход:

$gcc -o main *.c
main.c:5:5: error: variably modified ‘a’ at file scope
 int a[SIZE];
     ^

В C++, однако, он работает просто отлично.
Запустите приведенный выше код на C++.

Выход:

$g++ -o main *.cpp
$main
1, 2

Чтобы запустить его в C, вы должны использовать #define вместо переменной. то есть:

Это прекрасно работает в C или C++:

#include <stdio.h>

#define SIZE 2
// const int SIZE = 2;
int a[SIZE];

int main()
{
    a[0] = 1;
    a[1] = 2;
    printf("%i, %i", a[0], a[1]);
    return 0;
}

Запустите этот код на C.

Итак, в C++ я почти всегда использовал переменную, а не #define, чтобы объявить мои размеры массива. Я просто делаю переменную размера массива const и это все хорошо! Недавно я начал много программировать микроконтроллеры на чистом C, однако, когда я столкнулся с этой ошибкой и выяснил проблему, старший разработчик сказал мне, что использовать что-либо кроме #define константы (или, возможно, жестко запрограммированные числа) для объявления размеров массива.

Это правда? Это плохая практика в C++, чтобы использовать const переменные вместо #define при указании размеров массива? Если так, то почему?

В С, видимо, вы застряли с #define У тебя нет другого выбора. Но в C++ у вас явно есть как минимум 2 варианта, так что один лучше другого? Есть ли риск использования одного над другим?

Связанные с:

  1. переменно модифицированный массив в области видимости файла в C
  2. static const vs #define <- это серьезный вопрос и очень полезный. Это наиболее определенно связано с моим вопросом, но мой вопрос НЕ является дубликатом, потому что, хотя они оба о const vs #define, мой вопрос - это особый случай, когда один из вариантов даже не работает на языке, который регулярно считается подмножеством C++. Это довольно необычно и делает мой вопрос более узким подмножеством, которое вписывается в широкую сферу этого другого вопроса. Поэтому не дубликат.
  3. https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es31-dont-use-macros-for-constants-or-functions
  4. "static const" против "#define" против "enum"
  5. https://en.wikipedia.org/wiki/Variable-length_array

3 ответа

Было бы хорошо последовать совету Скотта Мейера в этом вопросе. Из его книги "Эффективный С ++":
Пункт 2: Предпочитайте констант, перечислений и строк в #defines.

Краткое описание предмета адаптировано к вашему примеру.

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

Когда вы делаете что-то подобное,

#define SIZE 2

символическое имя SIZE может никогда не быть замечено компиляторами; он может быть удален препроцессором до того, как исходный код попадет в компилятор. В результате название SIZE не может быть введен в таблицу символов. Это может сбивать с толку, если во время компиляции вы получаете ошибку, связанную с использованием константы, поскольку сообщение об ошибке может относиться к 2не SIZE, Если SIZE были определены в заголовочном файле, который вы не написали, вы бы не знали, где это 2 пришел, и вы бы потратили время на его поиск. Эта проблема также может возникать в символическом отладчике, поскольку, опять же, имя, с которым вы программируете, может отсутствовать в таблице символов. Решение состоит в том, чтобы заменить макрос на константу:
const double SIZE = 2; // uppercase names are usually for macros, hence the name change
Как языковая константа, SIZE определенно виден компиляторам и определенно заносится в их таблицы символов.

Simple Для простых констант предпочитайте константные объекты или перечисления #defines.
Function Для функционально-подобных макросов предпочтите встроенные функции #defines.

Также см. "Пункт 3: Используйте const, когда это возможно". для получения дополнительной информации о его использовании и исключениях из его использования.

Итак, чтобы ответить на ваш вопрос в заголовке:
Нет, это НЕ плохая практика указывать размер массива, используя переменную вместо #define в C++.

Мне нравится твой вопрос! C++ развивается до языка, который вам больше не нужен #define, В зависимости от версии вы можете заменить другие конструкции. Это объясняется несколькими причинами, в том числе проблемами с IDE, неожиданной заменой имен переменных значений enum другими константами (не объединять ERROR и) и отсутствием пространств имен, поскольку все это замена препроцессора.

Для этого примера вы создаете массив в стиле C, который требует константы. Пишу const int SIZE = 2 однако не создает константу, она создает неизменяемую переменную. Это может быть использовано многими способами и может быть инициализировано практически с любым кодом, включая то, что вы вычислили из чтения файла.

То, что вы ищете, это constexpr, Ключевое слово, которое переводит вашу переменную в постоянную времени компиляции и ограничивает параметры инициализации всем кодом, который может быть выполнен во время компиляции. Итак: константы, вычисления с ними и функции constexpr, которые группируют эти вычисления. Если вы хотите узнать больше об этом, Google для constexpr (или использовать другую поисковую систему).

Примечание C++11 требуется для constexpr, C++14 рекомендуется, когда вы хотите написать функции constexpr. Для стандартных библиотечных функций constexpr вам придется подождать C++20, если я хорошо помню.

constexpr int SIZE = 2;
int a[SIZE];

Так как SIZE был определен как const переменная. В C, в отличие от C++ const значения не являются истинными константами. Они хранятся в памяти и могут ссылаться и изменяться косвенно (путем изменения содержимого в памяти). Вот почему в C++ есть const это позволило бы const int SIZE = 2; работать, но даже const недостаточно в C.

С11-§6.7.9/3:

Тип объекта, который должен быть инициализирован, должен быть массивом неизвестного размера или полным типом объекта, который не является типом массива переменной длины.

Макрос препроцессора заменяют во время компиляции, поэтому он работает нормально.

Ссылка Ссылка: /questions/1619299/pochemu-int-xn-ne-tak-gde-n-eto-konstantnoe-znachenie/1619307#1619307

Другие вопросы по тегам