• Уменьшение отступа

    Обратная связь

    (info@ru-sfera.pw)

Уроки Создание пространства имен в Си с помощью структур


0b170xor

Уважаемый пользователь
Форумчанин
Регистрация
13.01.2020
Сообщения
70
Репутация
27
Одной из больших проблем си является отсутствие пространств имён или системы модулей. Если есть две библиотеки, в которых функции имеют одинаковые имена, то произойдёт коллизия: нельзя будет понять, какая из функций будет использована. Компоновщик выдаст ошибку, что функция определена более одного раза.

К примеру в нашем проекте есть два подключаемых файла File.h

C:
int func(int a, int b);


и соответствующий ему .с файл File.c

C:
#include "File.h"
 
int func(int a, int b)
{
    return a + b; // сумма параметров
}


И файл File2.h

C:
int func(int a, int b);


и соответствующий ему файл File2.c

C:
#include "File2.h"
 
int func(int a, int b)
{
    return a * b; // обратите внимение, здесь в теле функции умножение параметров
}


Если мы попытаемся подключить оба заголовочных файла, то получим ошибку: обнаружен многократно определенный символ - один или более.

C:
#include "File.h"
#include "File2.h"
 
void main()
{
    return 0;
}


Избавиться от неё можно (возможно, ещё есть другие способы...) объявив функции func статическими.

C:
static int func(int a, int b);


Теперь проект скомпилируется, только вот использовать эти функции в main не удастся. Для того, чтобы использовать их, создадим структуру.
Для файла File.h

C:
#include <stdlib.h>
 
typedef struct File
{
    int (*func)(int a, int b);
} File;
 
static int func(int a, int b);
File* getFileNamespace();


для файла File2.h

C:
#include <stdlib.h>
 
typedef struct File2 {
    int (*func)(int a, int b);
} File2;
 
static int func(int a, int b);
File2* getFile2Namespace();


Функции (getFileNamespace, getFile2Namespace) возвращают структуру, у которой поле - указатель на функцию - равно нашей статической функции.
File.c

C:
#include "File.h"
 
int func(int a, int b)
{
    return a + b;
}
 
File* getFileNamespace()
{
    File* temp = (File*)malloc(sizeof(File));
    temp->func = func;
 
    return temp;
}


File2.c

C:
#include "File2.h"
 
int func(int a, int b)
{
    return a * b;
}
 
File2* getFile2Namespace()
{
    File2* temp = (File2*)malloc(sizeof(File2));
    temp->func = func;
 
    return temp;
}


Теперь в main можно создать экземпляр структуры File или File2 и через поле структуры обратиться к статической функции.

C:
#include <conio.h>
#include <stdio.h>
#include "File.h"
#include "File2.h"
 
void main()
{
    int a;
    int b;
    File* namespaceFirst = getFileNamespace();
    File2* namespaceSecond = getFile2Namespace();
    a = 10;
    b = 20;
 
    printf("using namespace first = %d\n", namespaceFirst->func(a, b));
    printf("using namespace second = %d\n", namespaceSecond->func(a, b));
 
    free(namespaceFirst);
    free(namespaceSecond);
    _getch();
}
 

virt

Просветленный
Просветленный
Регистрация
24.11.2016
Сообщения
706
Репутация
228
Единственное, что в функции getFileNamespace и getFile2Namespace каждый раз выделяют память при вызове:
C:
File2* temp = (File2*)malloc(sizeof(File2));
Нужно вынести temp в глобальную область и проверять выделение так:
C:
#include "File2.h"

static File2* temp = NULL;

int func(int a, int b)
{
    return a * b;
}

File2* getFile2Namespace()
{
if (temp == NULL) {
temp = (File2*)malloc(sizeof(File2));
  if (temp == NULL) {
        printf ("No free memory \n");
        return NULL;     
   }
temp->func = func;
return temp;
}
 
Последнее редактирование:

virt

Просветленный
Просветленный
Регистрация
24.11.2016
Сообщения
706
Репутация
228
Ну и при использовании функций getFileNamespace и getFile2Namespace, выходной параметр всегда нужно проверять на NULL, пример:
C:
File* namespaceFirst = getFileNamespace();
if (namespaceFirst != NULL) {
****
} else {
printf("ERROR \n")
}
 

virt

Просветленный
Просветленный
Регистрация
24.11.2016
Сообщения
706
Репутация
228
Вообще указатели на функции хорошая вещь, можно скопировать бинарный код функции, или переместить его, использовать это можно например для скрытия внутренности функции от реверсера, или антивирусов.

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

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

Jefferson

Уважаемый пользователь
Форумчанин
Регистрация
09.06.2019
Сообщения
63
Репутация
35
Ну и костыли на самом деле))
Чем обычный namespace уступает?

А вообще, классы из ++ тема. Можно удобно структурировать код.
 

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 068
Репутация
8 173
Ну и костыли на самом деле))
Чем обычный namespace уступает?

А вообще, классы из ++ тема. Можно удобно структурировать код.
Здесь речь идет о классическом Си, без плюсов.

В классическом Си, нет не классов, нет перегрузки функций, как уже сказано нет пространств имен.)))

Вообще как по мне тогда проще по другому назвать функцию и не парится с указателями на функции.

И это вредно:
C:
free(namespaceFirst);
free(namespaceSecond);

На самом деле namespaceFirst и namespaceSecond не будут ноль и будут "висячие указатели", на несуществующую память.)

Лучше как предложено в посте Уроки - Создание пространства имен в Си с помощью структур

И не делать фри.

И помните, после освобождения памяти указателей, всегда нужно присваивать им NULL в Си и nullptr в С++.)
 
Верх Низ