Программирование PC Динамическое выделение памяти на C, C++ и ассемблере ARM Fri, December 13 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


Динамическое выделение памяти на C, C++ и ассемблере ARM Печать
Добавил(а) microsin   

В этой статье рассматриваются три эквивалентные техники выделения и освобождения динамической памяти в трех средах программирования: язык C, язык C++ и язык ассемблера ARM. Материалом для статьи послужил видеоролик [1] с замечательного YouTube-канала LaurieWired. Все примеры кода тестировались на Raspberry Pi [2] в операционной системе Raspberry Pi OS (Linux), процессор ARM.

[Выделение памяти на C+: new, delete]

Создайте файл allocate.cpp со следующими содержимым:

   #include < iostream>
   #include < new>
   int main()
   {
      int *num_ptr = nullptr;
   
      try
      {
         // Выделение блока памяти размером числа int:
         num_ptr = new int;
         // Использование выделенного блока, запись в него числа 4660 (0x1234):
         *num_ptr = 0x1234;
         // Вывод этого числа на печать:
         std::cout << "Dynamic number: " << *num_ptr << std::endl;
      }
      catch (const std::bad_alloc& e)
      {
         return 1;
      }
      
      // Освобождение выделенной памяти. Память будет
      // возвращена операционной системе:
      delete num_ptr;
      
      return 0;
   }

Скомпилируйте файл allocate.cpp командой:

$ g++ allocate.cpp -o allocate

После компиляции получится исполняемый файл allocate, запустите его:

$ ./allocate
Dynamic number: 4660

На языке C++ для выделения памяти также можно использовать стандартные библиотечные вызовы malloc, free, realloc, которые используются при программировании на языке C.

[Выделение памяти на C: malloc, free]

Создайте файл allocate.c со следующими содержимым:

   #include < stdio.h>
   #include < stdlib.h>
   int main ()
   {
      // Выделение блока памяти размером числа int:
      int *num_ptr = (int*) malloc(sizeof(int));
      
      // Проверка, была ли успешно выделена память:
      if (NULL == num_ptr)
      {
         // Выделить память не получилось.
         return 1;
      }
      
      // Использование выделенного блока, запись в него числа 4660 (0x1234):
      *num_ptr = 0x1234;
      // Вывод этого числа на печать:
      printf("Dynamic number: %d\n", *num_ptr);
      
      // Освобождение выделенной памяти:
      free(num_ptr);
      return 0;
   }

Скомпилируйте allocate.c:

$ gcc allocate.c -o allocate

Запуск allocate:

$ ./allocate
Dynamic number: 4660

[Выделение памяти на языке ассемблера ARMv7: mmap, munmap]

На языке ассемблера мы будем использовать системные вызовы mmap, munmap [3] стандартной библиотеки C. Вызов mmap() создает новое отображение анонимного файла или устройства в виртуальное адресное пространство текущего вызывающего процесса. Декларация функций mmap, munmap на языке C (определения из sys/mman.h):

   void *mmap(void addr[.length], size_t length, int prot, int flags,
              int fd, off_t offset);
   int munmap(void addr[.length], size_t length);

Начальный адрес для нового отображения задается параметром addr. Аргумент length задает размер выделяемой области в байтах (который должен быть больше 0).

Создайте файл allocate.s со следующим содержимым:

.section .text
    extern printf
    global main

// Подпрограмма, которая выделяет память:
allocate: push {lr} // Подготовка параметров для вызова системной функции mmap: mov r0, #0 /* addr: NULL, ядро OS само выберет адрес в памяти. */ mov r1, #4096 /* length: размер памяти для отображения (4096 байт, одна страница) */ mov r2, #3 /* prot: разрешения доступа к памяти, PROT_READ | PROT_WRITE */ mov r3, #0x22 /* flags: MAP_PRIVATE | MAP_ANONYMOUS */ mov r4, #-1 /* fd: -1, потому что мы не делаем отображение в файл */ mov r5, #0 /* offset: смещение 0 */ // Вызов системной функции mmap2. Номер системной функции 192 // для mmap2 (см. таблицу [4]) помещается в регистр r7: mov r7, #192 svc #0 // Проверка результата вызова mmap. В случае успеха r0 будет содержать адрес // выделенной памяти. Если же вызов был неудачным, то в регистре r0 будет // значение -1: cmp r0, #-1 beg alloc_failed // Использование выделенной памяти, запись в неё числа 4660 (0x1234): mov r1, #0x1234 str r1, [r0] /* Содержимое регистра r1 будет записано по адресу в регистре r0 */ b alloc_exit
alloc_failed: mov r0, #-1

alloc_exit:
pop {pc}

main: push {r4-r7, lr} // Выделение памяти для числа: bl allocate /* вызов подпрограммы allocate */ // Проверка: было ли выделение успешным? cmp r0, #-1 beq print_fail // Память была успешно выделена, и в неё было записано // число 4660. Выведем на печать это число, как // в предыдущих примерах. push {r0} ldr r1, [r0] /* Указатель на выделенную память в r0 */ ldr r0, =format_str bl printf // Освобождение памяти pop {r0} mov r1, #4096 // Системный вызов munmap: mov r7, #215 svc #0 b exit
print_fail: ldr r0, =format_str_fail bl printf

exit: ret

.section .data

format_str: db "Dynamic number: %d\n", 0
format_str_fail: db "Error call mmap\n", 0

Компиляция allocate.s:

$ arm-linux-gnueabi-as allocate.s -o allocate.o
$ arm-linux-gnueabi-gcc -static allocate.o -o allocate

Запуск allocate:

$ ./allocate
Dynamic number: 4660

[Ссылки]

1. Mastering Memory: Allocation Techniques in C, C++, and ARM Assembly site:youtube.com.
2. Raspberry Pi, быстрый старт.
3. mmap(2) Linux manual page site:man7.org.
4. Linux System Call Table site:googlesource.com.
5. Calling printf from the C standard library in assembly site:mourtada.se.

 

Добавить комментарий


Защитный код
Обновить

Top of Page