LINUX.ORG.RU

[СИ] Коварный malloc.

 


0

1

[СИ] Коварный malloc.

Язык СИ
ОС UNIX

По совету анонимуса и других людей осваиваю malloc.

Ниже приведена небольшая тестовая программа. Смысл
ее в том, что в функции init_test(); выделяется
память malloc-ом, там же она первично инициируется
пробным текстом, указатель на эту память при
возврате из функции передается через аргумент главной
программе (main), и далее эта память используется
в главной программе.

Как я понимаю, память выделяется в статической памяти,
а не в стеке. И, стало быть, должна быть доступна и
при возврате в main. Выдача программы, как будто,
подтверждает это:

k=init_test()=0
proba malloc(); proba malloc(); proba malloc();
proba malloc() main()

Но сомнения все-же остались. В этом и вопрос:
можно ли выделять память в функции, а потом
использовать ее вне функции?

Кто знает прошу ответить.



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

//   gcc malloc_test_3.c -o malloc_test_3.cgi
//   ./malloc_test_3.cgi


//------------------ main ------------------------

int main()

{
	int  k;
	char *buf;

	k=init_test(&buf);
	printf("k=init_test()=%d\n",k);
	if(k < 0) exit(0);
	printf("%s\n", buf);
	strcpy(buf, "proba malloc() main()");
	printf("%s\n", buf);
	sbros_test(&buf);
	exit(0);
}

//--------------------- init_test ---------------------

int init_test(char **buf)

{
	*buf = (char*)malloc(100);
	if(*buf == NULL) return(-1);
	strcpy(*buf, "proba malloc(); proba malloc(); proba malloc();");
	return(0);
}

//--------------------- sbros_test -----------------

int sbros_test(char **buf)

{
	if(*buf != NULL){
		free(*buf);
		*buf = NULL;
	}
	return(0);
}

oleg_2
() автор топика

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

strdup именно этим и занимается. Так что проблем нет.

Eddy_Em ☆☆☆☆☆
()

> Как я понимаю, память выделяется в статической памяти, а не в стеке.

В динамической памяти или (как её называют) в куче.

bvvv
()
Ответ на: комментарий от oleg_2

зачем такой мудрённый init? нет, можно конечно и так, но можно и проще:

char *
init_test(size_t size)
{
        char *buf = malloc(size);

        if (buf)
                strncpy(buf, "proba malloc(); proba malloc(); proba malloc();", size);

        return buf;
}

и да, никогда не используй strcat/strcpy — они протухли и плохие, используй strncat/strncpy или strlcat/strlcpy.

beastie ★★★★★
()
Ответ на: комментарий от oleg_2
 
int init_test(char **buf)
{ 
  *buf = (char*)malloc(100); 
  if(*buf == NULL) return(-1); 
  strcpy(*buf, "proba malloc(); proba malloc(); proba malloc();");
  return(0); 
} 

а зачем так? поскольку маллоком аллоцируешь, то эта память выделяется в куче (динамической памяти) и просто так не освобождается (собсно это и есть утечка, если ее не освободить). посему просто можешь сделать просто:

 
char * init_test() 
{ 
  char *buff = malloc(100); 
  // здесь наполняешь всем, чем нужно 
  return buff; 
} 

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

Deleted
()
Ответ на: комментарий от beastie

Да, инит мудреный. Но программа-то тестовая.
И писался тест под рабочюю программу. Там указатель
упакован в структуру вместе с другими переменными.
По той же причине (тест) применены опасные функции.

Спасибо.

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

anonymous. Да. Я понял. Подобно тому, как нельзя
забывать закрывать файлы и сокеты во всех ветвях.

oleg_2
() автор топика

> память выделяется в статической памяти

маллоком - в динамической, ака куче

yoghurt ★★★★★
()

так на всякий, man brk. поймешь где и как именно выделяется память. ради интереса можешь написать собственный аллокатор с дефрагментацией.

exception13 ★★★★★
()
Ответ на: комментарий от catap

Наоборот. Будет ставить NULL - получит сегфолт. Не будет - может и память потереть, и структуру хипа попортить. Может и сегфолт получить, если повезёт.

Legioner ★★★★★
()
Ответ на: комментарий от exception13

ты еще забыл про анонимный mmap ;) Хотя на собеседование мне кто-то говорил про третий способ память получить.

catap ★★★★★
()
Ответ на: комментарий от Eddy_Em

О. Спасибо. Я думал это обвязка вокруг open/mmap, а оказалось эротиченее!

catap ★★★★★
()
Ответ на: комментарий от exception13

похоже shmget. Т.е. судя по glibc это дырка в ядро. Сейчас пойду удостоверюсь что с той тороны ядра торчит.

catap ★★★★★
()
Ответ на: комментарий от catap

имхо это все ж более для IPC нежели просто для аллокации. да и mmap хоть и безопаснее но я бы не стал его использовать только для аллокации.

exception13 ★★★★★
()
Ответ на: комментарий от catap

Почему это дырка? shmget тоже память только в user-space выделяет. Еще есть «эротичный» вариант: делать mmap на виртуальный файл в shm (у Стивенса описывалось) :)

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от catap

да, так можно аллоцировать память. И даже не shm. Как страшно жить.

catap ★★★★★
()
Ответ на: комментарий от Eddy_Em

shmget дергает ядерный sys_shmget, который просто обвязка наз ipcget :) => можно выставить key в IPC_PRIVATE и получить аллокатор.

catap ★★★★★
()
Ответ на: комментарий от Eddy_Em

Я расширенную память во многих вещах использовал. Правда, остается некоторая вероятность коллизий (т.к. не факт, что полученный, к примеру, ftok'ом идентификатор не используется уже кем-то еще).

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

>>А разве действие mmap с MAP_ANONYMOUS не идентично shmget с IPC_PRIVATE и тому же malloc?

я так думаю смотря с какой стороны посмотреть.

exception13 ★★★★★
()
Ответ на: комментарий от Eddy_Em

malloc это высокоуровневая обвязка над анонимным mmap и sbrk с какой-то логикой.

Действие mmap(MAP_ANONYMOUS) и shmget(IPC_PRIVATE) похожи, но на первый взгляд это разные дырки.

Еще я вспомнил про alloca и пока не могу понять где и как она реализуется.

Т.е. сейчас у меня отсатеся впросы:

  1. что такое alloca
  2. в чем разница в поведение между mmap(MAP_ANONYMOUS) и shmget(IPC_PRIVATE)
  3. в чем разница в поведение между mmap(MAP_ANONYMOUS|MAP_SHARED) и shmget(IPC_PRIVATE)
catap ★★★★★
()
Ответ на: комментарий от catap

The alloca() function allocates size bytes of space in the stack frame of the caller. This temporary space is automatically freed when the function that called alloca() returns to its caller.

интересно на реализацию посмотреть. там какие то хуки используются или как?

exception13 ★★★★★
()
Ответ на: комментарий от catap

в чем разница в поведение между mmap(MAP_ANONYMOUS|MAP_SHARED) и shmget(IPC_PRIVATE)

По-моему, в этом случае уже явно не IPC_PRIVATE (с этим ключом shmget выделяет сегмент только для «личного» пользования).

alloca вообще выделяет кусок стека со всеми вытекающими.

Советую, кстати, Стивенса почитать. У него хорошо объясняется что к чему во взаимодействии процессов и видах выделения памяти. (правда, я и сам в тонкости не вдавался - вычитал, что мне надо, и использую).

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от exception13

Если я правильно понял мануал, это встроено в gcc (через макросы), т.е. char *p = alloca(10) эквивалентно char tmp[10]; char *p = tmp;

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от exception13

> там какие то хуки используются или как?

Грубо говоря,

sub esp, size

Все просто :)

sjinks ★★★
()
Ответ на: комментарий от Eddy_Em

На самом деле все понятно. Просто странно что столько одинаковых дырок.

Про alloca тоже все понятно.

Даже как-то грустно.

catap ★★★★★
()
Ответ на: комментарий от anonymous

Точнее: на каждый успешный malloc. И только один раз.

amomymous ★★★
()
Ответ на: комментарий от exception13

>ЕМНИП в OpenBSD именно mmap используется для аллокации.

AFAIR только в openBSD

Led ★★★☆☆
()

В этом и вопрос: можно ли выделять память в функции, а потом использовать ее вне функции?

Можно, но так лучше не делать. Практика говорит о том, что жалкие человечишки в этом вопросе делают море ошибок.

Если из функции А нужно вызвать функцию Б, которая должна вернуть какой-то буфер с информацией, то гораздо лучше этот буфер выделить в А и передать его в Б. Идеально, если буфер в А выделяется автоматически, в виде массива, а не malloc'ом.

Пример:

void b(char *buf, int buf_sz)
{
    snprintf(buf, buf_sz, "Answer to life, universe, everything: %d", 42);
}

void a()
{
    char buf[MAX_BUF];

    b(buf, sizeof(buf));
    ...
}
mv ★★★★★
()
Ответ на: комментарий от catap

Не, дырок в итоге три: shmget, mmap и brk

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

mv ★★★★★
()
Ответ на: комментарий от Corey

А это вы «UNIX. Взаимодействие процессов» имели в виду?

Да

Eddy_Em ☆☆☆☆☆
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.