Указатель C на массив: почему нельзя использовать массив, определенный косвенно вычисляемым const var в функции begin(array)?

#c #arrays #pointers

#c #массивы #указатели

Вопрос:

Текстовое сообщение об ошибке:

Я изучаю книгу C Primer и сталкиваюсь с проблемой, указанной ниже, при написании ответа для одного упражнения:

 #include<iostream>
#include<vector>

using namespace std;

int main() {
int i  = 3;
const int ci = 3;
size_t si = 3;
const size_t csi = 3;
int ia[i];
int cia[ci];
int sia[si];
int csia[csi];
int another_a[] = {1,2,3};

int *pi = begin(ia);       // error here
// no instance of overloaded function "begin" matches the argument list -- 
// argument types are: (int [i])
int *pci = begin(cia);
int *psi = begin(sia);     // error here
// no instance of overloaded function "begin" matches the argument list -- 
// argument types are: (int [si])
int *pcsi = begin(csia);
int *p_ano = begin(another_a);

vector<int> v = {1,3,4};
const int m = v.size();
const size_t n = v.size();
int ma[m];
int na[n];
int *pm = begin(ma);    // error here
// no instance of overloaded function "begin" matches the argument list -- 
// argument types are: (int [m])
int *pn = begin(na);    // error here
// no instance of overloaded function "begin" matches the argument list -- 
// argument types are: (int [n])

system("pause");
return 0;
}
  

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

введите описание изображения здесь

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

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

1. Объявление массивов с непостоянными индексами не является стандартом c . Если вам нужны массивы с динамическим размером, используйте std::vector

2. Вы должны публиковать сообщения об ошибках или (лучше) соответствующий журнал сборки, включая версию компилятора в виде текста.

3. Я знаю, что я должен использовать const var для объявления массива, но я думаю, что на шаге «const size_t n = v.size()» я уже вычислил результат и сохранил его в const var.

4. Для массивов @GavinXu требуется константа времени компиляции (проще говоря, значение, которое может быть вычислено компилятором). У вас есть постоянная переменная, которая отличается. Компилятор не может обрабатывать размер произвольного вектора, пока программа не будет запущена.

5. @john Хотя код может выполняться нормально на этапе выполнения, но на этапе компиляции это объявление недопустимо. Это то, что вы имеете в виду, верно? Я понял!!! Большое спасибо!!!

Ответ №1:

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

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

Постоянное целое число, инициализированное постоянным выражением типа const int ci = 3; , может использоваться везде, где требуется постоянное выражение. So ci — это, по сути, само константное выражение (равное 3).

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

 constexpr int ci = 3;
  

Это точно так же, как ваш исходный код. Но то же самое не будет работать для const int m = v.size(); . Потому что constexpr требуется истинное постоянное выражение в качестве инициализатора, в отличие от const . Для const переменной не обязательно постоянное выражение. Это может быть просто переменная времени выполнения, которую вы не можете изменить. И это в случае с m .

Поскольку m это не постоянное выражение, то, что вы определили, является массивом переменной длины. Функция C, которая иногда вводится компиляторами C в качестве расширения. И это не соответствует std::begin шаблону, который ожидает, что экстент массива будет постоянным выражением.

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

1. Таким образом, размерность массива должна быть выражением константы времени компиляции, но не константой времени выполнения, верно? Если да, то теперь я знаю причину. Большое вам спасибо за ваше любезное и подробное объяснение!

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

3. Например, GCC будет жаловаться, если вы добавите -std=c 14 -pedantic-errors

4. Я просто использовал VS-code extensions C / C для изучения и настроил его в соответствии с некоторыми руководствами в Интернете. Спасибо за дополнительную информацию. И есть ли у вас какие-либо рекомендации? Должен ли я напрямую использовать Visual Studio?

5. @GavinXu — Вам не обязательно использовать полный VS. Я никогда не использовал VS-code, но слышал, что это неплохо. Просто проведите небольшое исследование, как настроить его, чтобы избежать расширений. Это поможет вам в обучении, и как только вы кое-что узнаете, вы сможете принять сознательное решение включить расширения.

Ответ №2:

Объявление массивов с непостоянными индексами не является стандартом c .

Если вам нужны массивы с динамическим размером, используйте std::vector .

Объявление переменной как const не делает ее константой времени компиляции (необходимой для объявления массива фиксированного размера), это просто означает, что вы не можете изменить ее после ее объявления.

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

1. Я понял это. Большое спасибо за вашу иллюстрацию!