#443_C_LIB_MTH
Функции popen() и pclose() в ЯП C предназначены для работы с процессами и организации взаимодействия между родительским процессом и дочерним процессом, запущенным через командную оболочку.
Эти функции полезны для выполнения внешних команд и чтения или записи данных с ними.
Функция popen() — открывает канал связи с новым процессом, созданным командой, указанной в качестве параметра.
Этот процесс выполняется в контексте командной оболочки (обычно /bin/sh), и popen() возвращает файловый дескриптор, который можно использовать для чтения или записи данных в стандартный ввод или вывод нового процесса.
Прототип:
Параметры:
command: строка, содержащая команду, которую нужно выполнить.
mode: строка, определяющая режим работы с каналом:
"r" — чтение данных из стандартного вывода дочернего процесса.
"w" — запись данных в стандартный ввод дочернего процесса.
Возвращаемое значение:
Указатель на объект типа FILE, если открытие прошло успешно.
NULL, если произошла ошибка.
Функция pclose() — закрывает канал, открытый ранее с помощью popen(), и ждет завершения дочернего процесса.
Она возвращает статус завершения дочернего процесса.
Прототип:
Параметр:
stream: указатель на объект типа FILE, возвращаемый функцией popen().
Возвращаемое значение:
Статус завершения дочернего процесса (аналогично значению, возвращаемому функцией system()).
-1, если произошла ошибка.
Пример использования popen() и pclose():
Допустим, нужно выполнить команду ls -l и прочитать результаты её выполнения:
Описание примера:
Открытие канала: Используем popen("ls -l", "r"), чтобы открыть канал для чтения результатов выполнения команды ls -l.
Чтение данных: В цикле while мы читаем строки из канала и выводим их на стандартный вывод с помощью fprintf().
Закрытие канала: После завершения чтения данных мы закрываем канал с помощью pclose(), ожидая завершения дочернего процесса и проверяя наличие ошибок.
Особенности и ограничения:
Безопасность: Нужно использовать popen() осторожно, особенно если команды формируются на основе пользовательского ввода.
Необходимо всегда проверять и экранировать потенциально опасные данные, чтобы предотвратить атаки типа инъекции команд.
Платформенная зависимость: Функции popen() и pclose() зависят от наличия командной оболочки и могут вести себя по-разному на разных платформах.
Производительность: Эти функции могут быть менее эффективны, чем прямое взаимодействие с дочерними процессами через fork() и exec(), так как создают дополнительный уровень косвенности через командную оболочку.
Функции popen() и pclose() удобны для простых сценариев, когда нужно выполнить внешнюю команду и обработать её вывод или ввод.
Однако для более сложных задач, требующих большего контроля над процессом, рекомендуется использовать более низкоуровневые функции, такие как fork(), exec(), dup2() и waitpid().
Функции popen() и pclose() в ЯП C предназначены для работы с процессами и организации взаимодействия между родительским процессом и дочерним процессом, запущенным через командную оболочку.
Эти функции полезны для выполнения внешних команд и чтения или записи данных с ними.
Функция popen() — открывает канал связи с новым процессом, созданным командой, указанной в качестве параметра.
Этот процесс выполняется в контексте командной оболочки (обычно /bin/sh), и popen() возвращает файловый дескриптор, который можно использовать для чтения или записи данных в стандартный ввод или вывод нового процесса.
Прототип:
FILE *popen(const char *command, const char *mode);
Параметры:
command: строка, содержащая команду, которую нужно выполнить.
mode: строка, определяющая режим работы с каналом:
"r" — чтение данных из стандартного вывода дочернего процесса.
"w" — запись данных в стандартный ввод дочернего процесса.
Возвращаемое значение:
Указатель на объект типа FILE, если открытие прошло успешно.
NULL, если произошла ошибка.
Функция pclose() — закрывает канал, открытый ранее с помощью popen(), и ждет завершения дочернего процесса.
Она возвращает статус завершения дочернего процесса.
Прототип:
int pclose(FILE *stream);
Параметр:
stream: указатель на объект типа FILE, возвращаемый функцией popen().
Возвращаемое значение:
Статус завершения дочернего процесса (аналогично значению, возвращаемому функцией system()).
-1, если произошла ошибка.
Пример использования popen() и pclose():
Допустим, нужно выполнить команду ls -l и прочитать результаты её выполнения:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *pipe;
char buffer[128];
pipe = popen("ls -l", "r");
if (pipe == NULL) {
perror("Ошибка при открытии канала");
return EXIT_FAILURE;
}
while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
fprintf(stdout, "%s", buffer);
}
if (pclose(pipe) == -1) {
perror("Ошибка при закрытии канала");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Описание примера:
Открытие канала: Используем popen("ls -l", "r"), чтобы открыть канал для чтения результатов выполнения команды ls -l.
Чтение данных: В цикле while мы читаем строки из канала и выводим их на стандартный вывод с помощью fprintf().
Закрытие канала: После завершения чтения данных мы закрываем канал с помощью pclose(), ожидая завершения дочернего процесса и проверяя наличие ошибок.
Особенности и ограничения:
Безопасность: Нужно использовать popen() осторожно, особенно если команды формируются на основе пользовательского ввода.
Необходимо всегда проверять и экранировать потенциально опасные данные, чтобы предотвратить атаки типа инъекции команд.
Платформенная зависимость: Функции popen() и pclose() зависят от наличия командной оболочки и могут вести себя по-разному на разных платформах.
Производительность: Эти функции могут быть менее эффективны, чем прямое взаимодействие с дочерними процессами через fork() и exec(), так как создают дополнительный уровень косвенности через командную оболочку.
Функции popen() и pclose() удобны для простых сценариев, когда нужно выполнить внешнюю команду и обработать её вывод или ввод.
Однако для более сложных задач, требующих большего контроля над процессом, рекомендуется использовать более низкоуровневые функции, такие как fork(), exec(), dup2() и waitpid().
#444_Cpp_PkS
Как разработать систему плагинов на С++?
Разработка системы плагинов на C++ требует четкого понимания архитектуры программы и возможностей языка. Основная идея заключается в том, чтобы создать динамически загружаемые библиотеки (DLL на Windows, shared objects на Unix-подобных системах), которые программа сможет подключать во время выполнения. Вот пошаговое руководство по созданию такой системы:
Шаг 1: Определите интерфейс плагина
Определите базовый интерфейс, который будут реализовывать плагины. Это позволит основной программе взаимодействовать с плагинами через единый интерфейс. Интерфейс должен содержать виртуальные функции, которые будет переопределять каждый плагин.
Пример интерфейса:
Шаг 2: Создайте абстрактный класс-загрузчик
Создайте абстрактный класс, который будет отвечать за загрузку и выгрузку плагинов. Этот класс будет использоваться основной программой для работы с плагинами.
Пример класса-загрузчика:
Шаг 3: Реализуйте функцию загрузки плагинов
Основная программа должна иметь возможность загружать и использовать плагины. Для этого создайте функцию, которая будет искать и загружать доступные плагины.
Пример функции загрузки плагинов:
Шаг 4: Реализуйте пример плагина
Теперь вы можете реализовать конкретный плагин, который будет соответствовать вашему интерфейсу IPlugin. Плагин должен экспортировать функцию CreatePlugin, которая возвращает указатель на экземпляр вашего плагина.
Как разработать систему плагинов на С++?
Разработка системы плагинов на C++ требует четкого понимания архитектуры программы и возможностей языка. Основная идея заключается в том, чтобы создать динамически загружаемые библиотеки (DLL на Windows, shared objects на Unix-подобных системах), которые программа сможет подключать во время выполнения. Вот пошаговое руководство по созданию такой системы:
Шаг 1: Определите интерфейс плагина
Определите базовый интерфейс, который будут реализовывать плагины. Это позволит основной программе взаимодействовать с плагинами через единый интерфейс. Интерфейс должен содержать виртуальные функции, которые будет переопределять каждый плагин.
Пример интерфейса:
class IPlugin {
public:
virtual ~IPlugin() {}
virtual void Initialize() = 0;
virtual void DoSomething() = 0;
};Шаг 2: Создайте абстрактный класс-загрузчик
Создайте абстрактный класс, который будет отвечать за загрузку и выгрузку плагинов. Этот класс будет использоваться основной программой для работы с плагинами.
Пример класса-загрузчика:
#include <iostream>
#include <string>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
class PluginLoader {
private:
#ifdef _WIN32
HMODULE handle;
#else
void* handle;
#endif
public:
PluginLoader(const std::string& filename) {
Load(filename);
}
~PluginLoader() {
Unload();
}
bool IsLoaded() const {
#ifdef _WIN32
return handle != nullptr;
#else
return handle != NULL;
#endif
}
template<typename T>
T GetSymbol(const char* symbolName) {
if (!IsLoaded()) {
throw std::runtime_error("Plugin not loaded");
}
#ifdef _WIN32
FARPROC procAddress = GetProcAddress(handle, symbolName);
if (!procAddress) {
throw std::runtime_error("GetProcAddress failed");
}
return reinterpret_cast<T>(procAddress);
#else
void* funcPtr = dlsym(handle, symbolName);
if (!funcPtr) {
throw std::runtime_error(dlerror());
}
return reinterpret_cast<T>(funcPtr);
#endif
}
protected:
void Load(const std::string& filename) {
#ifdef _WIN32
handle = LoadLibraryA(filename.c_str());
if (!handle) {
throw std::runtime_error("LoadLibrary failed");
}
#else
handle = dlopen(filename.c_str(), RTLD_LAZY);
if (!handle) {
throw std::runtime_error(dlerror());
}
#endif
}
void Unload() {
if (IsLoaded()) {
#ifdef _WIN32
FreeLibrary(handle);
#else
dlclose(handle);
#endif
handle = nullptr;
}
}
};
Шаг 3: Реализуйте функцию загрузки плагинов
Основная программа должна иметь возможность загружать и использовать плагины. Для этого создайте функцию, которая будет искать и загружать доступные плагины.
Пример функции загрузки плагинов:
std::vector<IPlugin*> LoadPlugins(const std::string& directory) {
std::vector<IPlugin*> plugins;
/* Найдите все файлы DLL/SO в указанной директории */
for (const auto& entry : fs::directory_iterator(directory)) {
if (entry.path().extension() == ".dll" || entry.path().extension() == ".so") {
try {
PluginLoader loader(entry.path().string());
IPlugin* (*createPlugin)() = loader.GetSymbol<IPlugin*(*)()>("CreatePlugin");
if (createPlugin) {
IPlugin* plugin = createPlugin();
plugin->Initialize();
plugins.push_back(plugin);
}
} catch (const std::exception& e) {
std::cerr << "Error loading plugin from " << entry.path() << ": " << e.what() << std::endl;
}
}
}
return plugins;
}Шаг 4: Реализуйте пример плагина
Теперь вы можете реализовать конкретный плагин, который будет соответствовать вашему интерфейсу IPlugin. Плагин должен экспортировать функцию CreatePlugin, которая возвращает указатель на экземпляр вашего плагина.
Пример плагина:
Шаг 5: Соберите основную программу
Соберите основную программу, которая будет загружать и использовать ваши плагины. Она должна находить и загружать все доступные плагины из заданной директории, вызывать их методы и освобождать ресурсы после завершения работы.
Пример основной программы:
Таким образом, вы создаете гибкую систему плагинов, позволяющую расширять функциональность вашей программы без изменения основного кода. Плагины могут быть написаны независимо от основной программы и добавлены позже, обеспечивая модульность и удобство поддержки.
// Plugin.cpp
#include "IPlugin.h"
class MyPlugin : public IPlugin {
public:
void Initialize() override {
std::cout << "MyPlugin initialized" << std::endl;
}
void DoSomething() override {
std::cout << "Doing something in MyPlugin" << std::endl;
}
};
extern "C" __declspec(dllexport) IPlugin* CreatePlugin() {
return new MyPlugin();
}
Шаг 5: Соберите основную программу
Соберите основную программу, которая будет загружать и использовать ваши плагины. Она должна находить и загружать все доступные плагины из заданной директории, вызывать их методы и освобождать ресурсы после завершения работы.
Пример основной программы:
int main() {
std::vector<IPlugin*> plugins = LoadPlugins("plugins");
for (auto plugin : plugins) {
plugin->DoSomething();
}
for (auto plugin : plugins) {
delete plugin;
}
return 0;
}Таким образом, вы создаете гибкую систему плагинов, позволяющую расширять функциональность вашей программы без изменения основного кода. Плагины могут быть написаны независимо от основной программы и добавлены позже, обеспечивая модульность и удобство поддержки.
#445_TP
Что такое плагин?
Плагин — независимый программный компонент, который добавляется к основному приложению для расширения его функциональности.
Плагины позволяют пользователям и разработчикам добавлять новые возможности в уже существующее ПО без необходимости изменять исходный код самого приложения.
Примеры использования плагинов:
Веб-браузеры, такие как Google Chrome или Mozilla Firefox, где пользователи могут устанавливать плагины для блокировки рекламы, управления паролями и многого другого.
Текстовые редакторы, такие как Visual Studio Code или Sublime Text, которые поддерживают установку различных плагинов для улучшения опыта разработки.
Игровые движки, такие как Unity или Unreal Engine, где разработчики могут создавать дополнительные инструменты и расширения для упрощения процесса разработки игр.
Преимущества использования плагинов:
Модульность — основное приложение остается простым и легким, а дополнительная функциональность реализуется через отдельные компоненты.
Гибкость — пользователи могут выбирать, какие именно функции им нужны, устанавливая только необходимые плагины.
Обновляемость — плагины могут обновляться отдельно от основного приложения, что делает процесс обновления проще и быстрее.
Совместимость — разработчики могут создавать плагины, совместимые с различными версиями основного приложения, минимизируя необходимость переписывания кода.
Для реализации плагинов часто используются механизмы динамической компоновки, когда библиотеки загружаются и выгружаются во время выполнения программы.
В случае с C++, это могут быть динамические библиотеки (.dll на Windows, .so на Linux).
Что такое плагин?
Плагин — независимый программный компонент, который добавляется к основному приложению для расширения его функциональности.
Плагины позволяют пользователям и разработчикам добавлять новые возможности в уже существующее ПО без необходимости изменять исходный код самого приложения.
Примеры использования плагинов:
Веб-браузеры, такие как Google Chrome или Mozilla Firefox, где пользователи могут устанавливать плагины для блокировки рекламы, управления паролями и многого другого.
Текстовые редакторы, такие как Visual Studio Code или Sublime Text, которые поддерживают установку различных плагинов для улучшения опыта разработки.
Игровые движки, такие как Unity или Unreal Engine, где разработчики могут создавать дополнительные инструменты и расширения для упрощения процесса разработки игр.
Преимущества использования плагинов:
Модульность — основное приложение остается простым и легким, а дополнительная функциональность реализуется через отдельные компоненты.
Гибкость — пользователи могут выбирать, какие именно функции им нужны, устанавливая только необходимые плагины.
Обновляемость — плагины могут обновляться отдельно от основного приложения, что делает процесс обновления проще и быстрее.
Совместимость — разработчики могут создавать плагины, совместимые с различными версиями основного приложения, минимизируя необходимость переписывания кода.
Для реализации плагинов часто используются механизмы динамической компоновки, когда библиотеки загружаются и выгружаются во время выполнения программы.
В случае с C++, это могут быть динамические библиотеки (.dll на Windows, .so на Linux).
#446_LIB_NTWK_PkS_TP
Что такое RPC?
Какие библиотеки знаете?
RPC (Remote Procedure Call) — механизм взаимодействия между процессами, который позволяет одной программе вызывать функции другой программы, находящейся на удалённом компьютере, как будто она вызывается локально.
В отличие от прямого обмена сообщениями, где одна программа отправляет данные другой, RPC абстрагирует этот процесс, делая его похожим на обычный вызов функции внутри одного процесса.
Основные компоненты RPC:
Клиент — программа, которая вызывает удалённую функцию.
Сервер — программа, предоставляющая определённые функции для вызова клиентами.
Маршалинг/Демаршалинг — процесс преобразования данных (например, структуры или объекта) в последовательность байтов для передачи по сети и обратное преобразование на стороне получателя.
Транспортный уровень — механизм, обеспечивающий передачу данных между клиентом и сервером (обычно TCP/IP).
Популярные реализации RPC:
gRPC — разработан Google, основан на протоколе HTTP/2, использует Protobuf для сериализации данных, поддерживает множество ЯП: C++, Java, Python, Go, Ruby, Node.js и др. Высокая производительность и низкая задержка.
Thrift — создан Facebook. Поддерживает широкий спектр языков программирования. Позволяет определять интерфейсы сервисов с помощью IDL (Interface Definition Language). Гибкий выбор транспортных протоколов и механизмов сериализации.
XML-RPC — один из старейших стандартов RPC. Использует XML для представления данных. Простота реализации, но относительно медленный по сравнению с современными решениями.
JSON-RPC — cовременная альтернатива XML-RPC. Использует JSON для сериализации данных. Легко интегрируется с веб-приложениями благодаря поддержке JSON большинством современных языков программирования.
RMI (Java Remote Method Invocation) — cпецифичен для платформы Java. Позволяет вызывать методы объектов, находящихся на другом JVM. Используется преимущественно в корпоративных приложениях на Java.
SOAP (Simple Object Access Protocol) — протокол на основе XML. Широко используется в корпоративных системах и веб-сервисах. Обеспечивает высокую совместимость и стандартизацию.
CORBA (Common Object Request Broker Architecture) — стандарт для распределённых объектных вычислений. Поддерживает межъязыковую и межплатформенную интеграцию. Применяется в сложных распределённых системах.
ONC RPC (Open Network Computing RPC) — разработка Sun Microsystems. Часто используется в Unix-системах. Реализована в NFS (Network File System).
Примеры использования:
gRPC часто применяется в микросервисной архитектуре благодаря своей производительности и поддержке множества языков.
Thrift популярен среди крупных компаний, таких как Facebook и Airbnb, благодаря гибкости и масштабируемости.
JSON-RPC широко используется в веб-разработке из-за простоты интеграции с JavaScript и легковесности формата JSON.
Выбор конкретной библиотеки зависит от требований проекта, используемых технологий и необходимых возможностей.
Что такое RPC?
Какие библиотеки знаете?
RPC (Remote Procedure Call) — механизм взаимодействия между процессами, который позволяет одной программе вызывать функции другой программы, находящейся на удалённом компьютере, как будто она вызывается локально.
В отличие от прямого обмена сообщениями, где одна программа отправляет данные другой, RPC абстрагирует этот процесс, делая его похожим на обычный вызов функции внутри одного процесса.
Основные компоненты RPC:
Клиент — программа, которая вызывает удалённую функцию.
Сервер — программа, предоставляющая определённые функции для вызова клиентами.
Маршалинг/Демаршалинг — процесс преобразования данных (например, структуры или объекта) в последовательность байтов для передачи по сети и обратное преобразование на стороне получателя.
Транспортный уровень — механизм, обеспечивающий передачу данных между клиентом и сервером (обычно TCP/IP).
Популярные реализации RPC:
gRPC — разработан Google, основан на протоколе HTTP/2, использует Protobuf для сериализации данных, поддерживает множество ЯП: C++, Java, Python, Go, Ruby, Node.js и др. Высокая производительность и низкая задержка.
Thrift — создан Facebook. Поддерживает широкий спектр языков программирования. Позволяет определять интерфейсы сервисов с помощью IDL (Interface Definition Language). Гибкий выбор транспортных протоколов и механизмов сериализации.
XML-RPC — один из старейших стандартов RPC. Использует XML для представления данных. Простота реализации, но относительно медленный по сравнению с современными решениями.
JSON-RPC — cовременная альтернатива XML-RPC. Использует JSON для сериализации данных. Легко интегрируется с веб-приложениями благодаря поддержке JSON большинством современных языков программирования.
RMI (Java Remote Method Invocation) — cпецифичен для платформы Java. Позволяет вызывать методы объектов, находящихся на другом JVM. Используется преимущественно в корпоративных приложениях на Java.
SOAP (Simple Object Access Protocol) — протокол на основе XML. Широко используется в корпоративных системах и веб-сервисах. Обеспечивает высокую совместимость и стандартизацию.
CORBA (Common Object Request Broker Architecture) — стандарт для распределённых объектных вычислений. Поддерживает межъязыковую и межплатформенную интеграцию. Применяется в сложных распределённых системах.
ONC RPC (Open Network Computing RPC) — разработка Sun Microsystems. Часто используется в Unix-системах. Реализована в NFS (Network File System).
Примеры использования:
gRPC часто применяется в микросервисной архитектуре благодаря своей производительности и поддержке множества языков.
Thrift популярен среди крупных компаний, таких как Facebook и Airbnb, благодаря гибкости и масштабируемости.
JSON-RPC широко используется в веб-разработке из-за простоты интеграции с JavaScript и легковесности формата JSON.
Выбор конкретной библиотеки зависит от требований проекта, используемых технологий и необходимых возможностей.
#447_ADM_DVOP_NTWK
Как настроить локальную сеть между двумя компьютерами с Linux подключенными к роутеру по Wi-Fi?
Пошаговая инструкция для настройки сети:
Шаг 1: Подключение компьютеров к роутеру.
Убедитесь, что оба компьютера подключены к одному и тому же Wi-Fi роутеру.
Для этого откройте настройки сети на каждом компьютере и проверьте, что они используют одно и то же подключение к Интернету.
Шаг 2: Настройка IP-адресов
Нужно убедиться, что компьютеры находятся в одной подсети.
Обычно домашние маршрутизаторы автоматически назначают IP-адреса через DHCP, однако иногда полезно вручную задать статические IP-адреса.
Автоматическое назначение IP-адресов (DHCP) — по умолчанию большинство домашних роутеров включают DHCP-сервер, который автоматически назначает IP-адреса устройствам в вашей сети.
Чтобы узнать назначенный IP-адрес на компьютере с Linux, выполните следующую команду в терминале:
Найдите интерфейс, связанный с Wi-Fi адаптером (обычно это wlan0 или wlp2s0), и посмотрите на строку, начинающуюся с inet, например:
Здесь 192.168.1.100 — ваш IP-адрес.
Назначение статического IP-адреса:
Если нужно назначить статический IP-адрес, выполните следующие шаги:
— откройте файл конфигурации сетевых интерфейсов. В большинстве дистрибутивов Linux это /etc/network/interfaces или /etc/sysconfig/network-scripts/ifcfg-*.
— добавьте необходимые параметры. Пример для Ubuntu/Debian:
Измените содержимое файла следующим образом:
Сохраните изменения и примените новую конфигурацию:
Шаг 3: Проверка соединения.
После назначения IP-адресов проверьте соединение между компьютерами с помощью команды ping. На одном компьютере выполните:
Если пинги проходят успешно, значит, компьютеры видят друг друга в сети.
Шаг 4: Настройка общего доступа к файлам (опционально).
Если нужно обмениваться файлами между компьютерами, можно настроить общий доступ с использованием протокола Samba.
Установите Samba на обоих компьютерах:
Создайте общую папку и настройте её права:
Отредактируйте файл конфигурации Samba:
Добавьте следующий раздел:
Перезапустите службу Samba:
Теперь можно получить доступ к этой папке с другого компьютера, используя IP-адрес первого компьютера:
Таким образом, создается локальная сеть между двумя компьютерами с Linux, подключёнными к одному Wi-Fi роутеру и настраивается возможность обмена файлами с использованием Samba.
Как настроить локальную сеть между двумя компьютерами с Linux подключенными к роутеру по Wi-Fi?
Пошаговая инструкция для настройки сети:
Шаг 1: Подключение компьютеров к роутеру.
Убедитесь, что оба компьютера подключены к одному и тому же Wi-Fi роутеру.
Для этого откройте настройки сети на каждом компьютере и проверьте, что они используют одно и то же подключение к Интернету.
Шаг 2: Настройка IP-адресов
Нужно убедиться, что компьютеры находятся в одной подсети.
Обычно домашние маршрутизаторы автоматически назначают IP-адреса через DHCP, однако иногда полезно вручную задать статические IP-адреса.
Автоматическое назначение IP-адресов (DHCP) — по умолчанию большинство домашних роутеров включают DHCP-сервер, который автоматически назначает IP-адреса устройствам в вашей сети.
Чтобы узнать назначенный IP-адрес на компьютере с Linux, выполните следующую команду в терминале:
ip addr show
Найдите интерфейс, связанный с Wi-Fi адаптером (обычно это wlan0 или wlp2s0), и посмотрите на строку, начинающуюся с inet, например:
inet 192.168.1.100/24 brd 192.168.1.255 scope global dynamic wlan0
Здесь 192.168.1.100 — ваш IP-адрес.
Назначение статического IP-адреса:
Если нужно назначить статический IP-адрес, выполните следующие шаги:
— откройте файл конфигурации сетевых интерфейсов. В большинстве дистрибутивов Linux это /etc/network/interfaces или /etc/sysconfig/network-scripts/ifcfg-*.
— добавьте необходимые параметры. Пример для Ubuntu/Debian:
sudo vim /etc/netplan/*.yaml
Измените содержимое файла следующим образом:
network:
version: 2
renderer: networkd
ethernets:
enp0s3: # Замените на имя вашего интерфейса
dhcp4: no
addresses: [192.168.1.101/24]
gateway4: 192.168.1.1
nameservers:
addresses: [8.8.8.8, 8.8.4.4]
Сохраните изменения и примените новую конфигурацию:
sudo netplan apply
Шаг 3: Проверка соединения.
После назначения IP-адресов проверьте соединение между компьютерами с помощью команды ping. На одном компьютере выполните:
ping <IP-адрес другого компьютера>
Если пинги проходят успешно, значит, компьютеры видят друг друга в сети.
Шаг 4: Настройка общего доступа к файлам (опционально).
Если нужно обмениваться файлами между компьютерами, можно настроить общий доступ с использованием протокола Samba.
Установите Samba на обоих компьютерах:
sudo apt-get install samba
Создайте общую папку и настройте её права:
mkdir /home/<ваш_пользователь>/share
chmod 777 /home/<ваш_пользователь>/share
Отредактируйте файл конфигурации Samba:
sudo vim /etc/samba/smb.conf
Добавьте следующий раздел:
[share]
comment = Shared Folder
path = /home/<ваш_пользователь>/share
browsable = yes
writable = yes
guest ok = yes
read only = no
create mask = 0777
directory mask = 0777
Перезапустите службу Samba:
sudo service smbd restart
Теперь можно получить доступ к этой папке с другого компьютера, используя IP-адрес первого компьютера:
smb://<IP-адрес первого компьютера>/share
Таким образом, создается локальная сеть между двумя компьютерами с Linux, подключёнными к одному Wi-Fi роутеру и настраивается возможность обмена файлами с использованием Samba.
#448_ADM_DVOP_NTWK
Как выявить файлы одинаковые по содержанию, но имеющие разные имена?
Выявление файлов с одинаковым содержимым, но разными именами, можно сделать несколькими способами. Один из наиболее распространённых подходов заключается в использовании контрольных сумм (хешей). Этот метод сравнивает хеш-значения файлов, чтобы определить, идентичны ли они по содержимому.
Использование утилиты md5sum
Один из простых способов — использовать утилиту md5sum, которая генерирует MD5-хеш для каждого файла. Алгоритм MD5 считается достаточно надёжным для проверки целостности файлов.
Генерация списка хешей: Сначала создайте список хешей для всех файлов в интересующей вас директории. Для этого выполните следующую команду:
md5sum * > checksums.txt
Эта команда создаст файл checksums.txt, содержащий MD5-хеши всех файлов в текущей директории.
Поиск дубликатов: Теперь можно отсортировать и сгруппировать файлы по их хешам, чтобы найти дубликаты:
sort checksums.txt | uniq -d -w32
Команда sort сортирует строки по хешам, а uniq -d -w32 выводит только строки с дублирующимися хешами (где длина хеша составляет 32 символа).
Проверка результатов: После выполнения этих команд вы получите список файлов, имеющих одинаковые хеши, что означает, что их содержимое идентично.
Использование утилиты fdupes
Ещё один удобный инструмент для поиска дубликатов файлов — это утилита fdupes. Она уже включает в себя алгоритм сравнения хешей и позволяет быстро находить дубликаты.
Установка fdupes: Утилита fdupes может отсутствовать в стандартной поставке некоторых дистрибутивов Linux. Её можно установить командой:
sudo apt-get install fdupes # для Debian/Ubuntu
sudo yum install fdupes # для CentOS/RHEL/Fedora
Поиск дубликатов: Чтобы найти все дубликаты файлов в указанной директории, выполните команду:
fdupes -r /path/to/directory
Флаг -r рекурсивно просматривает все подпапки в заданной директории.
Удаление дубликатов: Если вы хотите удалить найденные дубликаты, оставив только один экземпляр каждого файла, добавьте флаг -d:
fdupes -rd /path/to/directory
Программа спросит, какой файл оставить, а какие удалить.
Использование утилиты diff
Если у вас есть подозрение, что два конкретных файла могут быть одинаковыми, вы можете сравнить их содержимое с помощью утилиты diff:
diff file1 file2
Если вывод пустой, значит, файлы идентичны.
Использование хеш-функций (например, md5sum) или специализированных инструментов (таких как fdupes) позволяет эффективно выявлять файлы с одинаковым содержимым, даже если они имеют разные имена.
Как выявить файлы одинаковые по содержанию, но имеющие разные имена?
Выявление файлов с одинаковым содержимым, но разными именами, можно сделать несколькими способами. Один из наиболее распространённых подходов заключается в использовании контрольных сумм (хешей). Этот метод сравнивает хеш-значения файлов, чтобы определить, идентичны ли они по содержимому.
Использование утилиты md5sum
Один из простых способов — использовать утилиту md5sum, которая генерирует MD5-хеш для каждого файла. Алгоритм MD5 считается достаточно надёжным для проверки целостности файлов.
Генерация списка хешей: Сначала создайте список хешей для всех файлов в интересующей вас директории. Для этого выполните следующую команду:
md5sum * > checksums.txt
Эта команда создаст файл checksums.txt, содержащий MD5-хеши всех файлов в текущей директории.
Поиск дубликатов: Теперь можно отсортировать и сгруппировать файлы по их хешам, чтобы найти дубликаты:
sort checksums.txt | uniq -d -w32
Команда sort сортирует строки по хешам, а uniq -d -w32 выводит только строки с дублирующимися хешами (где длина хеша составляет 32 символа).
Проверка результатов: После выполнения этих команд вы получите список файлов, имеющих одинаковые хеши, что означает, что их содержимое идентично.
Использование утилиты fdupes
Ещё один удобный инструмент для поиска дубликатов файлов — это утилита fdupes. Она уже включает в себя алгоритм сравнения хешей и позволяет быстро находить дубликаты.
Установка fdupes: Утилита fdupes может отсутствовать в стандартной поставке некоторых дистрибутивов Linux. Её можно установить командой:
sudo apt-get install fdupes # для Debian/Ubuntu
sudo yum install fdupes # для CentOS/RHEL/Fedora
Поиск дубликатов: Чтобы найти все дубликаты файлов в указанной директории, выполните команду:
fdupes -r /path/to/directory
Флаг -r рекурсивно просматривает все подпапки в заданной директории.
Удаление дубликатов: Если вы хотите удалить найденные дубликаты, оставив только один экземпляр каждого файла, добавьте флаг -d:
fdupes -rd /path/to/directory
Программа спросит, какой файл оставить, а какие удалить.
Использование утилиты diff
Если у вас есть подозрение, что два конкретных файла могут быть одинаковыми, вы можете сравнить их содержимое с помощью утилиты diff:
diff file1 file2
Если вывод пустой, значит, файлы идентичны.
Использование хеш-функций (например, md5sum) или специализированных инструментов (таких как fdupes) позволяет эффективно выявлять файлы с одинаковым содержимым, даже если они имеют разные имена.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Библиотека C/C++ разработчика
🚀Это отличный ресурс для программистов, работающих с C++. Можно найти подробную документацию по стандартной библиотеке, STL, различным версиям стандарта C++, а также примеры кода и объяснения по ключевым аспектам языка.
Справочник по C++
C++11, C++14, C++17, C++20, C++23, C++26 │ Поддержка компиляторами C++11, C++14, C++17, C++20, C++23, C++26
Справочник по языку C
C89, C95, C99, C11, C17, C23 │ Поддержка компиляторами C99, C23
https://ru.cppreference.com/w/
#cpp #programming
👉 @cpp_lib
Справочник по C++
C++11, C++14, C++17, C++20, C++23, C++26 │ Поддержка компиляторами C++11, C++14, C++17, C++20, C++23, C++26
Справочник по языку C
C89, C95, C99, C11, C17, C23 │ Поддержка компиляторами C99, C23
https://ru.cppreference.com/w/
#cpp #programming
👉 @cpp_lib
#449_ALG_C_Cpp_TP
Структура данных интрузивный список реализация на Си и С++.
Интрузивный список (intrusive list) — структура данных, где каждый элемент содержит указатели на следующий и предыдущий элементы, и эти указатели хранятся непосредственно внутри самих элементов, а не в отдельной структуре узла списка.
Это позволяет избежать дополнительного уровня косвенности и улучшает производительность.
Реализация на C.
.
Структура данных интрузивный список реализация на Си и С++.
Интрузивный список (intrusive list) — структура данных, где каждый элемент содержит указатели на следующий и предыдущий элементы, и эти указатели хранятся непосредственно внутри самих элементов, а не в отдельной структуре узла списка.
Это позволяет избежать дополнительного уровня косвенности и улучшает производительность.
Реализация на C.
#include <stdio.h>
#include <stdlib.h>
// Определение структуры элемента списка
struct IntrusiveElement {
int data;
struct IntrusiveElement *next;
struct IntrusiveElement *prev;
};
// Функция для создания нового элемента
struct IntrusiveElement* create_element(int value) {
struct IntrusiveElement *new_element = (struct IntrusiveElement *)malloc(sizeof(struct IntrusiveElement));
if (new_element == NULL) {
printf("Ошибка выделения памяти\n");
exit(1);
}
new_element->data = value;
new_element->next = NULL;
new_element->prev = NULL;
return new_element;
}
// Функция для вставки элемента после заданного элемента
void insert_after(struct IntrusiveElement *element, int value) {
struct IntrusiveElement *new_element = create_element(value);
if (element->next != NULL) {
element->next->prev = new_element;
}
new_element->next = element->next;
new_element->prev = element;
element->next = new_element;
}
// Функция для удаления элемента
void remove_element(struct IntrusiveElement *element) {
if (element->prev != NULL) {
element->prev->next = element->next;
}
if (element->next != NULL) {
element->next->prev = element->prev;
}
free(element);
}
// Функция для печати содержимого списка
void print_list(struct IntrusiveElement *head) {
struct IntrusiveElement *current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
int main() {
struct IntrusiveElement *head = create_element(0); // Создание головы списка
// Вставка элементов в список
insert_after(head, 1);
insert_after(head->next, 2);
insert_after(head->next->next, 3);
// Печать списка
print_list(head);
// Удаление второго элемента
remove_element(head->next->next);
// Печать обновленного списка
print_list(head);
return 0;
}
.
Реализация на C++.
Эти реализации показывают базовую функциональность интрузивного списка: вставка элементов, удаление элементов и печать содержимого списка.
#include <iostream>
using namespace std;
// Определение класса элемента списка
class IntrusiveElement {
public:
int data;
IntrusiveElement *next;
IntrusiveElement *prev;
};
// Класс для управления списком
class IntrusiveList {
private:
IntrusiveElement *head;
public:
IntrusiveList() { head = nullptr; } // Конструктор инициализирует пустой список
// Метод для вставки элемента после заданного элемента
void insertAfter(IntrusiveElement *element, int value) {
IntrusiveElement *new_element = new IntrusiveElement();
new_element->data = value;
if (element->next != nullptr) {
element->next->prev = new_element;
}
new_element->next = element->next;
new_element->prev = element;
element->next = new_element;
}
// Метод для удаления элемента
void removeElement(IntrusiveElement *element) {
if (element->prev != nullptr) {
element->prev->next = element->next;
}
if (element->next != nullptr) {
element->next->prev = element->prev;
}
delete element;
}
// Метод для печати содержимого списка
void printList() {
IntrusiveElement *current = head;
while (current != nullptr) {
cout << current->data << " -> ";
current = current->next;
}
cout << "NULL" << endl;
}
};
int main() {
IntrusiveList list;
IntrusiveElement *head = new IntrusiveElement(); // Создание головы списка
head->data = 0;
list.head = head;
// Вставка элементов в список
list.insertAfter(list.head, 1);
list.insertAfter(list.head->next, 2);
list.insertAfter(list.head->next->next, 3);
// Печать списка
list.printList();
// Удаление второго элемента
list.removeElement(list.head->next->next);
// Печать обновленного списка
list.printList();
return 0;
}
Эти реализации показывают базовую функциональность интрузивного списка: вставка элементов, удаление элементов и печать содержимого списка.
#448_TLS_Vim
Как в Vim переместиться к определению функции?
Для перемещения к определению функции в редакторе Vim можно использовать несколько различных методов в зависимости от того, какие плагины установлены и какой язык программирования используется.
Основные способы:
1. Используя встроенные возможности поиска тегов — Vim поддерживает систему тегов (ctags), которая позволяет создавать индексы функций, классов и других элементов программы.
Чтобы воспользоваться этой системой, нужно сначала создать файл с индексами, используя ctags, а затем использовать команды Vim для навигации.
Для установки ctags (если еще не установлен):
Создадим файл тегов для вашего проекта:
Это создаст файл tags в текущей директории, который будет содержать индексы всех функций и классов в проекте.
Теперь в Vim можно перемещаться к определенному элементу следующим образом:
— если курсор находится над именем функции, нажмимаем <Ctrl-]>;
— используя команду: :tag <имя_функции>.
2. Использование плагинов — плагины расширяют функциональность Vim и делают работу с кодом удобнее.
Например, можно установить такие плагины, как:
Пример настройки с использованием Coc.nvim:
Установите Coc.nvim согласно инструкции на GitHub.
Настройте плагин для работы с нужным ЯП (Python, C++ и др.).
После установки и настройки, находясь внутри редактора, можно перемещаться к определениям функций с помощью команд:
<Ctrl+>] — перемещение к определению функции.
<Ctrl+t> — возврат назад после перехода.
3. Интеграция с IDE-функциональностью через Language Server Protocol (LSP) — современные редакторы и плагины используют протокол LSP для интеграции с языковой поддержкой.
Например, coc.nvim или nvim-lspconfig позволяют легко подключать серверы поддержки для разных языков.
Пример для Python:
Установите сервер поддержки для Python, например, Pyright или Jedi.
Подключите его к Vim с помощью плагина LSP (например, через coc.nvim).
Используйте команды перемещения, аналогичные тем, что описаны выше для Coc.nvim.
На практике наиболее удобным вариантом является использование плагинов вроде YouCompleteMe или Coc.nvim, поскольку они предоставляют мощный функционал для навигации по коду и автоматического завершения.
Как в Vim переместиться к определению функции?
Для перемещения к определению функции в редакторе Vim можно использовать несколько различных методов в зависимости от того, какие плагины установлены и какой язык программирования используется.
Основные способы:
1. Используя встроенные возможности поиска тегов — Vim поддерживает систему тегов (ctags), которая позволяет создавать индексы функций, классов и других элементов программы.
Чтобы воспользоваться этой системой, нужно сначала создать файл с индексами, используя ctags, а затем использовать команды Vim для навигации.
Для установки ctags (если еще не установлен):
sudo apt-get install exuberant-ctags # Для Ubuntu/Debian
Создадим файл тегов для вашего проекта:
ctags -R .
Это создаст файл tags в текущей директории, который будет содержать индексы всех функций и классов в проекте.
Теперь в Vim можно перемещаться к определенному элементу следующим образом:
— если курсор находится над именем функции, нажмимаем <Ctrl-]>;
— используя команду: :tag <имя_функции>.
2. Использование плагинов — плагины расширяют функциональность Vim и делают работу с кодом удобнее.
Например, можно установить такие плагины, как:
YouCompleteMe — плагин для автодополнения и навигации по коду.
Coc.nvim — аналогичен YouCompleteMe, но использует LSP (Language Server Protocol).
Tagbar — отображение структуры файла в виде боковой панели.
Пример настройки с использованием Coc.nvim:
Установите Coc.nvim согласно инструкции на GitHub.
Настройте плагин для работы с нужным ЯП (Python, C++ и др.).
После установки и настройки, находясь внутри редактора, можно перемещаться к определениям функций с помощью команд:
<Ctrl+>] — перемещение к определению функции.
<Ctrl+t> — возврат назад после перехода.
3. Интеграция с IDE-функциональностью через Language Server Protocol (LSP) — современные редакторы и плагины используют протокол LSP для интеграции с языковой поддержкой.
Например, coc.nvim или nvim-lspconfig позволяют легко подключать серверы поддержки для разных языков.
Пример для Python:
Установите сервер поддержки для Python, например, Pyright или Jedi.
Подключите его к Vim с помощью плагина LSP (например, через coc.nvim).
Используйте команды перемещения, аналогичные тем, что описаны выше для Coc.nvim.
На практике наиболее удобным вариантом является использование плагинов вроде YouCompleteMe или Coc.nvim, поскольку они предоставляют мощный функционал для навигации по коду и автоматического завершения.
#450_ALG_C_Cpp
Структура данных "ЛЕС".
ЛЕС — структура данных, представляющая собой набор деревьев (деревьев поиска).
В контексте компьютерных наук чаще всего используется термин «лес» в двух основных значениях:
Ориентированный ациклический граф (DAG) — структура данных, где каждый узел имеет одного родителя, кроме корневого узла, который родителей не имеет. Такой DAG называют лесом, потому что он состоит из нескольких деревьев, каждое из которых представляет собой ориентированное дерево (с одним корнем).
Лес в контексте файловых систем — также иногда употребляется этот термин для обозначения набора каталогов (директорий), где каждая директория может содержать другие директории или файлы.
Это похоже на древовидную структуру файлового хранилища, но здесь важно понимать, что файловые системы могут иметь циклы через символические ссылки, поэтому строго говоря, это не совсем лес в смысле графа.
Пример леса (DAG):
Представим себе набор элементов, где у каждого элемента есть один родитель, но сами элементы могут формировать несколько отдельных деревьев.
Например, такие деревья могут выглядеть следующим образом:
Здесь два дерева: одно с корнем A, другое с корнем B.
Этот набор деревьев образует лес. Каждый элемент может иметь максимум одного родителя, и между элементами не должно быть циклов.
Основные операции над лесом:
Добавление нового узла — можно добавить новый узел как корень (создать новое дерево) либо как потомок уже существующего узла.
Удаление узла — удаление узла возможно вместе с его потомками или только самого узла, передавая его детей его родителю.
Поиск пути от корня до листа — можно использовать алгоритмы обхода (например, глубину или ширину) для нахождения путей между узлами.
Обход леса — аналогично дереву, можно применять алгоритмы обхода, начиная с каждого корня отдельного дерева.
Применение лесов:
Графовые структуры данных — леса используются для представления иерархии объектов, когда объекты имеют одного родителя, но самих объектов много, и они образуют несколько независимых структур.
Файловые системы — в ОС леса часто применяются для описания каталога (корень дерева), содержащего другие подкаталоги и файлы.
Компьютерная графика — лес может использоваться для моделирования сложных сцен, состоящих из множества объектов, связанных друг с другом иерархически.
Связь с другими структурами данных:
Лес можно рассматривать как обобщенную версию бинарного дерева, где количество дочерних узлов у каждого узла не ограничено.
В случае, если в лесу существует только одно дерево, оно превращается в обычное дерево (или корневое дерево).
Таким образом, лес является удобной структурой для работы с наборами взаимосвязанных объектов, организованных в виде нескольких деревьев.
Структура данных "ЛЕС".
ЛЕС — структура данных, представляющая собой набор деревьев (деревьев поиска).
В контексте компьютерных наук чаще всего используется термин «лес» в двух основных значениях:
Ориентированный ациклический граф (DAG) — структура данных, где каждый узел имеет одного родителя, кроме корневого узла, который родителей не имеет. Такой DAG называют лесом, потому что он состоит из нескольких деревьев, каждое из которых представляет собой ориентированное дерево (с одним корнем).
Лес в контексте файловых систем — также иногда употребляется этот термин для обозначения набора каталогов (директорий), где каждая директория может содержать другие директории или файлы.
Это похоже на древовидную структуру файлового хранилища, но здесь важно понимать, что файловые системы могут иметь циклы через символические ссылки, поэтому строго говоря, это не совсем лес в смысле графа.
Пример леса (DAG):
Представим себе набор элементов, где у каждого элемента есть один родитель, но сами элементы могут формировать несколько отдельных деревьев.
Например, такие деревья могут выглядеть следующим образом:
A B
/ | \ \
C D E F
Здесь два дерева: одно с корнем A, другое с корнем B.
Этот набор деревьев образует лес. Каждый элемент может иметь максимум одного родителя, и между элементами не должно быть циклов.
Основные операции над лесом:
Добавление нового узла — можно добавить новый узел как корень (создать новое дерево) либо как потомок уже существующего узла.
Удаление узла — удаление узла возможно вместе с его потомками или только самого узла, передавая его детей его родителю.
Поиск пути от корня до листа — можно использовать алгоритмы обхода (например, глубину или ширину) для нахождения путей между узлами.
Обход леса — аналогично дереву, можно применять алгоритмы обхода, начиная с каждого корня отдельного дерева.
Применение лесов:
Графовые структуры данных — леса используются для представления иерархии объектов, когда объекты имеют одного родителя, но самих объектов много, и они образуют несколько независимых структур.
Файловые системы — в ОС леса часто применяются для описания каталога (корень дерева), содержащего другие подкаталоги и файлы.
Компьютерная графика — лес может использоваться для моделирования сложных сцен, состоящих из множества объектов, связанных друг с другом иерархически.
Связь с другими структурами данных:
Лес можно рассматривать как обобщенную версию бинарного дерева, где количество дочерних узлов у каждого узла не ограничено.
В случае, если в лесу существует только одно дерево, оно превращается в обычное дерево (или корневое дерево).
Таким образом, лес является удобной структурой для работы с наборами взаимосвязанных объектов, организованных в виде нескольких деревьев.
Примеры реализации леса на С и С++.
Реализация леса на ЯП С и С++ предполагает создание структуры данных, представляющей собой набор деревьев.
Реализация на С:
Реализация на С++:
.
Реализация леса на ЯП С и С++ предполагает создание структуры данных, представляющей собой набор деревьев.
Реализация на С:
#include <stdio.h>
#include <stdlib.h>
// Структура узла дерева
typedef struct Node {
int data;
struct Node *parent; // Указатель на родителя
struct Node *leftChild; // Левый ребенок
struct Node *rightSibling; // Правый брат/сестра
} Node;
// Функция для создания нового узла
Node* createNode(int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
if (newNode == NULL) {
printf("Ошибка выделения памяти\n");
exit(1);
}
newNode->data = value;
newNode->parent = NULL;
newNode->leftChild = NULL;
newNode->rightSibling = NULL;
return newNode;
}
// Функция для добавления ребенка к узлу
void addChild(Node* parent, Node* child) {
if (parent->leftChild == NULL) {
parent->leftChild = child;
} else {
Node* sibling = parent->leftChild;
while (sibling->rightSibling != NULL) {
sibling = sibling->rightSibling;
}
sibling->rightSibling = child;
}
child->parent = parent;
}
// Функция для печати леса
void printForest(Node* root) {
if (root == NULL) return;
Node* current = root;
while (current != NULL) {
printf("%d ", current->data);
if (current->leftChild != NULL) {
printForest(current->leftChild);
}
current = current->rightSibling;
}
}
int main() {
// Создание леса
Node* forestRootA = createNode(10); // Первое дерево
Node* forestRootB = createNode(20); // Второе дерево
Node* child1 = createNode(11);
Node* child2 = createNode(12);
Node* child3 = createNode(13);
addChild(forestRootA, child1);
addChild(forestRootA, child2);
addChild(forestRootA, child3);
Node* child21 = createNode(21);
Node* child22 = createNode(22);
addChild(forestRootB, child21);
addChild(forestRootB, child22);
// Печать леса
printForest(forestRootA);
printForest(forestRootB);
return 0;
}
Реализация на С++:
#include <iostream>
using namespace std;
// Класс узла дерева
class Node {
public:
int data;
Node* parent; // Указатель на родителя
Node* leftChild; // Левый ребенок
Node* rightSibling; // Правый брат/сестра
// Конструктор класса
Node(int value) : data(value), parent(nullptr), leftChild(nullptr), rightSibling(nullptr) {}
};
// Функция для добавления ребенка к узлу
void addChild(Node* parent, Node* child) {
if (parent->leftChild == nullptr) {
parent->leftChild = child;
} else {
Node* sibling = parent->leftChild;
while (sibling->rightSibling != nullptr) {
sibling = sibling->rightSibling;
}
sibling->rightSibling = child;
}
child->parent = parent;
}
// Функция для печати леса
void printForest(Node* root) {
if (root == nullptr) return;
Node* current = root;
while (current != nullptr) {
cout << current->data << " ";
if (current->leftChild != nullptr) {
printForest(current->leftChild);
}
current = current->rightSibling;
}
}
int main() {
// Создание леса
Node* forestRootA = new Node(10); // Первое дерево
Node* forestRootB = new Node(20); // Второе дерево
Node* child1 = new Node(11);
Node* child2 = new Node(12);
Node* child3 = new Node(13);
addChild(forestRootA, child1);
addChild(forestRootA, child2);
addChild(forestRootA, child3);
Node* child21 = new Node(21);
Node* child22 = new Node(22);
addChild(forestRootB, child21);
addChild(forestRootB, child22);
// Печать леса
printForest(forestRootA);
printForest(forestRootB);
return 0;
}
.
Структура узла (struct Node в С и class Node в С++):
Поле data хранит значение узла.
Поле parent указывает на родителя узла.
Поля leftChild и rightSibling указывают соответственно на левого ребенка и правого брата/сестру.
Функция createNode/конструктор Node — cоздает новый узел с заданным значением и инициализирует указатели на родителя, левого ребенка и правого брата/сестры в NULL.
Функция addChild — добавляет новый узел как ребенка к существующему узлу. Если у узла еще нет детей, новый узел становится левым ребенком. Иначе новый узел добавляется справа от последнего существующего ребенка.
Функция printForest — обходит лес и выводит значения всех узлов в порядке обхода в ширину.
Пример использования:
Создадим два дерева с корнями 10 и 20, добавим к ним детей и выведем результат.
Эти примеры демонстрируют базовую реализацию леса на С и С++, которую можно расширить для решения различных задач.
Поле data хранит значение узла.
Поле parent указывает на родителя узла.
Поля leftChild и rightSibling указывают соответственно на левого ребенка и правого брата/сестру.
Функция createNode/конструктор Node — cоздает новый узел с заданным значением и инициализирует указатели на родителя, левого ребенка и правого брата/сестры в NULL.
Функция addChild — добавляет новый узел как ребенка к существующему узлу. Если у узла еще нет детей, новый узел становится левым ребенком. Иначе новый узел добавляется справа от последнего существующего ребенка.
Функция printForest — обходит лес и выводит значения всех узлов в порядке обхода в ширину.
Пример использования:
Создадим два дерева с корнями 10 и 20, добавим к ним детей и выведем результат.
Эти примеры демонстрируют базовую реализацию леса на С и С++, которую можно расширить для решения различных задач.
#451_ALG_C_PRGM
Реализация "частотного словаря" (Frequency Dictionary) на С.
Программа производит подсчет того, сколько раз в тексте встречается каждое слово.
При подсчете игнорируются все символы, которые не является буквами английского алфавита. Так же не имеет значения какой регистр имеют буквы в слове (слова "Text" и "TEXT", подсчитываются как употребление слова "text" два раза).
Результатом работы программы является список содержащий слова и частоту их упоминания в тексте, которые в расположены в порядке убывания по частоте упоминания.
В программе используется структура данных BST (бинарное дерево поиска), а сортировка результата производится путем создания массива указателей на узлы дерева и сортировки этого массива в порядке убывания.
Программу можно расширить добавив возможность сортировки по возрастанию или в алфавитном порядке, так же легко получить количество уникальных слов. (функция для подсчета количества узлов BST уже реализована).
Реализация "частотного словаря" (Frequency Dictionary) на С.
Программа производит подсчет того, сколько раз в тексте встречается каждое слово.
При подсчете игнорируются все символы, которые не является буквами английского алфавита. Так же не имеет значения какой регистр имеют буквы в слове (слова "Text" и "TEXT", подсчитываются как употребление слова "text" два раза).
Результатом работы программы является список содержащий слова и частоту их упоминания в тексте, которые в расположены в порядке убывания по частоте упоминания.
В программе используется структура данных BST (бинарное дерево поиска), а сортировка результата производится путем создания массива указателей на узлы дерева и сортировки этого массива в порядке убывания.
Программу можно расширить добавив возможность сортировки по возрастанию или в алфавитном порядке, так же легко получить количество уникальных слов. (функция для подсчета количества узлов BST уже реализована).