Базовое применение memset

Начнем с фундаментального синтаксиса и типичных применений memset:

#include <cstring>
#include <iostream>

int main() {
char str[50];
std::memset(str, '*', sizeof(str));
str[sizeof(str) - 1] = '\0'; // Конечный ноль для строковых операций

std::cout << str << std::endl; // Выводится 49 звездочек

return 0;
}

Функцией memset принимается три параметра:

  1. Указатель на блок памяти.
  2. Задаваемое значение, оно преобразуется в беззнаковый символ.
  3. Количество задаваемых байтов.

Инициализация массивов

Вот как инициализируются различные типы массивов:

#include <cstring>
#include <iostream>

void printArray(const int arr[], int size) {
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}

int main() {
// Массив инициализируется в нули
int numbers[10];
std::memset(numbers, 0, sizeof(numbers));
printArray(numbers, 10); // Все нули

// Массив инициализируется в «–1»
std::memset(numbers, -1, sizeof(numbers));
printArray(numbers, 10); // Все «–1»

// Массив инициализируется в «1», не делайте так: это не правильно
std::memset(numbers, 1, sizeof(numbers));
printArray(numbers, 10); // Неожиданные результаты

return 0;
}

Важно: memset характеризуется побайтной работой, эта функция безопасна для:

  1. Задания в 0.
  2. Задания в –1, все биты задаются в 1.
  3. Массивов символов.

Но не для других значений с многобайтовыми типами, такими как int.

Инициализация двумерного массива

Вот как используется memset с 2D-массивами:

#include <cstring>
#include <iostream>

void print2DArray(int arr[][3], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 3; j++) {
std::cout << arr[i][j] << " ";
}
std::cout << std::endl;
}
}

int main() {
int matrix[3][3];

// Вся матрица инициализируется в ноль
std::memset(matrix, 0, sizeof(matrix));

std::cout << "Zero matrix:\n";
print2DArray(matrix, 3);

// Инициализируется в «–1»
std::memset(matrix, -1, sizeof(matrix));

std::cout << "\nNegative one matrix:\n";
print2DArray(matrix, 3);

return 0;
}

Пользовательские структуры данных

При работе со структурами или классами функцией memset инициализируются типы POD, то есть типы простой структуры данных:

#include <cstring>
#include <iostream>

struct GameTile {
int x;
int y;
bool isWalkable;
bool isVisible;
char terrain;
};

class GameMap {
private:
static const int WIDTH = 10;
static const int HEIGHT = 10;
GameTile map[HEIGHT][WIDTH];

public:
GameMap() {
// Все плитки инициализируются в ноль
std::memset(map, 0, sizeof(map));
}

void clearVisibility() {
for (int i = 0; i < HEIGHT; i++) {
// В «isVisible» для каждой строки задается «false»
std::memset(&map[i][0].isVisible, 0, WIDTH * sizeof(GameTile));
}
}

void printMap() {
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
std::cout << (map[i][j].isVisible ? 'V' : '.');
}
std::cout << std::endl;
}
}
};

int main() {
GameMap game;
game.printMap();
return 0;
}

Реальный сценарий: буфер обработки изображений

Вот практический пример использования memset при обработке изображений:

#include <cstring>
#include <iostream>

class ImageBuffer {
private:
static const int WIDTH = 100;
static const int HEIGHT = 100;
unsigned char pixels[HEIGHT][WIDTH];

public:
// Буфер очищается в черный
void clearToBlack() {
std::memset(pixels, 0, sizeof(pixels));
}

// Буфер очищается в белый
void clearToWhite() {
std::memset(pixels, 255, sizeof(pixels));
}

// Применяется горизонтальная линия
void drawHorizontalLine(int y, unsigned char value) {
if (y >= 0 && y < HEIGHT) {
std::memset(pixels[y], value, WIDTH);
}
}

// Выводится предпросмотр изображения
void preview() {
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
std::cout << (pixels[i][j] > 127 ? '#' : '.');
}
std::cout << std::endl;
}
}
};

int main() {
ImageBuffer img;

img.clearToBlack();
img.drawHorizontalLine(50, 255); // В середине рисуется белая линия
img.preview();

return 0;
}

Типичные проблемы и их решения

Вот ситуации, в которых memset чревата проблемами:

#include <cstring>
#include <iostream>

class Example {
public:
void demonstratePitfalls() {
// Проблема 1: использование с не-POD типами
std::string strArray[10];
// std::memset(strArray, 0, sizeof(strArray)); // Не делайте этого

// Проблема 2: задание целых чисел значениям, отличным от ноля или «–1»
int numbers[5];
std::memset(numbers, 1, sizeof(numbers)); // Неправильно

// Корректный способ инициализировать массивы не-POD типов
std::string correctStrArray[10] = {}; // Инициализация по умолчанию

// Корректный способ задать конкретные целочисленные значения
int correctNumbers[5];
for (int& num : correctNumbers) {
num = 1; // Используется прямое присваивание
}
}
};

Оптимизация производительности с memset

При работе с большими наборами данных memset эффективнее, чем инициализация на основе цикла:

#include <cstring>
#include <iostream>
#include <chrono>

class PerformanceTest {
private:
static const int SIZE = 1000000;
char largeArray[SIZE];

public:
void testMemset() {
auto start = std::chrono::high_resolution_clock::now();
std::memset(largeArray, 0, SIZE);
auto end = std::chrono::high_resolution_clock::now();

std::chrono::duration<double> diff = end - start;
std::cout << "memset time: " << diff.count() << " seconds\n";
}

void testLoop() {
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < SIZE; i++) {
largeArray[i] = 0;
}
auto end = std::chrono::high_resolution_clock::now();

std::chrono::duration<double> diff = end - start;
std::cout << "Loop time: " << diff.count() << " seconds\n";
}
};

int main() {
PerformanceTest test;
test.testMemset();
test.testLoop();
return 0;
}

Практические применения

  1. Сброс игрового состояния:
struct GameState {
int score;
bool gameOver;
int lives;
float timeElapsed;

void reset() {
std::memset(this, 0, sizeof(GameState));
lives = 3; // После обнуления задаются жизни по умолчанию
}
};

2. Очистка сетевого буфера:

class NetworkBuffer {
private:
static const int BUFFER_SIZE = 1024;
unsigned char buffer[BUFFER_SIZE];

public:
void clear() {
std::memset(buffer, 0, BUFFER_SIZE);
}

void prepare() {
clear(); // Очищается до получения новых данных
}
};

Заключение

memset  —  это эффективный инструмент для инициализации памяти на C++, особенно полезный для:

  1. Обнуления массивов и буферов.
  2. Задания массивам значения –1.
  3. Работы с массивами символов.
  4. Инициализации структур POD.

Обобщим ключевые моменты:

  1. Используйте только с типами POD.
  2. Будьте осторожны с многобайтовыми значениями.
  3. Всегда проверяйте параметр размера size.
  4. Для не-POD типов используйте std::fill.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи ryan: C++ memset: A Complete Guide

Предыдущая статьяВаш JavaScript-бандлер слишком раздут
Следующая статьяОсвоение Scrollable во Flutter