Как можно скопировать указатель на int (массив) в массив int?

#c #arrays

#c #массивы

Вопрос:

Поскольку вы не можете возвращать int[] (или любой другой тип массива, если уж на то пошло) в c , у меня есть функция или две, которые вместо этого возвращают int* , что довольно усложнило мое существование.

Функция моей программы требует, чтобы массив, который я передавал между функциями, копировался и увеличивался различными способами, а затем копия возвращалась. Единственный способ, который я нашел для копирования данных таким образом, чтобы исходный массив не подвергался изменениям в копии, — это копирование каждого «элемента» за раз, например

 int* foo(int* a) {
    int b[2] = {a[0], a[1]};
    /* do unspeakable things to b */
    return b;
}
  

Это достаточно хорошо для данного конкретного случая, но я уверен, что это не самый эффективный метод, особенно для больших массивов.

Есть ли какое-нибудь волшебство приведения типов или другой метод, который я могу использовать, чтобы сделать это более эффективно?


ПРИМЕЧАНИЕ: Я не спрашиваю, является ли это хорошей практикой, полезной, в соответствии с Женевской конвенцией или безопасной очень намеренно. В данный момент меня не особенно волнует эта информация, и если вам абсолютно необходимо это сказать, я бы предпочел, чтобы это не было телом или открытием вашего ответа.

Кроме того, является ли кернинг вокруг T в NOTE супер странным для кого-либо еще?

РЕДАКТИРОВАТЬ: После фактического использования моего «решения» я решил, что я идиот и должен протестировать вещи, прежде чем разглагольствовать о них.

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

1. «Есть ли какое-нибудь волшебство приведения типов или другой метод, который я могу использовать, чтобы сделать это более эффективно?» Использовать циклы? Или std::memcpy ? Почему вас вообще волнует эффективность, когда вы не хотите слышать идиоматические решения (которые, очевидно, являются лучшими)? Вы хотя бы знаете, действительно ли вам нужно копировать, что само по себе может быть неэффективным вместо перемещения? Так много вопросов…

2. К сожалению, вы не можете вернуть необработанный массив. Он распадается на указатель, и массив выходит за пределы области видимости, оставляя указатель, указывающий на недопустимую память. Разрешено ли вам использовать std::array вместо этого?

3. Кроме того, является ли кернинг вокруг T в NOTE супер странным для кого-либо еще? Да.

4. «Что достаточно хорошо для этого очень конкретного случая», — нет, это не так (вы возвращаете указатель на локальный массив)

5. Есть ли причина не использовать std::vector<int> ? Вектор может быть передан в функцию как копия и возвращен как копия (или передать цель по ссылке).

Ответ №1:

 int b[2];
int * c = b;
  

Из неявного преобразования cppreference:

Преобразование массива в указатель
Значение lvalue или rvalue типа «массив из N T» или «массив с неизвестной границей T» может быть неявно преобразовано в значение pr типа «указатель на T».

Это означает, что любой массив типа неявно преобразуется в указатель на type. Указатель указывает на первый элемент массива, поэтому c == amp;b[0] . Интересно, что указатель на array также равен указателю на первый элемент массива amp;b == amp;b[0] , но имеет другой тип, т.Е.:

 int b[2];
int * c = b; // implicit conversion from array to pointer
int (*d)[2] = amp;b; // amp;b is a pointer to an array of 2 ints
int * e = amp;b[0]; // pointer to the first element
  

Функция:

 int* foo(int* a) {
    int b[2] = {a[0], a[1]};
    /* do unspeakable things to b */
    return b;
}
  

Является недопустимым, неправильным, сбивающим с толку и плохим. Память, находящаяся за ним, b недействительна после } возврата функции, поэтому указатель, возвращаемый функцией, недействителен и не может быть использован.

Поскольку вы не можете вернуть int[]

int[] это массив неопределенного размера, вы ничего не можете с ним сделать. Обратите внимание, что внутри список параметров функции int[] неявно преобразуется в int* . Итак, следующее в точности эквивалентно:

 void f(int a[]);
void f(int *a);
  

или любой тип массива, если на то пошло) в c

Вы можете вернуть std::array<int , 2>

  std::array<int, 2> f() {
        std::array<int, 2> ret;
        return ret;
 }
  

Обратите внимание, что это крайне неэффективно. Он должен возвращать 2 * sizeof(int) данные, которые будут увеличиваться с увеличением размера. Возврат указателя на первый элемент массива возвращает только значение указателя, для этого требуется меньше места. Также обратите внимание, что std::array<int, 2> var = f(); все элементы массива будут copy находиться между f() возвращаемым значением и var (без исключения копирования), поэтому это очень неэффективно.

Я уверен, что это не самый эффективный метод

С точки зрения скорости, явная инициализация каждого элемента массива и возврат указателя на первый элемент массива является наиболее эффективным методом. Надлежащий компилятор сгенерирует быструю инструкцию для этого. Но вы говорите and then the copy returned — вам нужно создать копию в вашей функции массива, поэтому вам нужно скопировать все элементы массива между оригиналом и копией. Другого пути нет.

Есть ли какое-нибудь волшебство приведения типов или другой метод, который я могу использовать, чтобы сделать это более эффективно?

Выбросьте необработанный массив C в корзину и перейдите к контейнерам. Перейдите к std::array<int, 2> , если вы знаете размер во время компиляции, и std::vector<int> если размер неизвестен во время компиляции.

 std::vector<int> foo(std::vector<int> a) {
    std::vector<int> b{a};
    return b;
}
  

Обратите внимание, что здесь return b все в порядке, так как std::vector<int> сам по себе хранит указатель на динамически выделяемую память. Память, выделенная с помощью new , не перестает существовать после } , поэтому вы можете вернуть указатель на нее. Если ваш компилятор поддерживает исключение копирования, функция должна работать быстро. Более быстрым вариантом было бы использовать ссылки.

  void f(std::vector<int>amp; ret, std::vector<int> a) {
       ret = a;
 }
  

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

1. И если вы работаете в std::array s (или любом другом библиотечном контейнере), проблемы с назначением в значительной степени исчезают. Вы можете передать по значению и получить бесплатную копию для работы без дополнительных усилий с вашей стороны.

2. Камиль, у тебя почти получилось то, о чем я говорил с std::vector<int> foo(std::vector<int> a) { std::vector<int> b{a}; return b; } . Вы можете упростить это до, std::vector<int> foo(std::vector<int> a) { return a; } поскольку параметр a является копией аргумента, используемого в вызове.

Ответ №2:

Вы можете ввести оба массива int в функцию и позволить функции выполнить копирование за вас следующим образом:

 bool CopyIntArray(int* pA, int cA, int* pB, int cB)
{
    if (pA == NULL || cA == 0 || pB == NULL || cB == 0)
    {
        return false;
    }

    for (int i = 0; i < cA amp;amp; i < cB; i  )
    {
        pB[i] = pA[i];
    }

    return true;
}

int main()
{
    int x[10] = { 0,1,2,3,4,5,6,7,8,9 };
    int y[10] = { 0 };

    CopyIntArray(x, 10, y, 10);

    return 0;
}

  

кстати

 int* foo(int* a) {
    int b[2] = {a[0], a[1]};
    /* do unspeakable things to b */
    return b;
}
  

b уничтожается при выходе из области действия функции, поэтому ваше приложение будет опасным при использовании уничтоженной переменной в качестве вывода функции.