Подтвердить что ты не робот

Как я могу выделить память и вернуть ее (через указательный параметр) в вызывающую функцию?

У меня есть код в нескольких разных функциях, который выглядит примерно так:

void someFunction (int *data) {
  data = (int *) malloc (sizeof (data));
}

void useData (int *data) {
  printf ("%p", data);
}

int main () {
  int *data = NULL;

  someFunction (data);

  useData (data);

  return 0;
}

someFunction () и useData () определяются в отдельных модулях (файлы *.c).

Проблема заключается в том, что, хотя malloc работает нормально, а выделенная память может использоваться в someFunction, одна и та же память недоступна после возвращения функции.

Пример запуска программы можно увидеть здесь, с выходом, показывающим различные адреса памяти.

Может кто-нибудь объяснить мне, что я делаю неправильно здесь, и как я могу заставить этот код работать?


EDIT: Мне кажется, что мне нужно использовать двойные указатели, чтобы сделать это - как я буду делать то же самое, когда мне действительно нужно использовать двойные указатели? Так, например, данные

int **data = NULL; //used for 2D array

Должен ли я использовать тройные указатели в вызовах функций?

4b9b3361

Ответ 1

Вы хотите использовать указатель на указатель:

void someFunction (int **data) {
  *data = malloc (sizeof (int));
}

void useData (int *data) {
  printf ("%p", data);
}

int main () {
  int *data = NULL;

  someFunction (&data);

  useData (data);

  return 0;
}

Почему? Ну, вы хотите изменить указатель data в основной функции. В C, если вы хотите изменить что-то, переданное в качестве параметра (и чтобы это изменение отображалось в версии для вызывающего абонента), вам нужно передать указатель на все, что вы хотите изменить. В этом случае это "то, что вы хотите изменить" - это указатель, поэтому для изменения этого указателя вы должны использовать указатель на указатель...

Обратите внимание, что помимо вашей основной проблемы в коде была еще одна ошибка: sizeof(data) дает вам количество байтов, необходимых для хранения указателя (4 байта в 32-битной ОС или 8 байтов на 64- разрядной ОС), тогда как вы действительно хотите, чтобы количество байтов должно было хранить то, на что указывает указатель (a int, т.е. 4 байта на большинстве ОС). Поскольку обычно sizeof(int *)>=sizeof(int), это, вероятно, не вызвало бы проблему, но это то, о чем нужно знать. Я исправил это в коде выше.

Вот некоторые полезные вопросы по указателям на указатели:

Как указатель на указатели работает на C?

Используется для нескольких уровней разметки указателя?

Ответ 2

Общая ошибка, особенно если вы переместили форму Java на C/С++

Помните, когда вы передаете указатель, он передает значение i.e копирует значение указателя. Это полезно для внесения изменений в данные, указываемые указателем, но любые изменения самого указателя являются только локальными, поскольку это копия!

Трюк заключается в том, чтобы использовать передачу указателя по ссылке, так как вы хотите изменить его i.e malloc it и т.д.

** указатель → напугает программиста noobie C;)

Ответ 3

Вам нужно передать указатель на указатель, если вы хотите изменить указатель.

т.

void someFunction (int **data) {
  *data = malloc (sizeof (int)*ARRAY_SIZE);
}

изменить: Добавлено ARRAY_SIZE, в какой-то момент вам нужно знать, сколько целых чисел вы хотите выделить.

Ответ 4

Это потому, что данные указателя передаются значением someFunction.

int *data = NULL;
//data is passed by value here.
someFunction (data); 
//the memory allocated inside someFunction  is not available.

Указатель на указатель или возврат выделенного указателя разрешит проблему.

void someFunction (int **data) {
  *data = (int *) malloc (sizeof (data));
}


int*  someFunction (int *data) {
  data = (int *) malloc (sizeof (data));
return data;
}

Ответ 5

someFunction() принимает свой параметр как int *. Поэтому, когда вы вызываете его из main(), создается копия полученного вами значения. Независимо от того, что вы изменяете внутри функции, эта копия и, следовательно, изменения не будут отображаться снаружи. Как и другие, вы можете использовать int ** для получения изменений, отраженных в данных. Другой способ сделать это - вернуть int * из someFunction().

Ответ 6

Помимо использования метода двойного указателя, если требуется только один возвращаемый параметр, необходимо переписать следующее:

 int *someFunction () {
   return (int *) malloc (sizeof (int *));
 }

и используйте его:

 int *data = someFunction ();

Ответ 7

Вот общий шаблон для выделения памяти в функции и возврата указателя через параметр:

void myAllocator (T **p, size_t count)
{
  *p = malloc(sizeof **p * count);
}
...
void foo(void)
{
  T *p = NULL;
  myAllocator(&p, 100);
  ...
}

Другой метод - сделать указатель функцией возвращаемого значения (мой предпочтительный метод):

T *myAllocator (size_t count)
{
  T *p = malloc(sizeof *p * count);
  return p;
}
...
void foo(void)
{
  T *p = myAllocator(100);
  ...
}

Некоторые примечания по управлению памятью:

  • Лучший способ избежать проблем с управлением памятью - избежать управления памятью; не гадайте с динамической памятью, если вам это действительно не нужно.
  • Не выдавайте результат malloc(), если вы не используете реализацию, предшествующую стандарту ANSI 1989 года, или вы собираетесь скомпилировать код как С++. Если вы забудете включить stdlib.h или иначе не имеете прототипа для malloc() в области видимости, то приведение возвращаемого значения приведет к сбою полезной диагностики компилятора.
  • Используйте размер выделяемого объекта вместо размера типа данных (т.е. sizeof *p вместо sizeof (T)); это спасет вас от изжоги, если тип данных должен измениться (скажем, от int до long или float, чтобы удвоить). Это также заставляет код читать немного лучше ИМО.
  • Изолировать функции управления памятью за более высоким уровнем выделения и освобождать функции; они могут обрабатывать не только распределение, но и инициализацию и ошибки.

Ответ 8

Здесь вы пытаетесь изменить указатель, т.е. от "data == Null" до "data == 0xabcd", какую-то другую выделенную вами память. Таким образом, чтобы изменить данные, которые вам нужны, передайте адрес данных, а именно: данные.

void someFunction (int **data) {
  *data = (int *) malloc (sizeof (int));
}

Ответ 9

Ответ на ваш дополнительный вопрос, который вы отредактировали:

'*' обозначает указатель на что-то. Таким образом, "**" будет указателем на указатель на что-то, "***" указатель на указатель на указатель на что-то и т.д.

Обычная интерпретация "int ** data" (если данные не являются параметром функции) будет указателем на список массивов int (например, "int a [100] [100]" ).

Итак, вам нужно сначала выделить ваши массивы int (для простоты я использую прямой вызов для malloc()):

data = (int**) malloc(arrayCount); //allocate a list of int pointers
for (int i = 0; i < arrayCount; i++) //assign a list of ints to each int pointer
   data [i] = (int*) malloc(arrayElemCount);

Ответ 10

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

Возврат void *, поэтому его можно использовать для любого типа размещения.

void *someFunction (size_t size) {
    return  malloc (size);
}

и используйте его как:

int *data = someFunction (sizeof(int));