Как сделать memset для указателя на массив?

Как сделать memset для указателя на массив?

int (*p)[2];

p=(int(*))malloc(sizeof(*p)*100);

memset(p,0,sizeof(*p)*100);

Является ли это распределение правильным?


person user1660982    schedule 03.10.2012    source источник
comment
Не приводить результат malloc, и пробовали ли вы это? С чего вы взяли, что это может быть неправильно?   -  person Eregrith    schedule 03.10.2012
comment
Вам нужно уточнить, хотите ли вы C или C++. Потому что для C++ все это не потребуется   -  person iammilind    schedule 03.10.2012
comment
я запутался, должен ли я написать memset(p,0,sizeof(*p)*100); или memset(p,0,sizeof(int[2])*100);   -  person user1660982    schedule 03.10.2012
comment
Я действительно не понимаю, что вы пытаетесь сделать... Я имею в виду, какова ваша цель (это не ясно из вашего кода).   -  person CAFxX    schedule 03.10.2012
comment
Всегда используйте sizeof(*your_variable), чтобы никогда не ошибиться   -  person Eregrith    schedule 03.10.2012
comment
@Эрегрит Это не работает. Он выделяет массив из 100 элементов; результирующий указатель указывает только на один элемент. Единственное реальное решение — использовать не malloc, а new. (Конечно, это только для C++.)   -  person James Kanze    schedule 03.10.2012
comment
использование new - не единственное реальное решение, особенно не в C, как вы сказали. Если он хочет несколько массивов по 2 байта, это сделано правильно, и memset будет работать. sizeof(*p) не разыменовывает p, он содержит только тип *p, поэтому int [2]   -  person Eregrith    schedule 03.10.2012
comment
@ user1660982: просто для справки в будущем, вы создали здесь огненную бурю, задав по сути 4 разных вопроса: правильно ли распределение и как мне сделать memset, скрещенный с C против C ++. И поскольку оба языка предоставляют способы выделения уже обнуленной памяти, есть еще 2 вопроса, на которые люди могут выбрать ответ, предложив calloc или new int[100][2]().   -  person Steve Jessop    schedule 03.10.2012


Ответы (5)


вы можете использовать calloc.

calloc заменит и malloc, и memset.

p = calloc(100, sizeof (*p));
person MOHAMED    schedule 03.10.2012

Я суммирую многие ответы (хотя я проигнорировал некоторые стилистические варианты в пользу собственных предпочтений).

In C:

Как использовать маллок:

int (*p)[2] = malloc(100 * sizeof(*p));

Как использовать мемсет:

memset(p, 0, 100 * sizeof(*p));

Как сделать оба в одном заявлении:

int (*p)[2] = calloc(100, sizeof(*p));

В C++ возможно то же самое, за исключением того, что вам нужно преобразовать результаты malloc и calloc: static_cast<int(*)[2]>(std::malloc(100 * sizeof(*p)).

Однако C++ предоставляет альтернативные способы выделения этого:

int (*p)[2] = new int[100][2](); // like calloc.
delete[] p; // *not* delete p

C++ также предоставляет vector, что обычно хорошо, но, к сожалению, вы не можете создать вектор массивов в стиле C. В С++ 03 вы можете обойти это следующим образом:

struct my_array {
    int data[2];
};

std::vector<my_array> p(100);
// no need to free/delete anything

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

my_array initvalue = {0};
std::vector<my_array> p(100, initvalue);

другой способ представить 2 целых числа:

std::vector<std::pair<int,int> > p(100);

Если вы можете использовать Boost:

std::vector<boost::array<int, 2> > p(100);

In C++11:

std::vector<std::array<int, 2>> p(100);

Я перечислил их в порядке возрастания того, насколько они обычно хороши, поэтому используйте последний вариант, который не блокируется никакими ограничениями, с которыми вы работаете. Например, если вы ожидаете взять указатель на первый элемент одного из внутренних массивов 2-int и увеличить его, чтобы получить указатель на второй, тогда std::pair отсутствует, потому что это не гарантирует, что это работает. .

person Steve Jessop    schedule 03.10.2012
comment
Будет ли структура содержать мусор или ноль по умолчанию, конечно, будет зависеть от того, в какой области она была объявлена ​​(глобальная/статическая или автоматическая). В C++ было бы лучше добавить в структуру конструктор, чтобы не полагаться на явную инициализацию. - person Lundin; 03.10.2012
comment
@Lundin: я почти добавил конструктор, но (а) я не хотел без необходимости делать его не-POD, и (б) только потому, что я хочу, чтобы этот вектор был обнулен, не обязательно означает, что я хочу, чтобы каждый экземпляр класс когда-либо будет обнулен. Моя основная путаница заключается в том, что я не могу вспомнить, инициализирован ли временный my_array() по умолчанию или инициализирован нулем. Это ни глобально, ни автоматически! - person Steve Jessop; 03.10.2012
comment
Он запустит конструктор по умолчанию, который ничего не делает. Таким образом, данные будут мусором, если вы не добавите конструктор. Вы всегда можете добавить один, например my_array(int arr[2] = NULL), и сделать нулевую инициализацию условной. - person Lundin; 03.10.2012

Элегантный способ:

typedef int int_arr_2[2];

int_arr_2* p;

p = malloc(sizeof(int_arr_2)*100);
memset(p,0,sizeof(int_arr_2)*100);

Лучший способ:

typedef int int_arr_2[2];

int_arr_2* p;

p = calloc(100, sizeof(int_arr_2));

calloc, в отличие от malloc, гарантирует, что все байты равны нулю.

person Lundin    schedule 03.10.2012
comment
Я вроде +1 за calloc, -1 за то, что сказал, что sizeof(int_arr_2) элегантнее, чем sizeof(*p). Но это больше +1, чем -1, так что меня легко убедить ;-) - person Steve Jessop; 03.10.2012
comment
@SteveJessop Код элегантен по следующим причинам: программист не может случайно взять sizeof (сам указатель). В частности, вы обязательно ошибетесь в не слишком редком случае bool custom_allocate (int** array);, когда выделенные данные передаются в main через параметр. Чтобы сделать это правильно, вам нужно написать malloc(sizeof(**array)). Кроме того, синтаксис typedef также проще и понятнее для среднего программиста на C, который не привык к указателям на массивы. - person Lundin; 03.10.2012
comment
Я не думаю, что обязательно ошибусь. Я бы написал *array = malloc(number * sizeof(**array)). Или some_function()->member = malloc(number * sizeof(*(some_function()->member))). Это не сложное правило. Но я имел в виду, что меня можно убедить, что это хороший ответ, а не то, что меня можно убедить предпочесть ваш способ написания размера. Я не могу в этом убедиться, потому что вы ошибаетесь :-) Я не возражаю против typedef для указателя на массив, но это еще один плюс. - person Steve Jessop; 03.10.2012
comment
@SteveJessop Но тогда, судя по качеству ваших сообщений, связанных с C, на этом сайте, я не верю, что вы подходите под категорию среднего программиста C :) Написать код, который может понять и поддерживать даже начинающий программист, это гораздо сложнее, чем написание кода, который может понять и поддерживать гуру C с парой десятков лет опыта. - person Lundin; 03.10.2012
comment
Лесть доставит вас куда угодно. Я полностью согласен с тем, что typedef помогает новичкам: синтаксис указателя на массив иногда сбивает с толку. Однако я действительно не согласен с тем, что повторение типа помогает новичкам. В вашей версии есть только один правильный тип, и программист должен выяснить, что это такое, возможно, время от времени обновлять его. В моей версии они указывают тип, на который указывает LHS, который всегда является правильным типом (иначе вы бы не присваивали ему значение), и они делают это, добавляя *. - person Steve Jessop; 03.10.2012
comment
@SteveJessop Хорошо, если быть злым: предположим, у нас есть что-то обыденное, например, указатель массива на массив указателей на функции, передаваемый по ссылке в функцию custom_alloc. Мы будем иметь дело с тривиальным объявлением вроде void(* (**fptr_arr)[] )(void);. Тогда я все равно напишу *fptr_arr = malloc(n * sizeof(fptr)); и пойму, что этот код делает намного лучше, чем sizeof(***fptr_array). - person Lundin; 03.10.2012
comment
Разница в терминах, в которых вы это понимаете. По-вашему, вы понимаете, что он выделяет массив fptr размера n. По-моему, вы понимаете, что он выделяет массив того, на что правильно указывает *fptr_arr, размером n. Если есть шанс, что *fptr_arr является неправильным присваиванием, но мы абсолютно уверены, какой тип мы должны выделить, тогда будет яснее сделать это по-своему (хотя, к сожалению, в C код будет скомпилирован, несмотря на то, что он неправильный). Это просто не мой опыт. - person Steve Jessop; 03.10.2012
comment
@SteveJessop Другой случай, который, возможно, более действителен, чем мое объявление-абонимация выше, - это когда вы выполняете универсальное программирование с указателями void. Предположим, что результат malloc передается пустому указателю. Затем вам придется узнать тип каким-либо другим способом, кроме использования оператора *. - person Lundin; 03.10.2012
comment
@Lundin: да, это сработает. Когда вы назначаете void*, у вас нет подсказок. Строго говоря, подобное может применяться в C++, когда вы выделяете пространство для производного класса и присваиваете указатель на базу, но я не думаю, что берусь утверждать, что вы никогда этого не сделаете. - person Steve Jessop; 03.10.2012

Строка memset() правильная.

Для C вам приведение malloc не требуется.
В С++, если вы все еще хотите это сделать, приведение типа должно быть таким:

p = (int(*)[2]) malloc(sizeof(*p)*100);  // or `static_cast<...>
  // ^^^^^^^^^

Но я бы предложил изменить ваш подход к использованию std::vector вместо этого. Чище, лучше и «полуавтомат»:

std::vector<int*> vi(100);  // or std::vector vi(100, nullptr);

Другой способ работы с необработанными указателями — использовать new[]:

int **p = new[100]();  // allocates and sets to 0

Но вы должны управлять этой памятью позже, освобождая ее с помощью delete[]

person iammilind    schedule 03.10.2012
comment
В C++ приведение является обязательным и хорошим стилем. В C приведение типов опасно и может привести к серьезным ошибкам. Это разные языки. Вот почему вы должны программировать только на одном языке программирования одновременно :) - person Lundin; 03.10.2012
comment
@Lundin: я думаю, что точка зрения iammilind заключалась в том, что в C++ malloc не является обязательным. Следовательно, вы можете иметь это, но его предложение состоит в том, что вы этого не сделаете :-) - person Steve Jessop; 03.10.2012
comment
В C++ вы должны использовать new, поэтому приведение не требуется. И просто добавление () в конце выражения new эффективно установит все выделенные значения в 0. - person James Kanze; 03.10.2012
comment
@JamesKanze, в идеале я использую new/new[] только в том случае, если есть особые требования к программированию (например, копирование является дорогостоящим или многопоточным). Всегда рекомендуется использовать std::vector, который делает это внутри и заботится об удалении памяти. - person iammilind; 03.10.2012
comment
@iammilind Хороший вопрос. std::vector<int> здесь было бы еще лучше. - person James Kanze; 03.10.2012

В C (не C++) вы бы просто сделали

int (*p)[2] = malloc(sizeof(*p)*100);

memset(*p,0,sizeof(*p)*100);

то есть переменные могут быть инициализированы выражениями, а malloc не нуждается (и не должен иметь) приведения. Тогда *p является "lvalue" вашего типа массива, который распадается на указатель при передаче в memset.

person Jens Gustedt    schedule 03.10.2012