memset 对浮点数组做了什么?
What is memset doing to a float array?
我最近在网上找到一段代码,看起来有点像这样:
#include <stdio.h>
#include <string.h>
int main() {
float m[10];
memset(m, 0, 20);
}
我还看到了一个我认为是正确的片段:
memset(m, 0, sizeof m);
当尝试使用此代码段打印出第一个示例的所有值时:
for (int i = 0; i < 20; i++) {
printf("%f, \n", m[i]);
}
它产生这样的输出:
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
-0.000000,
-4587372491414098149376.000000,
-0.000013,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000
重新编译时值发生变化。
现在我有几个问题:
- 为什么 memset 可以向
float
数组写入比分配的更多的内容,为什么不能对 char
数组执行此操作?
- 为什么这么不一致?
- 为什么将第二个值更改为
memset
到 1
的值不会更改输出?
Why can memset write to a float
-array more than what was allocated and why can't you do that with a char
-array?
memset(m, 0, 20);
,正如问题最初显示的那样,写入的内容不超过分配的内容。通常,float
在C实现中是四个字节,所以float m[10];
分配40个字节,memset(m, 0, 20);
写20个。
在新代码中,memset(m, 0, sizeof m);
向 m
写入的字节数与它写入的字节数一样多,不多也不少。
如果 memset
被要求写更多,您可以尝试这样做的原因是 C 实现通常不进行安全检查操作,并且 C 标准不要求它们这样做。
Why is it so inconsistent?
没有什么不一致的。 memset
在m
的前20个字节写0,也就是浮点数0的编码,float
常用的格式(IEEE-754 binary32,也叫“单精度”)。
之后的字节没有被写入,所以打印它们使用的是未初始化的数据。 C 标准说未初始化对象的值未确定。一个常见的结果是程序使用内存中已经存在的任何内容。那可能是零,也可能是其他东西。
但是,使用循环 for (int i = 0; i < 20; i++)
,您超出了 m
中的 10 个元素。然后访问 m[i]
的行为不是由 C 标准定义的。如上所述,一个常见的结果是程序访问计算出的内存并使用其中的任何内容。然而,各种其他行为也是可能的,包括由于尝试访问未映射的内存而导致崩溃,或者编译器在优化期间用备用代码替换未定义的代码。
Why does changing the second value of memset
not change the output?
它会,取决于你把它改成什么。该字节的某些值可能会导致 float
值非常小,它们仍然打印为“0.000000”。例如,如果字节设置为 1,使每个 float
中的 32 位 0x01010101
,它们表示 float
值 8,454,401•2− 148 = 2.36942782761723955384693006253917004604239556833255136345174597127722672385008451101384707726538181304931640625•10[=8[31=]3]3]。
如果您使用 64 作为 memset
的第二个参数,这些位将被设置为 0x40404040
,其编码值为 3.0039215087890625,因此将打印“3.003922”。
Memset
对 float
数组没有任何作用。你只有一个包含 10 个浮点数的数组,你使用了一个覆盖 20 个浮点数的循环,这意味着,由于 C 不检查数组边界......不是。我不会在这里重复其他答案中已经说过的内容,但是您在调整 C 对象的大小时遇到了问题。 sizeof
运算符是您的朋友。在将参数传递给 memcpy
或 malloc
之类的函数时使用它而不是使用常量,因此如果您调整对象的大小,您仍然可以,而无需更改常量值。
我最近在网上找到一段代码,看起来有点像这样:
#include <stdio.h>
#include <string.h>
int main() {
float m[10];
memset(m, 0, 20);
}
我还看到了一个我认为是正确的片段:
memset(m, 0, sizeof m);
当尝试使用此代码段打印出第一个示例的所有值时:
for (int i = 0; i < 20; i++) {
printf("%f, \n", m[i]);
}
它产生这样的输出:
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
-0.000000,
-4587372491414098149376.000000,
-0.000013,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000,
0.000000
重新编译时值发生变化。
现在我有几个问题:
- 为什么 memset 可以向
float
数组写入比分配的更多的内容,为什么不能对char
数组执行此操作? - 为什么这么不一致?
- 为什么将第二个值更改为
memset
到1
的值不会更改输出?
Why can memset write to a
float
-array more than what was allocated and why can't you do that with achar
-array?
memset(m, 0, 20);
,正如问题最初显示的那样,写入的内容不超过分配的内容。通常,float
在C实现中是四个字节,所以float m[10];
分配40个字节,memset(m, 0, 20);
写20个。
在新代码中,memset(m, 0, sizeof m);
向 m
写入的字节数与它写入的字节数一样多,不多也不少。
如果 memset
被要求写更多,您可以尝试这样做的原因是 C 实现通常不进行安全检查操作,并且 C 标准不要求它们这样做。
Why is it so inconsistent?
没有什么不一致的。 memset
在m
的前20个字节写0,也就是浮点数0的编码,float
常用的格式(IEEE-754 binary32,也叫“单精度”)。
之后的字节没有被写入,所以打印它们使用的是未初始化的数据。 C 标准说未初始化对象的值未确定。一个常见的结果是程序使用内存中已经存在的任何内容。那可能是零,也可能是其他东西。
但是,使用循环 for (int i = 0; i < 20; i++)
,您超出了 m
中的 10 个元素。然后访问 m[i]
的行为不是由 C 标准定义的。如上所述,一个常见的结果是程序访问计算出的内存并使用其中的任何内容。然而,各种其他行为也是可能的,包括由于尝试访问未映射的内存而导致崩溃,或者编译器在优化期间用备用代码替换未定义的代码。
Why does changing the second value of
memset
not change the output?
它会,取决于你把它改成什么。该字节的某些值可能会导致 float
值非常小,它们仍然打印为“0.000000”。例如,如果字节设置为 1,使每个 float
中的 32 位 0x01010101
,它们表示 float
值 8,454,401•2− 148 = 2.36942782761723955384693006253917004604239556833255136345174597127722672385008451101384707726538181304931640625•10[=8[31=]3]3]。
如果您使用 64 作为 memset
的第二个参数,这些位将被设置为 0x40404040
,其编码值为 3.0039215087890625,因此将打印“3.003922”。
Memset
对 float
数组没有任何作用。你只有一个包含 10 个浮点数的数组,你使用了一个覆盖 20 个浮点数的循环,这意味着,由于 C 不检查数组边界......不是。我不会在这里重复其他答案中已经说过的内容,但是您在调整 C 对象的大小时遇到了问题。 sizeof
运算符是您的朋友。在将参数传递给 memcpy
或 malloc
之类的函数时使用它而不是使用常量,因此如果您调整对象的大小,您仍然可以,而无需更改常量值。