динамическое выделение памяти под структуры

Модератор: Модераторы разделов

Ответить
v4567
Сообщения: 162
ОС: Devuan

динамическое выделение памяти под структуры

Сообщение v4567 »

Есть вот такая структура:

Код: Выделить всё

struct asd
{
 int data;
 struct asd *next;
};


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

Код: Выделить всё

struct asd *newst(struct asd *head)
{
 struct asd *newstructura;
 newstructura = (struct asd *)malloc(sizeof(struct asd));
 newstructura -> next = head;
 return newstructura;
}


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

Код: Выделить всё

int i, n = 20;
struct asd *head;
head = NULL;

for(i = 0; i < n; i++)
{
 head = newst(head);
}


Вопрос можно ли перемещаться по структурам меняя указатель head вот таким образом head-- или head++
Я думаю что нельзя, хотя написал небольшую тестовую программку и всё работает. Но работает я думаю из-за того, что данные структуры занимают очень мало места в памяти и при выделении памяти они все будут в одном месте, поэтому меняя указатель head - head-- или head++ мы будем перемещаться по структурам. Но если бы структуры были очень большими и их было очень много, то, есть вероятность, что одна часть их будет расположена в одном месте, а другая в другом месте памяти и тогда перемещаться по структурам меняя head не получиться.
Хотелось бы узнать правильно я думаю или ошибаюсь!
Правильно перемещаться по структурам надо наверное так:

Код: Выделить всё

int i, n = 20;
struct asd *head;
head = NULL;

for(i = 0; i < n; i++)
{
 head = newst(head);
}

struct asd *p;
p = head;
p = p -> next;


Исходя из этого перемещаться можно сверху вниз и если нужно подняться на одну структуру вверх, то надо переместиться в самый вверх, а потом опуститься до нужной структуры. Можно ли как то произвольно гулять по структурам?

Следующий непонятный момент вот какой. После выделения памяти под структуры и поработав с ними, освобождаю память free(head), head в данном случае указывает на самую верхнюю структуру.
В интернете вычитал вот что:
free
Освобождение памяти с помощью free

Теперь рассмотри, как происходит освобождение памяти. Переменная указатель хранит адрес области памяти, начиная с которого она может им пользоваться. Однако, она не хранит размера этой области. Откуда тогда функция free знает, сколько памяти необходимо освободить?

Очевидно, что информация о размере выделенного участка должна где-то храниться. Есть несколько решения этой проблемы.
1. Можно создать карту, в которой будет храниться размер выделенного участка. Каждый раз при освобождении памяти компьютер будет обращаться к этим данным и получать нужную информацию.
2. Второе решение более распространено. Информация о размере хранится на куче до самих данных. Таким образом, при выделении памяти резервируется места больше и туда записывается информация о выделенном участке. При освобождении памяти функция free "подсматривает", сколько памяти необходимо удалить.
Функция free освобождает память, но при этом не изменяет значение указателя, о чём нужно помнить.


Но у меня почему то при освобождении памяти выдаёт ошибку хотя до освобождения памяти программа работает нормально:

Код: Выделить всё

*** glibc detected *** ./yrok161: double free or corruption (out): 0x0812e020 ***
======= Backtrace: =========
/lib/libc.so.6[0xb35231]
./yrok161[0x8048654]
/lib/libc.so.6(__libc_start_main+0xe6)[0xadba66]
./yrok161[0x8048411]
======= Memory map: ========
003e7000-00411000 r-xp 00000000 08:02 2237624    /lib/libgcc_s-4.4.0-20090506.so.1
00411000-00412000 rw-p 00029000 08:02 2237624    /lib/libgcc_s-4.4.0-20090506.so.1
00a77000-00a78000 r-xp 00a77000 00:00 0          [vdso]
00a9d000-00abd000 r-xp 00000000 08:02 2236420    /lib/ld-2.10.1.so
00abd000-00abe000 r--p 0001f000 08:02 2236420    /lib/ld-2.10.1.so
00abe000-00abf000 rw-p 00020000 08:02 2236420    /lib/ld-2.10.1.so
00ac5000-00c30000 r-xp 00000000 08:02 2236959    /lib/libc-2.10.1.so
00c30000-00c31000 ---p 0016b000 08:02 2236959    /lib/libc-2.10.1.so
00c31000-00c33000 r--p 0016b000 08:02 2236959    /lib/libc-2.10.1.so
00c33000-00c34000 rw-p 0016d000 08:02 2236959    /lib/libc-2.10.1.so
00c34000-00c37000 rw-p 00c34000 00:00 0
08048000-08049000 r-xp 00000000 08:02 7955556
08049000-0804a000 rw-p 00000000 08:02 7955556
0812e000-0814f000 rw-p 0812e000 00:00 0          [heap]
b7efe000-b7f00000 rw-p b7efe000 00:00 0
b7f29000-b7f2b000 rw-p b7f29000 00:00 0
bfa15000-bfa2a000 rw-p bffeb000 00:00 0          [stack]
Аварийный останов


И последний вопрос надо ли после освобождения памяти обнулять указатель head: head = NULL?

Вот моя небольшая тестовая программка:

Код: Выделить всё

#include <stdio.h>
#include <stdlib.h>

struct asd
{
 int data;
 struct asd *next;
};

struct asd *newst(struct asd *);
void zdan(struct asd *, int);
int vivod(struct asd *);

void main(void)
{
 int i, n;
 struct asd *head;
 head = NULL;

 scanf("%d", &n);

 for(i = 0; i < n; i++)
 {
  head = newst(head);
 }

 for(i = 0; i < (n - 1); i++)
 {
  head--;
 }

 for(i = 0; i < n; i++)
 {
  zdan(head, i);
  if(i != (n - 1))
  {
   head++;
  }
 }

 for(i = 0; i < (n - 1); i++)
 {
  head--;
 }

 for(i = 0; i < n; i++)
 {
  printf("%d", vivod(head));
  if(i != (n - 1))
  {
   printf(" --> ");
   head++;
  }
 }
 printf("\n");

 for(i = 0; i < n; i++)
 {
  printf("%d", vivod(head));
  if(i != (n - 1))
  {
   printf(" --> ");
   head--;
  }
 }
 printf("\n");

 if(i != (n - 1))
 {
  head++;
 }
 free(head);
 head = NULL;
}

struct asd *newst(struct asd *head)
{
 struct asd *newstructura;
 newstructura = (struct asd *)malloc(sizeof(struct asd));
 newstructura -> next = head;
 return newstructura;
}

void zdan(struct asd *head, int x)
{
 head -> data = x;
}

int vivod(struct asd *head)
{
 return head -> data;
}


Компилирую командой:

Код: Выделить всё

gcc -o yrok161 yrok161.c


За помощь заранее благодарен!
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5282
ОС: Gentoo

Re: динамическое выделение памяти под структуры

Сообщение /dev/random »

v4567 писал(а):
13.10.2016 11:08
Вопрос можно ли перемещаться по структурам меняя указатель head вот таким образом head-- или head++

Ни в коем случае!


v4567 писал(а):
13.10.2016 11:08
Я думаю что нельзя, хотя написал небольшую тестовую программку и всё работает. Но работает я думаю из-за того, что данные структуры занимают очень мало места в памяти и при выделении памяти они все будут в одном месте, поэтому меняя указатель head - head-- или head++ мы будем перемещаться по структурам. Но если бы структуры были очень большими и их было очень много, то, есть вероятность, что одна часть их будет расположена в одном месте, а другая в другом месте памяти и тогда перемещаться по структурам меняя head не получиться.
Хотелось бы узнать правильно я думаю или ошибаюсь!

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

v4567 писал(а):
13.10.2016 11:08
Правильно перемещаться по структурам надо наверное так:

Код: Выделить всё

int i, n = 20;
struct asd *head;
head = NULL;

for(i = 0; i < n; i++)
{
 head = newst(head);
}

struct asd *p;
p = head;
p = p -> next;

Да.

v4567 писал(а):
13.10.2016 11:08
Исходя из этого перемещаться можно сверху вниз и если нужно подняться на одну структуру вверх, то надо переместиться в самый вверх, а потом опуститься до нужной структуры. Можно ли как то произвольно гулять по структурам?

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

v4567 писал(а):
13.10.2016 11:08
Следующий непонятный момент вот какой. После выделения памяти под структуры и поработав с ними, освобождаю память free(head), head в данном случае указывает на самую верхнюю структуру.

...

Но у меня почему то при освобождении памяти выдаёт ошибку хотя до освобождения памяти программа работает нормально:

Код: Выделить всё

*** glibc detected *** ./yrok161: double free or corruption (out): 0x0812e020 ***
======= Backtrace: =========
/lib/libc.so.6[0xb35231]
./yrok161[0x8048654]
/lib/libc.so.6(__libc_start_main+0xe6)[0xadba66]
./yrok161[0x8048411]
======= Memory map: ========
003e7000-00411000 r-xp 00000000 08:02 2237624    /lib/libgcc_s-4.4.0-20090506.so.1
00411000-00412000 rw-p 00029000 08:02 2237624    /lib/libgcc_s-4.4.0-20090506.so.1
00a77000-00a78000 r-xp 00a77000 00:00 0          [vdso]
00a9d000-00abd000 r-xp 00000000 08:02 2236420    /lib/ld-2.10.1.so
00abd000-00abe000 r--p 0001f000 08:02 2236420    /lib/ld-2.10.1.so
00abe000-00abf000 rw-p 00020000 08:02 2236420    /lib/ld-2.10.1.so
00ac5000-00c30000 r-xp 00000000 08:02 2236959    /lib/libc-2.10.1.so
00c30000-00c31000 ---p 0016b000 08:02 2236959    /lib/libc-2.10.1.so
00c31000-00c33000 r--p 0016b000 08:02 2236959    /lib/libc-2.10.1.so
00c33000-00c34000 rw-p 0016d000 08:02 2236959    /lib/libc-2.10.1.so
00c34000-00c37000 rw-p 00c34000 00:00 0
08048000-08049000 r-xp 00000000 08:02 7955556
08049000-0804a000 rw-p 00000000 08:02 7955556
0812e000-0814f000 rw-p 0812e000 00:00 0          [heap]
b7efe000-b7f00000 rw-p b7efe000 00:00 0
b7f29000-b7f2b000 rw-p b7f29000 00:00 0
bfa15000-bfa2a000 rw-p bffeb000 00:00 0          [stack]
Аварийный останов

Во-первых, вы, обращаясь абы к какой памяти, затёрли информацию о том, что и где выделено, и malloc/free больше не в состоянии нормально работать. Во-вторых, *каждому* вызову malloc() должен соответствовать вызов free(). Вы же выделяете память множество раз, а освобождаете только один.

v4567 писал(а):
13.10.2016 11:08
И последний вопрос надо ли после освобождения памяти обнулять указатель head: head = NULL?

Вам решать. Всё зависит от того, как вы используете эти указатели. Если вашей программе это нужно, то да. Приведённой вами тестовой это не нужно.

v4567 писал(а):
13.10.2016 11:08
Вот моя небольшая тестовая программка:

Код: Выделить всё

#include <stdio.h>
#include <stdlib.h>

struct asd
{
 int data;
 struct asd *next;
};

struct asd *newst(struct asd *);
void zdan(struct asd *, int);
int vivod(struct asd *);

void main(void)
{
 int i, n;
 struct asd *head;
 head = NULL;

 scanf("%d", &n);

 for(i = 0; i < n; i++)
 {
  head = newst(head);
 }

 for(i = 0; i < (n - 1); i++)
 {
  head--;
 }

 for(i = 0; i < n; i++)
 {
  zdan(head, i);
  if(i != (n - 1))
  {
   head++;
  }
 }

 for(i = 0; i < (n - 1); i++)
 {
  head--;
 }

 for(i = 0; i < n; i++)
 {
  printf("%d", vivod(head));
  if(i != (n - 1))
  {
   printf(" --> ");
   head++;
  }
 }
 printf("\n");

 for(i = 0; i < n; i++)
 {
  printf("%d", vivod(head));
  if(i != (n - 1))
  {
   printf(" --> ");
   head--;
  }
 }
 printf("\n");

 if(i != (n - 1))
 {
  head++;
 }
 free(head);
 head = NULL;
}

struct asd *newst(struct asd *head)
{
 struct asd *newstructura;
 newstructura = (struct asd *)malloc(sizeof(struct asd));
 newstructura -> next = head;
 return newstructura;
}

void zdan(struct asd *head, int x)
{
 head -> data = x;
}

int vivod(struct asd *head)
{
 return head -> data;
}
Спасибо сказали:
Ответить