SGDK. Разбираемся с указателями.

Предисловие.

Без указателей не обойтись при разработке на языке Си, т.к. они слишком эффективны. Все как в реальной жизни, проще указать на информацию в книге, чем прочитать её лично. Также, затронем выделение и освобождение памяти, и структуры.

Зачем вам нужны указатели.

Я расскажу, почему вам нужно их использовать:

  • Указатель почти ничего не весит (32 бита), что хорошо, учитывая что у сеги всего 64 Кб оперативной памяти.
  • Указатель можно передать в функцию, это значит, что вы можете изменять переменную которую передали в функцию в качестве аргумента.
  • Указывать можно не только на переменную, но и на функцию, что дает возможность делать эффективные state-машины (массив функций).

Разбираемся с указателями.

Указатель — это переменная, которая хранит не данные, а адрес в памяти. Поэтому и называется указатель, он указывает на данные.

Технически, указатель ничем не отличается от обычной переменной, различие наступает в способе получения данных из неё и записи в неё. Указатель весит 32 бита, поэтому используйте его лишь тогда, когда это выгодно, например для больших структур.

Для создания указателя применяется «*» (звездочка) после типа.

u32* pointerTest;

Для получения адреса переменной используется «&» (Амперсанд).

Давайте, передадим адрес переменной valueTest в указатель pointerTest, а потом, получим данные из указателя.

u32 valueTest = 1337; //Создали переменную valueTest и записали в неё значение
u32* pointerTest = &valueTest; //Передали адрес valueTest в указатель pointerTest
KLog_U1("PointerTest: ", *pointerTest); //Получили данные из указателя pointerTest, и вывели в качестве лога.

Заметьте, что у указателя тот же тип что и у обычной переменной.

u32 u32*

В результате получили значение из указателя.

Резюмируя. В pointerTest хранится адрес valueTest, мы вывели данные по адресу valueTest с помощью «*».

Чтобы получить данные по адресу который хранится в указателе, используйте «*», чтобы посмотреть адрес который хранится в указателе, не используем «*».

KLog_U1("PointerTest: ", pointerTest); //Адрес
KLog_U1("PointerTest2: ", *pointerTest); //Данные

Про вес указателей.

Данный текст написал werton

На m68k указатели 32х битные (как ни странно для 16 битного процессора) и занимает соответственно 4 байта в памяти, т.е. эквивалентен по размеру типу u32/s32/f32. Так что передавать по указателю переменные простых типов не имеет, практически, никакого смысла. Так же, не имеет особого смысла по указателю передавать структуры размер которых <= 4 байт, например вектора с полями на <= u16, или встроенные структуры типа BoxCollision, которая имеет 4 поля по u8.

Указатель на структуру.

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

typedef struct {
  u32 val1;
  u32 val2;
  u32 val3;
} testStruct;
testStruct val1 = (testStruct){1,2,3};
testStruct* pointerTest = &val1;
KLog_U1("PointerTest: ", pointerTest->val1);

Чтобы получить данные из структуры используется «.» (точка).

val1.val1

Тогда как, для получения данных из указателя на структуру, используется «->» (стрелка)

pointerTest->val1

В итоге, получили данные из структуры по указателю

Передаем массив в структуру.

Вот пример:

u32 testArr[3] = {11,22,33};
typedef struct {
  u32* arrPointer;
} testStruct;
testStruct structVar;
structVar.arrPointer = testArr;

KLog_U1("PointerTest: ", structVar.arrPointer[2]);

Как видим, для передачи надо создать указатель с тем же типом что и массив

u32* arrPointer;

И присвоить массив указателю

structVar.arrPointer = testArr;

Структура которая указывает на себя.

Бывает так, что нужно сделать структуру которая может хранить указатель своего типа. Делается это так:

typedef struct {
  struct EntityMerged* friend;
} EntityMerged;

В данном случае friend, это указатель на свою же структуру.

Передаем указатель в функцию.

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

void changeU32To5(u32* num){
  *num = 5;
}
u32 testVar = 0;
changeU32To5(&testVar);
KLog_U1("testVar: ", testVar);

В функцию changeU32To5 передали адрес переменной testVar, и в этой функции сменили значение на 5.

Создаем массив функций.

Массив функций создается так:

void(* showEntityFuncArr[])(EntityMerged*) = {showEntitySimple, showBarierDead, showCoin};

Для функций такого типа:

void showEntitySimple(EntityMerged* entity) {
  //something
}

Т.е. по следующему шаблону:

возвращаемый_тип(* название_массива_функций[])(тип_аргумента) = {название_функции1, название_функции2, ... , название_функцииN};

Если у функции нет аргументов, то вместо аргумента пишем void.

void(* showEntityFuncArr[])(void) = {showEntitySimple, showBarierDead, showCoin};

Чтобы выполнить функцию из массива функций, используем следующий шаблон:

название_массива_функций[индекс_в_массиве](аргумент);

И в качестве примера:

showEntityFuncArr[entity->entityType](entity);

Выделение памяти (аллокация).

Для выделения памяти (аллокации) используем MEM_alloc

MEM_alloc(количество_байтов)

На выходе функция выдает указатель на выделенный блок памяти, на память под нашу будущую переменную.

Если хотим выделить память под конкретную структуру, то используем sizeof.

curLocalVariables = MEM_alloc(sizeof(LocalVariableMerged)); //выделяем память под структуру LocalVariableMerged

Если нужно выделить память под массив, то умножаем sizeof(..) на количество элементов в массиве.

curLocalVariables = MEM_alloc(sizeof(LocalVariableMerged)*LocalVariableMerged_size);

Записывание данных в указатель.

Для записи в указатель используется функция memcpy у которой следующий синтаксис:

memcpy(указатель_куда, указатель_откуда, размер_в_байтах)

Т.е. в нашем примере с curLocalVariables, делаем так:

memcpy(curLocalVariables, LevelFull_arr[levelNum].variable_arr, sizeof(LocalVariableMerged));

где LevelFull_arr[levelNum].variable_arr это нужные данные текущего уровня.

Освобождение памяти (деаллокация).

Для освобождения памяти используется функция MEM_free, в которую передается указатель.

MEM_free(curLocalVariables);

После того как деаллоцируете все что нужно, используйте

MEM_pack();

Для уменьшения фрагментации памяти.

Узнать количество свободной RAM

Чтобы узнать сколько осталось RAM, используйте эту комманду:

KLog_U1("FreeMem: ", MEM_getFree());

Она возвращает количество свободных байтов, и выводит в лог эмулятора GENS KMod.

Итоговый результат.

Пожалуйста отключи блокировщик рекламы, или внеси сайт в белый список!

Please disable your adblocker or whitelist this site!