0. 前言
问题来源于大一下学期的第一次机考……这是一道分块的题目,程序里我有这样子的一个操作:
memset(mul_tag, 1, sizeof(mul_tag));
不出意外,程序WA了。我debug了太久太久,这行看起来根本没有问题啊?但事实上把这行改了就能对!Why???我们从 memset
函数原型来看,就能发现这个问题。
1. memset
函数简介
memset
函数的原型如下:
void *memset(void *s, int c, size_t n);
s
: 指向要填充的内存块的指针。c
: 要设置的值。注意:c
虽然是int
类型,但memset
实际上是将c
转换为unsigned char
后进行填充。n
: 要填充的字节数。
memset
的作用是将 s
指向的内存块的前 n
个字节设置为 c
指定的值。
2. 陷阱
memset
是按字节进行填充的。当 a
是一个 int
数组(假设 int
占用 4 个字节)时,memset(a, 1, sizeof(a))
会将每个 int
元素的每个字节都设置为 1
。 这会导致每个 int
元素的值变成 0x01010101
,即十进制的 16843009
,而不是我们期望的 1
。
3. 例外
memset(a, 1, sizeof(a))
大部分情况下是很危险的。但是,有几个例外情况它能够正常工作:
char
数组: 如果a
是char
数组,memset(a, 1, sizeof(a))
是正确的,因为char
类型只占用一个字节。- 初始化为 0:
memset(a, 0, sizeof(a))
可以安全地用于任何类型的数组,用于将整个数组初始化为 0。(也就是我们平时所做的!这也是为什么我会认为memset(a, 1, sizeof(a))
没问题!) - 初始化为 -1 (int类型):
memset(a, -1, sizeof(a))
对于int
数组是安全的, 可以得到-1的结果.
4. 你应该用std::fill
std::fill
示例(C++):
#include <algorithm>
#include <array>
std::array<int, 10> a; // 或者 int a[10];
std::fill(a.begin(), a.end(), 1); // 或者 std::fill(a, a + 10, 1);