2012-01-07 3 views
1

Рассмотрим следующий кодуказатели, указывающие непрерывной памяти

struct foo 
{ 
    const int txt_len; 
    const int num_len; 

    char * txt; 
    int * num; 

    foo(int tl, int nl): txt_len(tl), num_len(nl) 
    { 
     char * tmp = new char[txt_len * sizeof(char) + num_len * sizeof(int)]; 

     txt = new (tmp) char [txt_len * sizeof(char)]; 
     num = new (tmp + txt_len * sizeof(char)) int[num_len * sizeof(int)]; 

     // is this the same as above? 
     // txt = tmp;         
     // num = (int *) (tmp + txt_len * sizeof(char)); 
    } 

    ~foo() 
    { 
     delete[] txt; // is this the right way to free the memory? 
    } 
}; 

Я хочу, *txt и *num быть смежными, является то, что лучший способ сделать это?

также есть разница между размещением нового и арифметикой указателя? который я должен использовать?

+0

Я понятия не имел, что вы можете использовать новый на существующей памяти таким образом. +1 для вас. – rsaxvc

+1

Трудно дать вам полезный ответ, не понимая, почему вам нужна непрерывная память. Это действительно зависит от того, что вы планируете с ним делать. (Например, планируете ли вы использовать указатели разыменования? Если да, то какого типа? Вы хотите, чтобы промежуток имел нормальную структуру? Или вы хотите, чтобы он упакован?) Ваш код выше незаконный, вы не можете пройти размещение новых указатель, который не выровнен. –

+3

Сначала введите 'int', так как он имеет более строгие требования к выравниванию. И 'sizeof (char)' гарантированно будет точно '1'. –

ответ

1

Если вы хотите непрерывный блок памяти, вы должны выделить его целиком одним звонком на operator new[] или malloc() или аналогичным. Несколько вызовов этих функций не гарантируют смежности выделенных блоков. Вы можете выделить большой блок, а затем вырезать части из него по мере необходимости.

И вы должны delete и free() все блоки, ранее наделенный new и malloc(), в противном случае вы будете утечка памяти и, возможно, сделать вашу программу нестабильным (это не выделить больше памяти в какой-то момент) и оказывают ненужное давление на памяти ОС, возможно, замедляя другие программы или делая их нестабильными.

Размещение новое, однако, фактически не выделяет никакой памяти. Он просто создает объект в указанном месте и поэтому вам не нужно освобождать эту память дважды.

Одна из проблем, которые я вижу в вашем коде, заключается в том, что она не выравнивает ints. На некоторых платформах чтение или запись целых чисел, превышающих 1 байт из/в память, должно быть выровнено, а если это не так, вы можете либо считывать/записывать значения из/в неправильные местоположения, либо получать исключения процессора, приводящие к прекращению вашей программы. X86 очень разрешителен в этом отношении и не будет возражать, хотя может нанести вам урон с ухудшенной производительностью.

+0

Я не уверен, что вы думаете. Похоже, вы подтверждаете, что код OPs правильный, кроме проблемы с выравниванием. Это правильно? –

+0

@AaronMcDaid: Да. –

+0

OK, извините. Когда я прочитал предложение с «... вы должны выделить его целиком с помощью одного вызова оператору new [] ...» Я думал, вы имели в виду, что вы думали, что OP использовал несколько вызовов. Но тогда понятно, что вы понимаете, что только один из вызовов OP действительно выделяет новую память. Спасибо за это уточнение. –

0

Это то же самое, если вы используете типы POD. И ваше удаление в порядке.
Однако, как говорится в комментарии Дэвида, вам необходимо рассмотреть проблемы выравнивания.

+3

Что делать, если txt_len равно 3, а целые числа должны быть выровнены на 4-байтных границах на его платформе? –

+0

@ david: хорошая точка – Dani

0

Размещение нового в основном используется, если вы хотите вызвать конструктор класса/структуры на некоторые предварительно выделенные блоки памяти.

Но для родных типов он не имеет никакого отношения к размещению новых & указатель арифметики.

Пожалуйста, исправьте меня, если я ошибаюсь.

0

Если txt и num всегда указывают на int и char, другие встроенные типы или другие типы, не требующие строительства, то нет. Вам не нужно размещать новое.

Если, с другой стороны, вы должны были сменить один из них на класс, требующий построения, т. Е. Изменить txt на тип std :: string, а затем использовать новое место размещения.

Размещение new позволяет вам вызвать конструктор, если хотите, объект по этому адресу. Встроенные типы имеют конструкторы по умолчанию, которые ничего не делают, если вы не инициализируетесь.

В обоих случаях вам необходимо выполнить арифметику указателей, только один способ хранения ответа в указателе, другой - передать новое место размещения, которое возвращает его вам для хранения в указателе, а затем вызывает конструктор.

1

Сначала вы должны сначала ввести данные int из-за проблем с выравниванием. Но мы не можем делать delete num[], так как тип неправильный - перед удалением он должен быть отброшен до char*.

char * tmp = new char[num_len * sizeof(int) + txt_len * sizeof(char)]; 

num = new (tmp) int[num_len]; 
txt = new (tmp + num_len * sizeof(int)) char [txt_len]; 

(Это делает либеральное использование того факта, что sizeof(char)==1)

Вы могли бы возникнуть соблазн сделать delete[] num, но Num имеет тип int*, и он был new «эд как char*. Так что вам нужно делать;

delete[] (char*) num; 
Смежные вопросы