2013-03-21 3 views
0

Я нашел этот код онлайн http://www.cplusplus.com/forum/beginner/6644/#msg30551, который должен возвращать массив из C++-функции. Я хотел бы объяснить, поясняющий, как эта функция работает в отношении распределения памяти, стеков, куч, указатели и т.д.Объясните, как эта функция C++ возвращает массив

int *f(size_t s){ 
    int *ret=new int[s]; 
    for (size_t a=0;a<s;a++) 
     ret[a]=a; 
    return ret; 
} 
+1

Он не возвращает массив. Он возвращает указатель на первый элемент динамически распределенного массива. – Nawaz

ответ

0

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

int *f(size_t s) // It will return a pointer (maybe first element of an array) 
{ 
    int *ret=new int[s]; // Allocates memory in heap, 
         // An array with s integral elements, 
         // And stores the address of the array in ret 

    for (size_t a=0;a<s;a++) 
     ret[a]=a; 

    return ret; // Returns the pointer which points first element of the array 
} 
// ... 
int *z = f(10); // z points to the first element of returned array 

// ... then you're responsible to free the allocated memory ... 
// delete [] z; 
3

I.

int *ret=new int[s]; 

1. выделить память для ret в стеке - это int указатель
2. выделить непрерывную память размером s * sizeof(int) на куче
3. макияжем ret для указания на первый элемент выделенной памяти (от 2.)


II.

for (size_t a=0;a<s;a++) 
    ret[a]=a; 
  1. не выделяет память в стеке для a
  2. петли через выделенную память в I., присвоение значений каждого элемент
  3. После окончания for -statement, a больше не доступен (он доступен только в for)

III.

return ret; 

возвращение копия ret указателя, который указывает на первый элемент созданного в I. массиве, инициализируется в II.

После return, ret «разрушен».


Вызывающие эта функция не должен забывать освобождать (бесплатно) эту память, вызывая delete[].

Например:

int * my_array = f(6); 
// do sth with my_array 
delete[] my_array; 
0
int *ret=new int[s]; 

Эта строка определяет int* под названием ret с автоматической продолжительности хранения. Он инициализирует ret с указателем, возвращаемым новым выражениемnew int[s]. Это новое выражение создает массив sint с динамической продолжительностью хранения, возвращая указатель на первый элемент в этом массиве.

Итак, теперь у нас есть два объекта: int* с автоматическим временем хранения и int[] с динамическим временем хранения.

for (size_t a=0;a<s;a++) 

Это заявление for. для сохр-заявление определяет size_t объект под названием a и инициализирует его в 0. состояние проверяет, является ли a меньше s. Конечное выражение приращений a. Это означает, что петли a находятся в диапазоне [0, s).

ret[a]=a; 

Это присваивает значение a к a го элемента в ret. То есть ret[0] будет иметь значение 0, ret[1] будет иметь значение 1 и так далее.

Объект a теперь уничтожен, так как он имел автоматическую продолжительность хранения, и мы достигли конца его объема (инструкция for).

return ret; 

Это возвращает значение ret, который, как вы помните, был int*. Поэтому возвращаемым значением функции является int*, указывающий на первый элемент динамически распределенного массива.

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

Вы должны, в некоторый более поздний момент, запомнить delete[] возвращенный указатель.

0
int *ret = new int[s]; 

Это динамически (~ в куче) выделяет массив целых чисел s и сохраняет указатель на него (на самом деле, чтобы его первый элемент) в ret.

Я считаю, что остальная часть функции проста.

Таким образом, функция возвращает указатель на динамически выделенный массив. Это небезопасно; если вызывающий абонент не сохраняет возвращаемое значение, а затем называет его delete[], он будет протекать.

1

На самом деле функция не возвращает массив int s (т. Е. int[N]). То, что он возвращает, является указателем на int (int *). Оказывается, этот указатель указывает на первый элемент массива из s элементов типа int.

Обратите внимание, что память выделяется new:

int *ret = new int[s]; 

Таким образом, массив int с указываемого ret имеет динамический срок хранения. В частности, это означает, что

1) компилятор не будет автоматически вызывать деструктор каждого элемента массива. (В этом случае это не проблема, потому что элементы имеют тип int, но могут быть, если вместо элементов, где тип класса с нетривиальным деструктором.)

2) компилятор не будет автоматически освободить выделенную память.

Для контраста рассмотрим следующий код:

void g() { 
    int p[10]; // allocates 10 integer in the stack 
    // use p ... 
} 

Когда g заканчивается компилятор будет выполнять вышеупомянутые операции. Для этого размер массива (10 в этом примере) должен быть установлен во время компиляции. Если вы не знаете размер во время компиляции, то вам нужно new, как в исходном коде.

Для динамически распределенных массивов ответственность программиста заключается в обеспечении выполнения двух вышеупомянутых операций, когда массив больше не нужен. Для достижения этой цели, вы должны вызвать delete[]:

delete[] p; // where p is a `int*` with the same value as `ret` 

На практике это гораздо сложнее, чем кажется, из-за возможности исключения бросают. Например, рассмотрит этот код

void foo() { 
    int* p = f(10); // where f is in the question 
    // ... use the array pointed by p 
    a_function_that_might_throw(); 
    delete[] p; 
} 

Если a_function_that_might_throw делает сгенерирует исключение, то выполнение никогда не reachs точки, где p удаляются. В этом случае память , выделенная new (внутри f), не будет освобождена (она протекает) до завершения программы .

Чтобы избежать этой проблемы, вместо исходного указателя (например int*), лучше использование смарт-указатель (например, std::unique_ptr или std::shared_ptr).

И, наконец, память, выделенная new, является памятью кучи. Однако, вы можете изменить это поведение.

Смежные вопросы