выделение памяти (язык C)

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Виктория
Писатель
Сообщения: 113
Зарегистрирован: 28 дек 2012, 14:05
Откуда: Самара
Контактная информация:

выделение памяти (язык C)

Непрочитанное сообщение Виктория » 28 ноя 2013, 16:44

Olej писал(а): Массивы VLA в стандарте C99 можно посмотреть здесь: Расширение массивов.
Там их пример-объяснение:

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

void f(int diml, int dim2)
{
  int matrix[diml][dim2]; /* двумерный массив переменной длины */
  /* ... */
}
Главная особенность VLA C99 в том, что они могут быть только локально определёнными.
Память для массивов VLA может выделяется и с использованием стека (с пересчетом границ стека на этапе выполнения)? Мое занудство объясняется особенностями моих приложений (обработка сигналов в реальном времени на микроконтроллере). Со стеком интуитивно меньше проблем, чем использование malloc(). Код так сразу не могу сгенерить...

P.S.: вот ещё хорошая памятка
Status of C99 features in GCC

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: выделение памяти (язык C)

Непрочитанное сообщение Olej » 28 ноя 2013, 17:47

Виктория писал(а):
Olej писал(а): Массивы VLA в стандарте C99 можно посмотреть здесь: Расширение массивов.
Там их пример-объяснение:

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

void f(int diml, int dim2)
{
  int matrix[diml][dim2]; /* двумерный массив переменной длины */
  /* ... */
}
Главная особенность VLA C99 в том, что они могут быть только локально определёнными.
Память для массивов VLA может выделяется и с использованием стека (с пересчетом границ стека на этапе выполнения)? Мое занудство объясняется особенностями моих приложений (обработка сигналов в реальном времени на микроконтроллере). Со стеком интуитивно меньше проблем, чем использование malloc(). Код так сразу не могу сгенерить...
Это не "занудство", а, как раз, выяснение очень интересных и важных подробностей. ;-)

Я, может, не до конца понял ваш вопрос ... (или это не вопрос а утверждение?).

1. объявление локального массива int matrix[diml][dim2] - это же и есть выделение в стеке? ... без дёргания heap или MMU ...

2. кроме malloc() у нас есть ещё alloca(), который выделяет по запросу память в стеке.
Вот, собственно, из man alloca :
Because the space allocated by alloca() is allocated within the stack frame, that space is automatically freed if the function return is jumped over by a call to longjmp(3) or sig‐longjmp(3).

Do not attempt to free(3) space allocated by alloca()!
Хотя там же:
The alloca() function is machine- and compiler-dependent.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Olej » 28 ноя 2013, 18:08

Olej писал(а): 2. кроме malloc() у нас есть ещё alloca(), который выделяет по запросу память в стеке.
Вот как-то так:

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

typedef struct vararr {
   int n, data[ 0 ];
} vararr_t;

void varfunc( vararr_t *a ) {
   int ni = a->n, j;
   printf( "%p->[1:%d] элементы массива => ", a, ni );
   int *va = (int*)a;
   for( j = 1; j <= ni; j++ )
      printf( "%d%s", va[ j ], j != ni ? ", " : "\n" );
}

void test16( void ) {
   int var[] = { 3, 5, 7, 10 }, i;
   printf( "массивы переменной длины:\n" );
   printf( "стек вызывающей функции - %p\n", var );
   for( i = 0; i < sizeof( var ) / sizeof( *var ); i++ ) {
      int len = var[ i ], j;
      vararr_t *arr = (vararr_t*)alloca( ( len + 1 ) * sizeof( int ) );
      arr->n = len;
      for( j = 0; j < len; j++ ) arr->data[ j ] = j + 1;
      varfunc( arr );
   }
}

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

olej@notebook:~/2013_WORK/AntonG/examples.draft$ ./struct 5
05 ---------------------------------------
массивы переменной длины:
стек вызывающей функции - 0xbf9ecc10
0xbf9ecbe0->[1:3] элементы массива => 1, 2, 3
0xbf9ecbb0->[1:5] элементы массива => 1, 2, 3, 4, 5
0xbf9ecb80->[1:7] элементы массива => 1, 2, 3, 4, 5, 6, 7
0xbf9ecb40->[1:10] элементы массива => 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
------------------------------------------

Аватара пользователя
Виктория
Писатель
Сообщения: 113
Зарегистрирован: 28 дек 2012, 14:05
Откуда: Самара
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Виктория » 03 дек 2013, 18:23

Olej писал(а): 2. кроме malloc() у нас есть ещё alloca(), который выделяет по запросу память в стеке.
Спасибо, буду использовать alloca().
Насколько необходимо учитывать, что функция машинно-зависимая? Необходимо знать оценку глубины стека в своем проекте?

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Olej » 03 дек 2013, 18:35

Виктория писал(а):
Olej писал(а): 2. кроме malloc() у нас есть ещё alloca(), который выделяет по запросу память в стеке.
Спасибо, буду использовать alloca().
Насколько необходимо учитывать, что функция машинно-зависимая?
Она, скорее, как я понимаю, не машинно-зависимая, а компиляторо-зависимая...
Если у вас GCC - то оно в библиотеке libc.so есть.

P.S. Примечания ;-) :
- где-то в публикациях обсуждалось о потенциальной опасности alloca() ... сейчас не вспомню с чем это связывали, надо бы посмотреть;
- alloca() удобен ещё тем, что, в отличие от malloc(), calloc(), вам не нужно для выделения делать free() - если забудете ;-) , то не создадите утечки памяти.
Виктория писал(а):Необходимо знать оценку глубины стека в своем проекте?
Как-то (грубо, прикидочно...) оценивать глубину стека всё равно всегда надо...
Но:
- это же не рекурсия, когда стек многократно углубляется...
- вы же не чудовищно огромные структуры туда запихивать собираетесь? ... это в точности то же, что и просто локальные переменные объявлены
- размер стека процесса (потока, точнее говоря) устанавливается при сборке по умолчанию ... но, в принципе, его можно и изменить? ;-)

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Olej » 03 дек 2013, 20:00

Olej писал(а): - alloca() удобен ещё тем, что, в отличие от malloc(), calloc(), вам не нужно для выделения делать free() - если забудете ;-) , то не создадите утечки памяти.
alloca() ещё позволяет проследить чем отличается блок ({}) внутри кода, и блок как тело функции.
Смотрите:

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

#define N 10
void test052( void ) {             // размещение локальных данных
   void *pX;
   int X = 12345;
   pX = &X;
   printf( "размещение локальных данных:\n" );
   {  int X[ N ];
      printf( "1-й массив в блоке: %p...%p[%2d]\n",
              X, &X[ N - 1 ], (void*)&X[ N - 1 ] - pX );
   }
   {  int Y[ N ];
      printf( "2-й массив в блоке: %p...%p[%2d]\n",
              Y, &Y[ N - 1 ], (void*)&Y[ N - 1 ] - pX );
   }
   printf( "локальная переменная функции: %p->%d\n", &X, X );
}

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

olej@notebook:~/2013_WORK/AntonG/examples.draft$ ./function 5
05 ---------------------------------------
размещение локальных данных:
1-й массив в блоке: 0xbfe30280...0xbfe302a4[-4]
2-й массив в блоке: 0xbfe30280...0xbfe302a4[-4]
локальная переменная функции: 0xbfe302a8->12345
------------------------------------------
- размещение локальное в блоке int X[ N ] размещается впритык (-4) к дну стека ... а имя закрывает видимость X функции...
- размещение в следующем блоке (int Y[ N ]) размещается в точности на том же месте (предыдущее размещение X[] уже считается освобождённым)

А вот alloca() в блоке:

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

#undef N
#define N 6
void test053( void ) {             // alloca() данных в блоке
   void *p, *X;
   int i, Y = 12345;
   p = &Y;
   printf( "alloca() в блоке:\n" );
   for( i = 0; i < N; i ++ ) {
      *(short*)( X = alloca( sizeof( short ) ) ) = i + 1;
      printf( "%p[%2d]->%d%s",
              X, p - (void*)X, *(short*)X,
              ( N / 2 - 1 ) == i % ( N / 2 ) ? "\n" : " ,\t" );
   }
   printf( "локальная переменная функции: %p->%d\n", &Y, Y );
}
Тут много интересного...

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

olej@notebook:~/2013_WORK/AntonG/examples.draft$ ./function 6
06 ---------------------------------------
alloca() в блоке:
0xbfe30270[48]->1 ,	0xbfe30250[80]->2 ,	0xbfe30230[112]->3
0xbfe30210[144]->4 ,	0xbfe301f0[176]->5 ,	0xbfe301d0[208]->6
локальная переменная функции: 0xbfe302a0->12345
------------------------------------------
- последовательные размещения alloca() не освобождаются при выходе из блока (for ... )
- новое размещение делается вглубь стека (новый адрес)
- и освободится всё это, как я понимаю, при уничтожении кадра стека - при завершении функции!
- ну и ещё одна непонятная вещь ... при последовательных запросах alloca( 2 ) - выделяется по 32 байта и 48 байт при 1-м запросе ... но этого я не понимаю ;-)

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Olej » 03 дек 2013, 20:09

Olej писал(а): - размещение локальное в блоке int X[ N ] размещается впритык (-4) к дну стека ... а имя закрывает видимость X функции...
- размещение в следующем блоке (int Y[ N ]) размещается в точности на том же месте (предыдущее размещение X[] уже считается освобождённым)
Вот так даже смешнее будет ;-) :

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

void test052( void ) {             // размещение локальных данных
   void *pX;
   int i, X = 12345;
   pX = &X;
   printf( "размещение локальных данных:\n" );
   {  int X[ N ];
      for( i = 0; i < N; i++ ) X[ i ] = i + 1;
      printf( "1-й массив в блоке: %p...%p[%2d]:",
              X, &X[ N - 1 ], (void*)&X[ N - 1 ] - pX );
      for( i = 0; i < N; i++ ) 
         printf( "%2d%s", X[ i ], i != N - 1 ? ", " : "\n" ); 
   }
   {  int Y[ N ];
      printf( "2-й массив в блоке: %p...%p[%2d]:",
              Y, &Y[ N - 1 ], (void*)&Y[ N - 1 ] - pX );
      for( i = 0; i < N; i++ )
         printf( "%2d%s", Y[ i ], i != N - 1 ? ", " : "\n" );
   }
   printf( "локальная переменная функции: %p->%d\n", &X, X );
}

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

olej@notebook:~/2013_WORK/AntonG/examples.draft$ ./function 5
05 ---------------------------------------
размещение локальных данных:
1-й массив в блоке: 0xbfad627c...0xbfad62a0[-4]: 1,  2,  3,  4,  5,  6,  7,  8,  9, 10
2-й массив в блоке: 0xbfad627c...0xbfad62a0[-4]: 1,  2,  3,  4,  5,  6,  7,  8,  9, 10
локальная переменная функции: 0xbfad62a4->12345
------------------------------------------
- размещённый в блока массив X присваивается: 1, 2, 3, ...
- а затем размещённый на его же месте (но не инициализированный) массив Y "инициализируется" мусором, оставшимся от X ;-)

Аватара пользователя
Виктория
Писатель
Сообщения: 113
Зарегистрирован: 28 дек 2012, 14:05
Откуда: Самара
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Виктория » 03 дек 2013, 20:24

Olej писал(а):- ну и ещё одна непонятная вещь ... при последовательных запросах alloca( 2 ) - выделяется по 32 байта и 48 байт при 1-м запросе ... но этого я не понимаю ;-)
Смещение на резервирование указателя p?

Аватара пользователя
Виктория
Писатель
Сообщения: 113
Зарегистрирован: 28 дек 2012, 14:05
Откуда: Самара
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Виктория » 03 дек 2013, 20:39

- где-то в публикациях обсуждалось о потенциальной опасности alloca() ... сейчас не вспомню с чем это связывали, надо бы посмотреть;
Возможные опасности подстерегают при совместном использовании стека alloca() и динамических массивов (http://www.opennet.ru/docs/RUS/glibc/glibc-3.html ):
Обратите внимание: если Вы смешиваете использование alloca и динамических массивов внутри одной функции, выход из области, в который динамический массив был объявлен, освобождает все блоки, размещенные alloca во время выполнения этой области.
alloca() и аргументов функции
Не используйте alloca внутри аргументов обращения к функции, Вы получите непредсказуемые результаты, потому что стек-пространство для alloca появится на стеке в середине пространства для аргументов функции. Пример того, что нужно избегать - foo (x, alloca (4), y).

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

выделение памяти (язык C)

Непрочитанное сообщение Olej » 03 дек 2013, 23:49

Виктория писал(а):
Olej писал(а):- ну и ещё одна непонятная вещь ... при последовательных запросах alloca( 2 ) - выделяется по 32 байта и 48 байт при 1-м запросе ... но этого я не понимаю ;-)
Смещение на резервирование указателя p?
Не могу сказать, это, возможно, реализационные вещи (GCC), и alloca(), не исключено, резервирует блок за раз не менее 32 байт?
Если разберусь - напишу.

Вот такой вариант "чище", показательнее:

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

#undef N
#define N 6

void test053( void ) {             // alloca() данных в блоке
   void *X[ N ];
   int i;
   void *p = &p;                   // дно кадра стека
   printf( "alloca() в блоке ():\n" );
   printf( "дно стека = %p\n", p );
   for( i = 0; i < N; i ++ ) {
      X[ i ] = alloca( sizeof( short ) );
      *(short*)X[ i ] = i + 1;
   }
   for( i = 0; i < N; i ++ )
      printf( "%p[%2d]->%d%s",
              X[ i ], p - (void*)X[ i ], *(short*)X[ i ],
              ( N / 2 - 1 ) == i % ( N / 2 ) ? "\n" : " ,\t" );
}
- размещаем участки памяти внутри блока for ...
- используем их за пределами этого блока (2-й for)

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

olej@notebook:~/2013_WORK/AntonG/examples.draft$ ./function 6
06 ---------------------------------------
alloca() в блоке ():
дно стека = 0xbfd4d4c4
0xbfd4d4a0[36]->1 ,0xbfd4d480[68]->2 ,0xbfd4d460[100]->3
0xbfd4d440[132]->4 ,0xbfd4d420[164]->5 ,0xbfd4d400[196]->6
------------------------------------------

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя