Базовое применение 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 принимается три параметра:
- Указатель на блок памяти.
- Задаваемое значение, оно преобразуется в беззнаковый символ.
- Количество задаваемых байтов.
Инициализация массивов
Вот как инициализируются различные типы массивов:
#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 характеризуется побайтной работой, эта функция безопасна для:
- Задания в
0. - Задания в
–1, все биты задаются в1. - Массивов символов.
Но не для других значений с многобайтовыми типами, такими как 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;
}
Практические применения
- Сброс игрового состояния:
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. - Работы с массивами символов.
- Инициализации структур POD.
Обобщим ключевые моменты:
- Используйте только с типами POD.
- Будьте осторожны с многобайтовыми значениями.
- Всегда проверяйте параметр размера
size. - Для не-POD типов используйте
std::fill.
Читайте также:
- C++: полное руководство по циклам while
- C++: практическое руководство по rotate
- C++: практическое руководство по Transform
Читайте нас в Telegram, VK и Дзен
Перевод статьи ryan: C++ memset: A Complete Guide





