为什么memset()错误地初始化int?

29
为什么下面程序的输出是 84215045
int grid[110];
int main()
{
    memset(grid, 5, 100 * sizeof(int));
    printf("%d", grid[0]);
    return 0;
}

我不认为这会导致您看到的问题,但是您是否有意将网格设置为110个元素的数组,然后只分配了100个元素? - Alex
7
如果你将那个数字以十六进制的形式打印出来,你可能会看到问题所在。 - James Black
@Alex:这不会有任何改变,因为我正在打印grid[0]。 - Ahmad
没错,它看起来只是个打字错误,我想确保你已经注意到了。 - Alex
9个回答

51

memset函数将目标缓冲区的每个字节设置为指定的值。在你的系统上,一个int占四个字节,每个字节在调用memset后都被设置为5。因此,grid[0]的值为0x05050505(十六进制),对应的十进制是84215045

一些平台提供了替代的API来向目标缓冲区写入更宽的模式;例如,在OS X或iOS上,你可以使用:

int pattern = 5;
memset_pattern4(grid, &pattern, sizeof grid);

想要得到您期望的行为,您是针对哪个平台进行开发?

在 C++ 中,您只需使用std::fill_n

std::fill_n(grid, 100, 5);

Windows 7 32-bit // gnu++ 编译器 - Ahmad
@Ahmad:我不确定Windows是否提供这样的API;如果有,我相信其他人可以指出来。 - Stephen Canon
2
@Ahmad:我没有注意到你标记了C++,只需使用std::fill即可。 - Stephen Canon
通过memset分配指针是否安全?如果int不等于uintptr_t会怎样? - Dmytro
1
@Dmitry:不是很清楚你的问题,但这里的示例并不假设intuintptr_t是相同的(第一个示例假设int是四个字节,但在memset_pattern4 API可用的情况下,这是一个有效的假设)。 - Stephen Canon

13
memset(grid, 5, 100 * sizeof(int));

你正在设置从(char*)grid开始,长度为400字节,结束于(char*)grid + (100 * sizeof(int))的值为5的内存块(这里需要进行强制类型转换,因为memset处理的是字节,而指针运算处理的是对象)。

十六进制中的84215045等同于0x05050505;由于int在你的平台/编译器等上被表示为四个字节,所以当你打印它时,会输出"四个五"。


1
设置(100 * 4)字节,从&grid [0]开始,是吗? - johnsyweb
现在我能理解你的回答:D,但是还有解决方案吗?! - Ahmad

8

memset 是用于设置字节而非值的。在 C++ 中设置数组值的众多方法之一是 std::fill_n

std::fill_n(grid, 100, 5);

这个函数比循环所有元素更快吗? - Ahmad
@Ahmad:那个函数很可能会遍历所有元素。 - Keith Thompson
2
一个好的编译器将会把for循环和std::fill_n都转换成优化的代码序列,使用宽对齐存储(可能是向量),或者调用已知在目标平台上快速的库函数。 - Stephen Canon

6
不要使用memset。
您将内存的每个字节[]设置为5的值。每个int占用4个字节长[5] [5] [5] [5],编译器正确解释为5 * 256 * 256 * 256 + 5 * 256 * 256 + 5 * 256 + 5 = 84215045。相反,使用for循环,它也不需要sizeof()。一般来说,sizeof()意味着您正在以困难的方式执行某些操作。
for(int i=0; i<110; ++i)
    grid[i] = 5;

1
@Mooing Duck:memset 如何导致你的程序崩溃,具体来说是怎样的? - Stephen Canon
另一个原因是对于那些被设置为零是无效错误的对象。memset将会让这些对象崩溃,而for循环可以在编译期间告诉你。 - Mooing Duck
1
实际上,对于数组初始化的特定情况,我认为使用memset更容易做到正确无误,因为您可以简单地使用sizeof array。这样,如果将来更改数组的大小,则初始化会自动保持同步。 - Stephen Canon
@Stephen 我认为在数组的范围内使用memset是可以的,你说得对。但问题在于这样很容易出现代码错误。 - Ahmad
2
当然,如果你正在编写C++代码,这应该是一个容器和std::fill - Stephen Canon
显示剩余5条评论

3

对于 memset 函数来说,它会以所选值填充字节。因此,一个 int 类型的变量看起来可能像这样:

00000101 00000101 00000101 00000101

这将被解释为84215045。


那么...有什么解决办法吗?! :D - Ahmad

2

您并没有明确说明您的程序想要做什么。

假设您想将grid的前100个元素设置为5(忽略100110之间的差异),只需执行以下操作:

for (int i = 0; i < 100; i ++) {
    grid[i] = 5;
}

我知道你关心速度,但你的担忧可能是不必要的。一方面,memset() 很可能已经被优化过了,因此比简单循环更快。另一方面,优化很可能包括一次写入多个字节,而这正是这个循环所做的。另一方面,memset() 本身就是一个循环;明确编写循环而不是将其隐藏在函数调用中并没有改变这一点。最后,即使循环很慢,也不太可能有影响;专注于编写清晰的代码,并在实际测量表明存在显著性能问题时考虑进行优化。
你花费的时间撰写问题比计算机设置 grid 的时间多得多。
最后,在我手头的任务完成之前,如果 memset() 没有达到你想要的效果,那么它的速度再快也无济于事。(完全不设置 grid 更快!)

1
如果您在shell中键入man memset,它会告诉您:

void * memset(void *b, int c, size_t len)

简单的英文解释是,它用值c填充长度为len的字节字符串b
对于您的情况,
memset(grid, 5, 100 * sizeof(int));

由于sizeof(int)==4,因此上述代码段看起来像这样:
for (int i=0; i<100; i++)
    grid[i]=0x05050505;

或者

char *grid2 = (char*)grid;
for (int i=0; i<100*sizeof(int); i++)
    grid2[i]=0x05;

它将打印出84215045

但在大多数C代码中,我们希望将一块内存块初始化为零值。

  • char类型--> \0NUL
  • int类型-->0
  • float类型-->0.0f
  • double类型-->0.0
  • 指针类型-->nullptr

而且,无论是gcc还是clang等现代编译器都可以自动地为您处理这些。

// variadic length array (VLA) introduced in C99
int len = 20;
char carr[len];
int iarr[len];
float farr[len];
double darr[len];
memset(carr, 0, sizeof(char)*len);
memset(iarr, 0, sizeof(int)*len);
memset(farr, 0, sizeof(float)*len);
memset(darr, 0, sizeof(double)*len);
for (int i=0; i<len; i++)
{
    printf("%2d: %c\n", i, carr[i]);
    printf("%2d: %i\n", i, iarr[i]);
    printf("%2d: %f\n", i, farr[i]);
    printf("%2d: %lf\n", i, darr[i]);
}

但请注意,C ISO委员会并没有强制规定这样的定义,这是与编译器相关的。

0

由于memset写入字节,我通常将其用于将int数组设置为零,例如:

int a[100];
memset(a,0,sizeof(a));

或者您可以使用它来设置一个字符数组,因为一个字符恰好是一个字节:

char a[100];
memset(a,'*',sizeof(a));

此外,int数组还可以通过memset设置为-1:
memset(a,-1,sizeof(a));

这是因为-1在int中表示为0xffffffff,在char(一个字节)中表示为0xff。

0

这段代码已经过测试。以下是一种将“整数”数组的所有元素设置为0到255之间某个值的方法。

MinColCost=new unsigned char[(Len+1) * sizeof(int)];

memset(MinColCost,0x5,(Len+1)*sizeof(int));

memset(MinColCost,0xff,(Len+1)*sizeof(int));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接