Горячее
Лучшее
Свежее
Подписки
Сообщества
Блоги
Эксперты
Войти
Забыли пароль?
или продолжите с
Создать аккаунт
Я хочу получать рассылки с лучшими постами за неделю
или
Восстановление пароля
Восстановление пароля
Получить код в Telegram
Войти с Яндекс ID Войти через VK ID
Создавая аккаунт, я соглашаюсь с правилами Пикабу и даю согласие на обработку персональных данных.
ПромокодыРаботаКурсыРекламаИгрыПополнение Steam
Пикабу Игры +1000 бесплатных онлайн игр Собирайте фишки

Волшебный особняк

Казуальные, Три в ряд, Головоломки

Играть

Топ прошлой недели

  • AlexKud AlexKud 38 постов
  • Animalrescueed Animalrescueed 36 постов
  • Oskanov Oskanov 7 постов
Посмотреть весь топ

Лучшие посты недели

Рассылка Пикабу: отправляем самые рейтинговые материалы за 7 дней 🔥

Нажимая кнопку «Подписаться на рассылку», я соглашаюсь с Правилами Пикабу и даю согласие на обработку персональных данных.

Спасибо, что подписались!
Пожалуйста, проверьте почту 😊

Помощь Кодекс Пикабу Команда Пикабу Моб. приложение
Правила соцсети О рекомендациях О компании
Промокоды Биг Гик Промокоды Lamoda Промокоды МВидео Промокоды Яндекс Директ Промокоды Отелло Промокоды Aroma Butik Промокоды Яндекс Путешествия Постила Футбол сегодня
0 просмотренных постов скрыто
0
batray
batray
6 месяцев назад
RedditsMem

Grok против ChatGpt⁠⁠

Попросил ChatGpt накидать код игры тетрис чтобы можно было в браузере запустить. Час пытался добиться от него чтобы динамика была, фон красочный и т.п. Плюнул. Закинул сегодня его код в Grok с запросом "проверь код этой игры и дополни чтобы было красочно и динамично". Результат порадовал. OpenAi не поздоровится думаю.

Grok против ChatGpt Приложение, Программа, ChatGPT, Нейросеть Grok, Тестирование, Чат-бот, Сравнение, Игры, Гайд, Тренд, Windows, Картинка с текстом
Показать полностью 1
[моё] Приложение Программа ChatGPT Нейросеть Grok Тестирование Чат-бот Сравнение Игры Гайд Тренд Windows Картинка с текстом
7
2
0sadchi
0sadchi
6 месяцев назад
Лига Разработчиков Видеоигр

Как создать скрипт-beautifier в Ghidra на Python?⁠⁠

Как создать скрипт-beautifier в Ghidra на Python? Программирование, Python, Гайд, Windows, Реверс, Длиннопост

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

Если вы уже имели опыт работы с дизассемблером, то заметили, что читать его вывод не так легко, если целью является понять более высокие абстракции, заложенные в нём. Возможно, вы даже пытались декомпилировать его в псевдокод, но работать с переменными типа local_1-999 – то ещё удовольствие. Да, можно щёлкнуть на каждую из них и присвоить имя на основе логики. А что, если у вас 2000 строк и более?

Чтобы не натереть мозоль, давайте разберёмся, как написать скрипт, который сделает большую часть работы за нас.

Все манипуляции были проделаны на версии 11.1.2. Чтобы попасть в список доступных скриптов, откройте меню Window → Script Manager и там же создайте новый скрипт, нажав в правом верхнем углу кнопку Create New Script и выбрав язык Python.

Важное ограничение: Ghidra использует внутреннюю реализацию языка Python версии 2.7.

Пишем код.

Первым делом, нужно объявить кодировку, чтобы не получить кучу ошибок о наличии не-ASCI символов

# -*- coding: utf-8 -*-

Указываем, что исходный файл сохранён в кодировке UTF-8 (поддержка Unicode символов).

from ghidra.util.task import Task

Импортируем класс Task из модуля Ghidra, который позволяет создавать задачи для выполнения операций в фоне.

from ghidra.app.decompiler import DecompInterface

Импортируем интерфейс декомпилятора, позволяющий получать C-подобный код из бинарных данных.

from ghidra.util.task import ConsoleTaskMonitor

Импортируем класс ConsoleTaskMonitor для отслеживания прогресса выполнения задач с выводом в консоль.

from ghidra.program.model.symbol import SourceType, SymbolType

Импортируем типы источников и типов символов, используемые для обозначения происхождения имен (например, USER_DEFINED) и вида символа (функция, переменная, метка).

from ghidra.program.model.pcode import HighFunctionDBUtil

Импортируем утилиты для работы с высокоуровневым представлением функции (HighFunction) в базе данных Ghidra.

from ghidra.program.model.pcode.HighFunctionDBUtil import ReturnCommitOption

Импортируем опцию фиксации (commit) изменений в базе данных при обновлении параметров функции.

from java.awt import BorderLayout

Импортируем менеджер компоновки BorderLayout для организации компонентов в окне Java.

from javax.swing import JButton, JFrame, JTextArea, JScrollPane, JPanel

Импортируем стандартные Swing-компоненты: кнопку, окно, текстовую область, панель прокрутки и панель для построения GUI.

import re

Импортируем модуль регулярных выражений для поиска и обработки строк.

class RenameDialog(JFrame):

Объявляем класс RenameDialog, наследующийся от JFrame. Он представляет окно диалога для ввода новых имён.

def __init__(self, suggestions):

Конструктор класса, принимающий список предложенных имен (suggestions) для переименования.

JFrame.__init__(self, "Advanced Renamer")

Инициализируем базовый класс JFrame, задавая заголовок окна “Advanced Renamer”.

self.setSize(800, 600)

Устанавливаем размер окна – 800 пикселей по ширине и 600 по высоте.

self.setLayout(BorderLayout())

Задаём менеджер компоновки BorderLayout для организации компонентов внутри окна.

self.text_area = JTextArea()

Создаём текстовую область, в которой пользователь сможет редактировать имена.

self.text_area.setText("# Format: old=new\n" + "\n".join(suggestions))

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

scroll_pane = JScrollPane(self.text_area)

Создаём панель прокрутки, содержащую нашу текстовую область, чтобы можно было просматривать длинный текст.

self.add(scroll_pane, BorderLayout.CENTER)

Добавляем панель прокрутки в центр окна (согласно BorderLayout).

button_panel = JPanel()

Создаём панель для размещения кнопок в окне.

self.apply_btn = JButton("Apply", actionPerformed=lambda _: self.setVisible(False))

Создаём кнопку “Apply” с обработчиком события: при нажатии окно будет скрыто (setVisible(False)).

button_panel.add(self.apply_btn)

Добавляем кнопку “Apply” на панель кнопок.

self.add(button_panel, BorderLayout.SOUTH)

Размещаем панель кнопок в нижней части окна.

class AdvancedRenamer(Task):

Объявляем класс AdvancedRenamer, наследующийся от Task. Он отвечает за логику переименования символов в Ghidra.

def __init__(self, program, function):

Конструктор класса принимает объект программы (program) и функцию (function), над которой будет производиться переименование.

super(AdvancedRenamer, self).__init__("Advanced Renamer", True, False, True)

Вызываем конструктор базового класса Task, задавая имя задачи и некоторые флаги (например, показывать прогресс, отменяемость и т.д.).

self.program = program

Сохраняем ссылку на текущую программу Ghidra.

self.function = function

Сохраняем ссылку на функцию, которую собираемся анализировать и переименовывать.

self.monitor = ConsoleTaskMonitor()

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

self.skipped = {'int', 'char', 'void', 'return', 'break', 'float'}

Определяем множество ключевых слов, которые не будут изменяться при переименовании (например, базовые типы и управляющие конструкции).

def find_and_rename(self, old_name, new_name):

Определяем метод для поиска символа с именем old_name и его переименования в new_name.

decompiler = DecompInterface()

Создаём экземпляр интерфейса декомпилятора.

decompiler.openProgram(self.program)

Открываем текущую программу в декомпиляторе для дальнейшей работы.

results = decompiler.decompileFunction(self.function, 60, self.monitor)

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

if results.decompileCompleted():

Проверяем, успешно ли завершилась декомпиляция.

hfunction = results.getHighFunction()

Получаем высокоуровневое представление функции (HighFunction) из результатов декомпиляции.

signatureSrcType = self.function.getSignatureSource()

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

HighFunctionDBUtil.commitParamsToDatabase(hfunction, True, ReturnCommitOption.COMMIT, signatureSrcType)

Фиксируем изменения параметров функции в базе данных, используя указанные опции. Это необходимо для того, чтобы локальные имена, такие как lVar1, uVar2, pVar3, были согласованы с базой данных, потому, что они генерируются самим декомпилятором и просто выводятся на экран, без коммита в базу.

if old_name.startswith("FUN_"):

Если имя символа начинается с “FUN_”, считаем его именем функции.

return self.rename_function(old_name, new_name)

Вызываем метод для переименования функции.

elif old_name.startswith(("DAT_", "PTR_", "UNK_", "LAB_")):

Если имя начинается с “DAT_”, “PTR_”, “UNK_” или “LAB_”, обрабатываем его как метку или данные. Покаяние: У меня так и не получилось дать ума PTR_ и UNK_, хоть это и глобальные имена, как DAT_ и LAB_, они не переименовываются данной функцией, но я оставил их как есть :з

return self.rename_label(old_name, new_name)

Вызываем метод для переименования метки.

elif old_name.startswith(("local_", "param_", "uVar", "lVar")):

Если имя соответствует шаблону локальной переменной или параметра, то…

return self.rename_local_variable(old_name, new_name)

… вызываем метод для переименования локальной переменной.

else:

Если ни одно из условий не выполнено, по умолчанию обрабатываем как локальную переменную.

return self.rename_local_variable(old_name, new_name)

Вызываем метод для переименования локальной переменной.

def rename_function(self, old_name, new_name):

Определяем метод, который переименовывает функцию с именем old_name в new_name.

try:

Начинаем блок обработки исключений, чтобы избежать сбоев при ошибках.

addr_str = old_name[4:] if old_name.startswith("FUN_") else old_name

Если имя начинается с “FUN_”, удаляем этот префикс, чтобы получить строку адреса функции.

addr = toAddr("0x{}".format(addr_str))

Преобразуем строку с адресом в объект адреса Ghidra, добавляя префикс “0x”.

func = getFunctionAt(addr)

Получаем объект функции, расположенной по данному адресу.

if func and func.getName() == old_name:

Если функция найдена и её имя соответствует old_name, то…

func.setName(new_name, SourceType.USER_DEFINED)

… устанавливаем новое имя для функции, указывая, что оно задано пользователем (USER_DEFINED).

except: pass

Если возникает ошибка (например, неверный адрес), просто игнорируем её.

def rename_label(self, old_name, new_name):

Определяем метод для переименования метки или символа, представляющего данные.

try:

Начинаем блок обработки исключений.

addr_str = old_name[4:] if old_name.startswith(("LAB_", "DAT_", "PTR_", "UNK_")) else old_name

Извлекаем адрес метки, убирая префикс (например, “LAB_”) если он присутствует.

addr = toAddr(addr_str)

Преобразуем строку адреса в объект адреса Ghidra.

for sym in self.program.getSymbolTable().getSymbols(addr):

Перебираем все символы, зарегистрированные по этому адресу, из таблицы символов программы.

if sym.getName() == old_name:

Если имя символа совпадает с old_name, то…

sym.setName(new_name, SourceType.USER_DEFINED)

… устанавливаем новое имя для символа с типом источника USER_DEFINED.

return True

Возвращаем True, указывая, что переименование прошло успешно.

except: pass

При возникновении исключения игнорируем его.

def rename_local_variable(self, old_name, new_name):

Определяем метод для переименования локальной переменной или параметра.

try:

Начинаем блок обработки исключений.

if new_name.lower() in self.skipped:

Если новое имя (в нижнем регистре) содержится в списке ключевых слов для пропуска, то…

return False

… прекращаем переименование, возвращая False.

# Далее идет блок, необходимый для обработки параметров

for param in self.function.getParameters():

Перебираем все параметры текущей функции.

if param.getName() == old_name:

Если имя параметра совпадает с old_name, то…

param.setName(new_name, SourceType.USER_DEFINED)

… устанавливаем новое имя для параметра.

# Local variables (даже если наш пациент был найдет среди параметров

# необходимо пройтись и по локальным переменным,

# иначе они не переименовываются, возможно надо тут просто

# сделать коммит в базу)

decompiler = DecompInterface()

Создаём новый экземпляр декомпилятора для обработки локальных переменных.

decompiler.openProgram(self.program)

Открываем программу в декомпиляторе.

results = decompiler.decompileFunction(self.function, 60, self.monitor)

Декомпилируем функцию с таймаутом 60 секунд.

if results.decompileCompleted():

Если декомпиляция прошла успешно, то…

hfunction = results.getHighFunction()

… получаем высокоуровневое представление функции.

syms = hfunction.getLocalSymbolMap().getSymbols()

Получаем список локальных символов (переменных) из высокоуровневой функции.

for sym in syms :

Перебираем каждый локальный символ.

if sym.getName() == old_name and sym.getName() != new_name:

Если имя символа совпадает с old_name и ещё не равно new_name, то…

HighFunctionDBUtil.updateDBVariable(sym, new_name, None, SourceType.USER_DEFINED)

… обновляем имя переменной в базе данных с новым именем и источником USER_DEFINED.

return True

Возвращаем True после успешного переименования локальной переменной.

return False

Если ни один из блоков не сработал, возвращаем False – переименование не выполнено.

except Exception as e:

Ловим исключения и сохраняем их в переменной e для отладки.

print("Error: {} -> {} ({})".format(old_name, new_name, str(e)))

Выводим сообщение об ошибке с указанием старого и нового имени, а также текста ошибки. Обратите внимание на форматирование строки, т.к. это Python 2.7.

return False

Возвращаем False, сигнализируя, что произошла ошибка при переименовании.

def run(self):

Определяем метод run, который является точкой входа при выполнении задачи AdvancedRenamer.

decompiler = DecompInterface()

Создаем экземпляр декомпилятора для работы с функцией.

decompiler.openProgram(self.program)

Открываем программу в декомпиляторе.

results = decompiler.decompileFunction(self.function, 60, self.monitor)

Декомпилируем функцию с таймаутом 60 секунд.

if not results.decompileCompleted():

Если декомпиляция не завершилась успешно, то…

print("Decompilation failed!")

… выводим сообщение об ошибке декомпиляции.

return

Прерываем выполнение метода run (задача не может продолжаться без декомпиляции).

code = results.getDecompiledFunction().getC()

Получаем декомпилированный C-подобный код функции в виде строки.

entities = re.findall(r'\b([A-Za-z_][A-Za-z0-9_]*)\b', code)

С помощью регулярного выражения находим все идентификаторы (слова, начинающиеся с буквы или подчёркивания) в коде.

filtered_names = [n for n in set(entities) if n not in self.skipped]

Создаём множество уникальных идентификаторов и исключаем те, что присутствуют в self.skipped (ключевые слова).

dialog = RenameDialog(filtered_names)

Создаем диалоговое окно RenameDialog, передавая список найденных имён для потенциального переименования.

dialog.setLocationRelativeTo(None)

Устанавливаем расположение диалога по центру экрана (None означает центр относительно родительского окна).

dialog.setVisible(True)

Делаем диалог видимым – ожидаем, пока пользователь внесёт изменения и нажмёт “Apply”.

while dialog.isVisible(): pass

Активно ждём, пока окно не будет закрыто (пользователь не закончит ввод).

success = 0

Инициализируем счётчик успешно переименованных символов.

for line in dialog.text_area.getText().split('\n'):

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

line = line.strip()

Удаляем пробелы в начале и конце строки.

if line and '=' in line and not line.startswith('#'):

Если строка не пуста, содержит символ “=” и не является комментарием (не начинается с “#”), то…

old, new = line.split('=', 1)

Разбиваем строку на две части по первому символу “=”, где левая часть – старое имя, правая – новое.

if self.find_and_rename(old.strip(), new.strip()):

Вызываем метод find_and_rename с очищенными от пробелов старыми и новыми именами; если переименование прошло успешно, то…

success += 1

… увеличиваем счётчик успешных переименований.

print("Successfully renamed: {}/{}".format(success, len(filtered_names)))

Выводим итоговое сообщение о том, сколько из найденных идентификаторов было успешно переименовано.

if __name__ == "__main__":

Стандартная проверка: выполняется ли скрипт как главный модуль.

func = getFunctionContaining(currentAddress)

Получаем функцию, в которой находится текущий адрес курсора в Ghidra.

if func:

Если функция найдена, то…

task = AdvancedRenamer(currentProgram, func)

Создаем экземпляр задачи AdvancedRenamer, передавая текущую программу и найденную функцию.

task.run()

Запускаем выполнение задачи переименования.

else:

Если функция не найдена (курсор не находится внутри функции), то…

print("Position cursor inside function!")

… выводим сообщение с просьбой установить курсор внутри функции для корректной работы скрипта.

Подготовка.

Когда все готово, можем пойти в любую нейросеть. По-моему мнению самая лучшая на сегодняшний день для данной задачи будет DeepSeek-r1. Выверенный опытным путем промпт.

Прокомментируй каждую строку и переименнуй переменные и функции нормально. Саму структуру кода оставь, как есть, не сокращая и не меняя, то есть никаких "и так далее" - пиши код полностью! Все имена должны быть подробными, то есть не tmp и прочее, никаких сокращенных бысмысленных имен. Опиши к чему пренадлежат переменные, не просто windowStruct, а UIState как напимер. Так же переименую DAT_ как g_..новоеИмя, и LABEL_куда прыгаем {НАШ_ПСЕВДО_КОД} после кода выведи все, что переименовал в формате списка старое_имя=новое_имя\n в список включай все переменные, функции, DAT_, LAB_, param_ и прочее без исключений прям все пиши

Выбираем цель, для эксперимента

Как создать скрипт-beautifier в Ghidra на Python? Программирование, Python, Гайд, Windows, Реверс, Длиннопост

В итоге, у меня получился вот такой вот список для моей функции

param_1=hWndPointer

bVar1=isCursorConfinedDueToCapture

bVar2=isMarginApplied

cVar3=isCursorCapturedResult

local_res8=clientTopLeftPoint

local_res10=clientBottomRightPoint

local_38=confinedCursorRect

local_28=windowRect

Запускаем!!

После запуска скрипта (при условии, что курсор установлен внутри функции) откроется диалоговое окно, в котором будут выведены найденные идентификаторы. Введите список переименований в формате:

ВАЖНО!!! Переменные по типу xVarN нужно сортировать по убыванию, так как они не внесены в базу. Переименование меньшей переменной приведёт к тому, что большая займёт её место, и список станет невалидным.

После ввода нажмите кнопку Apply и дождитесь окончания выполнения.

Как создать скрипт-beautifier в Ghidra на Python? Программирование, Python, Гайд, Windows, Реверс, Длиннопост

(метод подсчета требует доработки)

Как создать скрипт-beautifier в Ghidra на Python? Программирование, Python, Гайд, Windows, Реверс, Длиннопост

В итоге должно получиться нечто подобное:

Как создать скрипт-beautifier в Ghidra на Python? Программирование, Python, Гайд, Windows, Реверс, Длиннопост

Спасибо за внимание! Скачать PDF и сам скрипт можно по ссылке – https://t.me/osiechan/62 А начать путь в реверс-инжиниринге можно на увлекательном бесплатном курсе по ботостроению для ММОРПГ – https://t.me/osiechan/41

Показать полностью 4
[моё] Программирование Python Гайд Windows Реверс Длиннопост
1
2
mcnikirikitiki
6 месяцев назад
Лига программистов

Основы программирования на C++: Жизненный цикл объекта⁠⁠

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

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

Здравствуйте, мои маленькие любители программирования!

Жизненный цикл объекта в C++ управляется конструкторами, деструкторами и операторами присваивания. Рассмотрим ключевые аспекты на примере класса Logger, который логирует вызовы своих специальных функций.

Основные этапы жизненного цикла

  1. Создание объекта:

    • Конструкторы: Вызываются при создании объекта. Могут быть параметризованными, копирующими или перемещающими.

    • Автоматические объекты (на стеке): Уничтожаются автоматически при выходе из области видимости.

    • Динамические объекты (в куче): Создаются через new, уничтожаются вручную через delete.

    Пример:

    Logger x1; // Вызов конструктора без аргументов

    Logger* x2 = new Logger(1); // Динамический объект

    delete x2; // Вызов деструктора

  2. Копирование и присваивание:

    • Конструктор копирования: Создает новый объект как копию существующего.

    • Оператор присваивания: Модифицирует существующий объект.

    • По умолчанию компилятор генерирует тривиальные версии этих функций.

    Пример:

    Logger x3 = x1; // Конструктор копирования

    x3 = x1; // Оператор присваивания

  3. Перемещение:

    • Конструктор перемещения и оператор присваивания перемещением позволяют эффективно передавать ресурсы временных объектов.

    • Используются с std::move для явного указания перемещения.

    Пример:

    Logger x4 = std::move(x1); // Конструктор перемещения

    x4 = Logger(); // Оператор присваивания перемещением

  4. Деструктор: Вызывается при уничтожении объекта. Для автоматических объектов — при выходе из области видимости, для динамических — при вызове delete.

Особенности работы с классами

  • Статические поля: Общие для всех объектов класса. Используются для подсчета созданных экземпляров или хранения общих данных.

    class Logger {

    inline static int counter = 0; // Статическое поле

    const int id;

    public:

    Logger() : id(++counter) {} // Инициализация id };

  • Композиция классов: При создании объекта сначала инициализируются его поля, затем тело конструктора. Деструкторы вызываются в обратном порядке.

    class OuterLogger {

    Logger inner1, inner2; // Поля инициализируются до тела конструктора

    public: OuterLogger() { /* ... */ }

    ~OuterLogger() { /* ... */ } // Деструкторы inner2, inner1 вызываются после };

Временные объекты и контейнеры

  • Временные объекты существуют до конца выражения. Могут быть переданы в функции через rvalue-ссылки (&&), что позволяет избежать копирования.

    void f(Logger&& x); // Перегрузка для временных объектов

    f(Logger()); // Вызов версии с перемещением

  • Контейнеры (например, std::vector, std::list) управляют памятью автоматически. При реаллокации элементы могут копироваться или перемещаться.

    std::vector<Logger> vec;

    vec.emplace_back(); // Создание объекта напрямую в контейнере

    vec.push_back(Logger()); // Создание временного объекта и его перемещение

Понимание жизненного цикла объектов помогает управлять ресурсами, избегать утечек и оптимизировать производительность.

1. Реализация класса Logger

Создайте класс Logger со следующими требованиями:

  • Имеет static int counter для подсчета созданных объектов.

  • Содержит const int id, инициализируемую в конструкторе через ++counter.

  • Реализуйте:

    • Конструктор по умолчанию (логирует "Constructor called, id=X").

    • Конструктор копирования (логирует "Copy constructor, id=X → Y").

    • Конструктор перемещения (логирует "Move constructor, id=X → Y").

    • Деструктор (логирует "Destructor, id=X").

    • Операторы присваивания (копирования и перемещения) с аналогичным логированием.

Цель: Научиться реализовывать все специальные функции класса.


2. Проверка порядка деструкторов

Создайте класс Composite, содержащий два поля типа Logger:

class Composite { Logger logger1; Logger logger2; public: Composite() { /* ... */ } };

  • В конструкторе Composite добавьте вывод "Composite constructor".

  • В деструкторе — "Composite destructor".

  • Создайте объект Composite в области видимости функции и определите порядок вызова деструкторов.

Цель: Понять порядок инициализации и уничтожения полей класса.


3. Временные объекты и перемещение

Напишите функцию:

void processLogger(Logger&& tempLogger) { std::cout << "Processing temporary logger" << std::endl; }

  • Создайте временный объект Logger() и передайте его в processLogger.

  • Объясните, почему не вызывается конструктор копирования.

Цель: Закрепить работу с rvalue-ссылками и перемещением.


4. Контейнеры и реаллокация

  • Создайте std::vector<Logger>.

  • Добавьте в него 3 элемента через push_back и emplace_back.

  • Запустите программу и объясните:

    • Почему при push_back(Logger()) вызывается перемещающий конструктор.

    • Как emplace_back позволяет избежать лишних вызовов конструкторов.

    • Что происходит при реаллокации вектора?

Цель: Изучить работу с контейнерами и оптимизацию через перемещение.


5. Динамические объекты и умные указатели

  • Создайте динамический объект Logger* dynamicLogger = new Logger();.

  • Удалите его через delete.

  • Перепишите код с использованием std::unique_ptr<Logger>.

  • Объясните, как умные указатели предотвращают утечки памяти.

Цель: Научиться управлять динамической памятью.


6. Запрет копирования

Модифицируйте класс Logger:

  • Удалите конструктор копирования и оператор присваивания.

  • Проверьте, что код Logger a; Logger b = a; вызывает ошибку компиляции.

Цель: Понять, как ограничивать жизненный цикл объектов.


7. Наследование и порядок вызовов

Создайте класс DerivedLogger : public Logger:

  • Добавьте поле Logger memberLogger.

  • В конструкторе и деструкторе DerivedLogger добавьте логирование.

  • Создайте объект DerivedLogger и зафиксируйте порядок вызовов конструкторов и деструкторов.

Цель: Изучить жизненный цикл объектов при наследовании.


8. Анализ кода

Дан код:

Logger createLogger() { return Logger(); } int main() { Logger a; Logger b = a; Logger c = std::move(createLogger()); std::vector<Logger> vec; vec.push_back(Logger()); return 0; }

  • Предскажите, сколько раз вызываются конструкторы (копирования, перемещения) и деструкторы.

  • Проверьте свой ответ, добавив логирование в класс Logger.

Цель: Научиться анализировать жизненный цикл объектов в реальном коде.

Показать полностью
[моё] Гайд Программирование IT C++ Windows Длиннопост
1
3
mcnikirikitiki
6 месяцев назад
Лига программистов

Основы программирования на C++: функции, шаблоны⁠⁠

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

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

Здравствуйте, мои маленькие любители программирования!

Функции — это фундаментальный элемент программирования, который позволяет структурировать код, избегать повторений и упрощать его понимание. Они позволяют выделить часто используемый код в отдельные блоки, которые можно многократно использовать с разными входными данными.

Основные концепции функций

  1. Что такое функция? Функция — это блок кода, который выполняет определенную задачу. Она может принимать входные данные (аргументы), обрабатывать их и возвращать результат. В языке C++ каждая программа начинается с выполнения функции main(), которая является точкой входа в программу.

    int main() {

    // Код программы

    return 0;

    }

    • int перед main() указывает, что функция возвращает целое число.

    • return 0; означает успешное завершение программы.

  2. Создание простой функции

    Рассмотрим пример функции, которая складывает два числа:

    int Sum(int a, int b) {

    return a + b;

    }

    • int перед именем функции указывает, что функция возвращает значение типа int.

    • Sum — это имя функции.

    • (int a, int b) — это параметры функции, то есть значения, которые передаются в функцию при её вызове.

    Пример использования функции Sum:

    int main() {

    int x = 17, y = 42;

    int z = Sum(x, y); // Вызов функции Sum

    std::cout << "Сумма: " << z << "\n"; // Выведет 59

    }

  3. Функции без возвращаемого значения

    Если функция не должна возвращать результат, её можно объявить как void. Это означает, что функция просто выполняет какие-то действия, но не возвращает никаких данных.

    void DoSomething(double d, char c) {

    std::cout << "Число: " << d << ", Символ: " << c << "\n";

    }

    Пример использования:

    int main() {

    DoSomething(3.14, '@'); // Выведет: Число: 3.14, Символ: @

    }

  4. Рекурсивные функции

    Рекурсия — это когда функция вызывает саму себя. Это полезно для решения задач, которые могут быть разделены на подзадачи того же типа. Например, вычисление факториала числа:

    #include <cstdint> // Для uint64_t

    std::uint64_t Factorial(std::uint64_t n) {

    if (n == 0) {

    return 1; // Базовый случай

    }

    return n * Factorial(n - 1); // Рекурсивный вызов

    }

    Пример использования:

    int main() {

    std::cout << "Факториал 5: " << Factorial(5) << "\n"; // Выведет 120

    }

    Важно: При использовании рекурсии нужно быть осторожным, чтобы не создать бесконечный цикл вызовов, что может привести к переполнению стека.


Аргументы функций

  1. Передача аргументов по значению

    По умолчанию аргументы передаются "по значению", то есть функция работает с копиями переменных. Любые изменения внутри функции не влияют на исходные переменные.

    void f(int x, int y) {

    x = 10; // Изменение x не повлияет на внешнюю переменную

    y = 20; // Изменение y не повлияет на внешнюю переменную

    }

    int main() {

    int a = 1, b = 2;

    f(a, b);

    std::cout << "a: " << a << ", b: " << b << "\n"; // Выведет: a: 1, b: 2

    }

  2. Передача аргументов по ссылке

    Чтобы изменять исходные переменные внутри функции, используются ссылки (&). Это позволяет функции работать с оригинальными переменными, а не с их копиями.

    void Swap(int& x, int& y) { // Передача по ссылке

    int temp = x;

    x = y;

    y = temp;

    }

    int main() {

    int a = 1, b = 2;

    Swap(a, b);

    std::cout << "a: " << a << ", b: " << b << "\n"; // Выведет: a: 2, b: 1

    }

  3. Константные ссылки

    Для сложных типов данных (например, векторов или строк) лучше передавать аргументы по константной ссылке (const &). Это позволяет избежать лишнего копирования данных и защищает их от случайного изменения внутри функции.

    void PrintVector(const std::vector<int>& vec) {

    for (int num : vec) {

    std::cout << num << " ";

    }

    std::cout << "\n";

    }

    int main() {

    std::vector<int> numbers = {1, 2, 3, 4, 5};

    PrintVector(numbers); // Выведет: 1 2 3 4 5

    }


Возвращаемые значения функций

  1. Возврат простых типов

    Функция может возвращать любые типы данных, такие как int, double, char и т.д. Например:

    double Divide(double a, double b) {

    return a / b;

    }

    int main() {

    double result = Divide(10.0, 2.0);

    std::cout << "Результат деления: " << result << "\n"; // Выведет: 5.0

    }

  2. Возврат сложных типов

    Возврат сложных типов данных (например, векторов или строк) также возможен. Компилятор автоматически оптимизирует процесс копирования таких объектов.

    std::string Concatenate(const std::vector<std::string>& parts) {

    std::string result;

    for (const auto& part : parts) {

    result += part;

    }

    return result;

    }

    int main() {

    std::vector<std::string> words = {"Hello", " ", "World", "!"};

    std::cout << Concatenate(words) << "\n"; // Выведет: Hello World!

    }

  3. Опасность возврата ссылок на локальные переменные

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

    int& GetLocalVariable() {

    int x = 10;

    return x; // Ошибка! x будет уничтожен после завершения функции

    }


Лямбда-функции

Лямбда-функции — это анонимные функции, которые можно определять непосредственно в месте использования. Они особенно полезны для коротких операций, например, для сортировки.

#include <algorithm>

#include <vector>

int main() {

std::vector<int> numbers = {5, 2, 8, 1, 9};

// Сортировка в обратном порядке с помощью лямбда-функции

std::sort(numbers.begin(), numbers.end(), [](int a, int b) {

return a > b; // Сравнение в обратном порядке

});

for (int num : numbers) {

std::cout << num << " "; // Выведет: 9 8 5 2 1

}

}


Шаблоны

Шаблоны позволяют создавать универсальный код, работающий с различными типами данных.

  1. Шаблонные функции

    Шаблонная функция позволяет работать с разными типами данных без необходимости писать отдельные версии для каждого типа.

    template<typename T>

    T Max(T a, T b) {

    return (a > b) ? a : b;

    }

    int main() {

    std::cout << "Максимум: " << Max(10, 20) << "\n"; // Выведет: 20

    std::cout << "Максимум: " << Max(3.14, 2.71) << "\n"; // Выведет: 3.14

    std::cout << "Максимум: " << Max("apple", "banana") << "\n"; // Выведет: banana

    }

  2. Шаблонные структуры

    Шаблоны также можно использовать для создания универсальных структур данных. Например, структура Triple, которая хранит три значения разных типов:

    template<typename T1, typename T2, typename T3>

    struct Triple {

    T1 first;

    T2 second;

    T3 third;

    };

    int main() {

    Triple<int, double, std::string> data = {1, 3.14, "Hello"};

    std::cout << "First: " << data.first << ", Second: " << data.second

    << ", Third: " << data.third << "\n";

    }


Это основные концепции работы с функциями и шаблонами в C++. Теперь вы знаете, как создавать и использовать функции, передавать аргументы, возвращать значения и работать с шаблонами.

Задачи на функции

1. Простые функции

  1. Функция "Приветствие"
    Напишите функцию void Greet(const std::string& name), которая выводит приветствие в формате:
    "Привет, [имя]!".
    Пример:

    Greet("Анна"); // Вывод: Привет, Анна!

  2. Вычисление площади прямоугольника
    Напишите функцию double CalculateRectangleArea(double width, double height), которая принимает ширину и высоту прямоугольника и возвращает его площадь.
    Пример:

    double area = CalculateRectangleArea(5.0, 3.0); // Результат: 15.0

  3. Проверка числа на четность
    Напишите функцию bool IsEven(int number), которая возвращает true, если число четное, и false — если нечетное.
    Пример:

    bool result = IsEven(4); // Результат: true


2. Функции с передачей по ссылке

  1. Увеличение значения на 1
    Напишите функцию void Increment(int& value), которая увеличивает переданное значение на 1.
    Пример:

    int x = 5;

    Increment(x);

    std::cout << x; // Вывод: 6

  2. Обмен значений двух переменных
    Реализуйте функцию void Swap(int& a, int& b), которая меняет значения двух переменных местами.
    Пример:

    int x = 10, y = 20;

    Swap(x, y);

    std::cout << x << " " << y; // Вывод: 20 10


3. Рекурсивные функции

  1. Числа Фибоначчи
    Напишите рекурсивную функцию int Fibonacci(int n), которая возвращает n-е число Фибоначчи.
    Пример:

    int result = Fibonacci(7); // Результат: 13

  2. Степень числа
    Напишите рекурсивную функцию int Power(int base, int exponent), которая возводит число base в степень exponent.
    Пример:

    int result = Power(2, 3); // Результат: 8


Задачи на шаблоны

4. Шаблонные функции

  1. Минимум двух чисел
    Напишите шаблонную функцию T Min(T a, T b), которая возвращает минимальное из двух значений.
    Пример:

    int result1 = Min(10, 20); // Результат: 10

    double result2 = Min(3.14, 2.71); // Результат: 2.71

  2. Сравнение строк
    Напишите шаблонную функцию bool AreEqual(T a, T b), которая возвращает true, если два значения равны, и false — если нет.
    Пример:

    bool result1 = AreEqual(5, 5); // Результат: true

    bool result2 = AreEqual("hello", "world"); // Результат: false

  3. Сумма элементов массива
    Напишите шаблонную функцию T SumArray(const std::vector<T>& arr), которая вычисляет сумму всех элементов вектора.
    Пример:

    std::vector<int> numbers = {1, 2, 3, 4};

    int result = SumArray(numbers); // Результат: 10


5. Шаблонные структуры

  1. Шаблонная структура "Точка"
    Создайте шаблонную структуру Point<T>, которая хранит координаты точки в двумерном пространстве. Добавьте метод void Print(), который выводит координаты точки.
    Пример:

    Point<int> p1 = {3, 4};

    p1.Print(); // Вывод: (3, 4)

  2. Шаблонная структура "Пара"
    Создайте шаблонную структуру Pair<T1, T2>, которая хранит два значения разных типов. Добавьте метод void Print(), который выводит значения пары.
    Пример:

    Pair<int, std::string> pair = {42, "Ответ"};

    pair.Print(); // Вывод: (42, Ответ)


Задачи на лямбда-функции

6. Лямбда-выражения

  1. Фильтрация чисел
    Используя лямбда-функцию, напишите программу, которая фильтрует числа из вектора, оставляя только четные.
    Пример:

    std::vector<int> numbers = {1, 2, 3, 4, 5, 6};

    std::vector<int> evenNumbers;

    std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(evenNumbers), [](int x) {

    return x % 2 == 0;

    });

    // Результат: evenNumbers = {2, 4, 6}

  2. Сортировка строк по длине
    Используя лямбда-функцию, отсортируйте вектор строк по их длине.
    Пример:

    std::vector<std::string> words = {"apple", "banana", "kiwi"};

    std::sort(words.begin(), words.end(), [](const std::string& a, const std::string& b) {

    return a.size() < b.size();

    });

    // Результат: words = {"kiwi", "apple", "banana"}


Дополнительные задачи

7. Комбинированные задачи

  1. Калькулятор
    Напишите шаблонную функцию T Calculator(T a, T b, char operation), которая выполняет одну из операций (+, -, *, /) над двумя числами.
    Пример:

    int result = Calculator(10, 5, '+'); // Результат: 15

    double result2 = Calculator(10.0, 2.0, '/'); // Результат: 5.0

  2. Поиск максимального элемента в массиве
    Напишите шаблонную функцию T FindMax(const std::vector<T>& arr), которая возвращает максимальный элемент вектора.
    Пример:

    std::vector<int> numbers = {3, 1, 4, 1, 5, 9};

    int max = FindMax(numbers); // Результат: 9

  3. Подсчет слов в строке
    Напишите функцию int CountWords(const std::string& text), которая подсчитывает количество слов в строке. Слова разделены пробелами.
    Пример:

    int count = CountWords("Hello world!"); // Результат: 2


Эти задачи помогут вам закрепить основные концепции работы с функциями, шаблонами и лямбда-выражениями в C++. Вы можете начать с простых задач и постепенно переходить к более сложным. Удачи!

Показать полностью
[моё] Программирование Гайд IT C++ Windows Длиннопост
48
3
mcnikirikitiki
6 месяцев назад
Лига программистов

Сборка проектов на C/C++: от базовых принципов к продвинутым решениям. Часть II - Инструменты автоматизации сборки⁠⁠

1. Почему важно правильно настраивать сборку?

Правильная настройка сборки — это не просто удобство для разработчиков, но и ключ к успешному развитию проекта. Вот несколько причин:

  • Масштабируемость: Чем больше проект, тем сложнее его поддерживать вручную. Автоматизация сборки позволяет легко добавлять новые файлы и зависимости.

  • Переносимость: Проекты часто запускаются на разных платформах (Linux, Windows, macOS). Хорошая система сборки гарантирует, что код будет работать везде.

  • Совместная работа: Когда в команде несколько разработчиков, автоматизированная сборка помогает избежать проблем с конфигурацией окружения.

  • CI/CD: Современные системы непрерывной интеграции и доставки (CI/CD) требуют четко настроенной сборки. Это ускоряет тестирование и деплой.


2. Универсальные Makefile'ы

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

# Имя исполняемого файла

TARGET = program

# Компилятор и флаги

CXX = g++

CXXFLAGS = -Wall -std=c++17

LDFLAGS =

# Список исходных файлов

SRCS = $(wildcard *.cpp)

OBJS = $(SRCS:.cpp=.o)

# Основная цель

all: $(TARGET)

# Как собирать программу

$(TARGET): $(OBJS)

$(CXX) $(OBJS) $(LDFLAGS) -o $(TARGET)

# Правило для объектных файлов

%.o: %.cpp

$(CXX) $(CXXFLAGS) -c $< -o $@

# Очистка

clean:

rm -f $(OBJS) $(TARGET)

Здесь используется функция wildcard, которая автоматически находит все .cpp файлы в директории. Это делает Makefile более гибким.


3. Работа с зависимостями

3.1. Установка библиотек через пакетный менеджер

Это самый простой способ, но он имеет ограничения:

  • Разные дистрибутивы Linux могут использовать разные пакетные менеджеры (apt, yum, pacman).

  • Версии библиотек в репозиториях могут быть устаревшими.

Пример установки libcurl:

sudo apt-get install libcurl4-openssl-dev

3.2. Локальная компиляция библиотек

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

wget https://curl.se/download/curl-8.11.1.tar.bz2

tar -jxf curl-8.11.1.tar.bz2

cd curl-8.11.1

./configure --with-openssl

make

Теперь используйте локальные пути при компиляции:

g++ curlexample.cpp -o curlexample \

-I ./curl-8.11.1/include \

-L ./curl-8.11.1/lib/.libs \

-l curl


4. Продвинутые инструменты сборки

4.1. CMake

CMake — это мощный инструмент, который поддерживает кроссплатформенную сборку. Вот пример CMakeLists.txt для проекта с зависимостью от libcurl:

cmake_minimum_required(VERSION 3.10)

project(CurlExample LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)

set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(CURL REQUIRED)

add_executable(CurlExample curlexample.cpp)

target_link_libraries(CurlExample PRIVATE CURL::libcurl)

Чтобы собрать проект:

mkdir build

cd build

cmake ..

make

4.2. Meson

Meson — это современный инструмент, который генерирует файлы для Ninja. Пример meson.build:

project('CurlExample', 'cpp')

# Поиск libcurl

dependency('libcurl')

executable('curlexample', 'curlexample.cpp', dependencies: ['libcurl'])

Сборка:

meson setup build

cd build

ninja

4.3. SCons

SCons использует Python для написания рецептов. Пример SConstruct:

env = Environment()

env.Append(LIBS=['curl'])

env.Program(target='curlexample', source='curlexample.cpp')

Сборка:

scons


5. Docker для изоляции сборки

Docker позволяет создавать изолированные контейнеры с нужной версией ОС и библиотек. Это особенно полезно для старых проектов или CI/CD.

Пример Dockerfile:

FROM ubuntu:20.04

RUN apt-get update && apt-get install -y \

build-essential cmake git libcurl4-openssl-dev

WORKDIR /app

COPY . .

RUN mkdir build && cd build && cmake .. && make

Сборка и запуск:

docker build -t myproject .

docker run -it --rm -v $(pwd):/app myproject


6. Советы по оптимизации сборки

  1. Инкрементальная сборка: Не пересобирайте весь проект, если изменился только один файл. Инструменты вроде make и ninja поддерживают это "из коробки".

  2. Параллельная сборка: Используйте флаг -j для ускорения сборки на многоядерных процессорах:

    make -j$(nproc)

  3. Кэширование зависимостей: Если вы используете Docker, сохраняйте зависимости в кэше, чтобы не скачивать их заново при каждой сборке.

  4. Статическая vs Динамическая линковка: Выбирайте подходящий метод в зависимости от ваших целей:

    • Статическая линковка создает автономный исполняемый файл, но увеличивает его размер.

    • Динамическая линковка делает файл меньше, но требует наличия библиотек на целевой системе.


7. Заключение

Настройка сборки проекта — это инвестиция в будущее. Чем раньше вы потратите время на её автоматизацию, тем проще будет развивать проект. Вот несколько рекомендаций:

  • Для небольших проектов используйте Makefile.

  • Для средних и больших проектов выбирайте CMake или Meson.

  • Если вам нужно тестировать сборку в разных окружениях, используйте Docker.

  • Всегда документируйте процесс сборки, чтобы другие разработчики могли легко подключиться к проекту.

Помните: хороший процесс сборки — это залог стабильности и успеха вашего проекта!

Показать полностью
[моё] Программирование Гайд IT Windows C++ Сборка Длиннопост
0
0
loonyloon
loonyloon
6 месяцев назад

Может быть кому-нибудь пригодиться "восстановление и перестройка репозитория wmi в случае когда он отвалился⁠⁠

Вот пример содержимого для .bat файла, который поможет восстановить репозиторий WMI в Windows 10 и 11. Этот скрипт выполняет ряд команд для остановки и перезапуска служб, связанных с WMI, а также перестроение репозитория.

@Echo off

echo Восстановление репозитория WMI...

echo Остановка службы Winmgmt...

net stop winmgmt /y

echo Удаление старого репозитория WMI...

if exist %windir%\system32\wbem\repository.bak rmdir /s /q %windir%\system32\wbem\repository.bak

if exist %windir%\system32\wbem\repository ren %windir%\system32\wbem\repository repository.bak

echo Перерегистрация WMI...

cd /d %windir%\system32\wbem

for /f %%s in ('dir /b /s *.dll') do regsvr32 /s %%s

echo Перекомпиляция MOF-файлов...

for /f %%s in ('dir /b *.mof') do mofcomp %%s

echo Перезапуск службы Winmgmt...

net start winmgmt

echo Восстановление завершено. Проверьте состояние WMI.

pause

### Как использовать:

1. Скопируйте этот текст в текстовый редактор (например, Блокнот).

2. Сохраните файл с расширением .bat, например, fix_wmi.bat.

3. Запустите файл от имени администратора (правой кнопкой мыши → "Запуск от имени администратора").

### Что делает скрипт:

1. Останавливает службу winmgmt.

2. Удаляет старый репозиторий WMI (если он есть) и создает резервную копию.

3. Перерегистрирует все DLL-файлы, связанные с WMI.

4. Перекомпилирует MOF-файлы.

5. Запускает службу winmgmt снова.

После выполнения скрипта проверьте, восстановилась ли функциональность WMI. Если проблема сохраняется, может потребоваться дополнительная диагностика.

Показать полностью
[моё] Windows Гайд Текст
0
4
mcnikirikitiki
6 месяцев назад
Лига программистов

Основы сборки проектов на C/C++⁠⁠

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

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

Здравствуйте, мои маленькие любители программирования!

Давайте разберем основы сборки проектов на Си/Си++ простым языком:

  1. Основные понятия:

  • Программа собирается из множества файлов

  • Есть два типа библиотек:

    • Статические (.a, .lib) - код встраивается в exe-файл

    • Динамические (.so, .dll) - подгружаются при запуске

  1. Этапы сборки:

// hello.cpp

#include <iostream>

void say_hello() {

std::cout << "Hello, world!" << std::endl;

}

// main.cpp

void say_hello();

int main() {

say_hello();

return 0;

}

Компиляция:

# Компилируем каждый файл отдельно

g++ -c hello.cpp -o hello.o

g++ -c main.cpp -o main.o

# Связываем объектные файлы в исполняемый

g++ hello.o main.o -o program

  1. Работа с библиотеками:

Создание статической библиотеки:

# Создаем объектный файл

g++ -c mylib.cpp -o mylib.o

# Архивируем в статическую библиотеку

ar rcs libmylib.a mylib.o

Использование:

g++ main.cpp -L. -lmylib -o program

  1. Особенности C++

Искажение имен (name mangling):

// В исходном коде

void my_function(int a);

// После компиляции

_Z11my_functioni

  1. Управление зависимостями

Пример Makefile:

program: main.o hello.o

g++ main.o hello.o -o program

main.o: main.cpp

g++ -c main.cpp

hello.o: hello.cpp

g++ -c hello.cpp

clean:

rm *.o program

Основные команды:

  • make - собрать проект

  • make clean - очистить скомпилированные файлы

Это базовые принципы, которые помогут начать работать с проектами на Си/Си++. В следующих частях рассмотрим более сложные случаи и современные инструменты сборки.

Показать полностью
[моё] Программирование IT Программист Сборка Linux Windows Гайд Текст
2
5
IdealTechnoNews
IdealTechnoNews
6 месяцев назад
GeekNews

Бустим Windows 10/11 за ДВА КЛИКА — отключаем лишние процессы, которые пожирают ресурсы компа⁠⁠

Бустим Windows 10/11 за ДВА КЛИКА — отключаем лишние процессы, которые пожирают ресурсы компа Программа, Инновации, Технологии, Гайд, Windows, Тестирование, Инструкция
  1. Открываем «Пуск» → «Настройки».

  2. Кликаем на «Систему» → далее «Уведомления и действия».

  3. Отжимаем галочку на «Получать советы, подсказки и предложения в процессе использования Windows».

Сохраняем себе простой гайд и забываем о тормозах.

Программа Инновации Технологии Гайд Windows Тестирование Инструкция
7
Посты не найдены
О нас
О Пикабу Контакты Реклама Сообщить об ошибке Сообщить о нарушении законодательства Отзывы и предложения Новости Пикабу Мобильное приложение RSS
Информация
Помощь Кодекс Пикабу Команда Пикабу Конфиденциальность Правила соцсети О рекомендациях О компании
Наши проекты
Блоги Работа Промокоды Игры Курсы
Партнёры
Промокоды Биг Гик Промокоды Lamoda Промокоды Мвидео Промокоды Яндекс Директ Промокоды Отелло Промокоды Aroma Butik Промокоды Яндекс Путешествия Постила Футбол сегодня
На информационном ресурсе Pikabu.ru применяются рекомендательные технологии