如何可靠地让析构函数覆盖缓冲区而不在 C++ 中对其进行优化
How to reliably get a destructor to overwrite buffer without it being optimized out in c++
我正在审查其他人的代码,我在析构函数中看到他正在覆盖一个缓冲区,该缓冲区是 class 中的一个 int 数组。我想编译器可能会优化它。
所以我做了一个小程序来测试,它确实优化了析构函数。
我发现的唯一简单解决方案是在析构函数上使用 attribute((optimize("O0")))。但是必须有一种方法可以在不使用特定于 gcc 的属性的情况下执行此操作。
我尝试过的事情:
将缓冲区设置为可变的——如果您直接在 for 循环(而不是 memset 或类似的)中设置缓冲区,这将起作用。但我真的不希望它不稳定,因为它不是。
attribute((optimize("O0"))) 有效但不可移植,而且有点丑。
用它自己的内存池制作我自己的 new/delete。我在想,如果编译器不知道内存是如何被使用的,它就不应该优化对它的写入。这不起作用的事实似乎是一个错误。
设置一个 class 唯一的工作就是覆盖另一个内存中的缓冲区。这行得通,但伙计,这是不必要的丑陋吗。
我想很多应用程序都想做这样的事情。不只是将键归零。肯定有人 运行 以前处理过这个问题,或者我只是做错了什么。
想法?
g++ memset.cpp -O1 -fsanitize=undefined -Wall -Wextra
#include <stdio.h>
#include <string.h>
class example{
public:
int key[100];
~example(){
printf("destructor\n");
memset(key,0, sizeof(key));
}
};
void print_memory(int *in){
printf ("\n");
for(int i =0; i < 10; i++){
printf(" %2d:a: %08x ",i, in[i]);
}
}
int main(){
example *ptr;
{
example *it = new example;
ptr = it;
// fill memory with something.
for(int i = 0; i< 100; i++){
it->key[i] = rand();
}
printf("Done randomizing.\n");
print_memory(it->key);
delete it;
}
printf("\ndeleted\n");
print_memory(ptr->key);
printf("\n");
}
输出:
完成随机化。
0:a: 6b8b4567 1:a: 327b23c6 2:a: 643c9869 3:a: 66334873 4:a: 74b0dc51 5:a: 19495cff 6:a : 2ae8944a 7:a: 625558ec 8:a: 238e1f29 9:a: 46e87ccd 析构函数
已删除
0:a: 00000000 1:a: 00000000 2:a: 0e57f010 3:a: 0000555b 4:a: 74b0dc51 5:a: 19495cff 6:a : 2ae8944a 7:a: 625558ec 8:a: 238e1f29 9:a: 46e87ccd
一种裸机方式是通过 volatile 访问它——那样它就不会被优化。 C 方式是:
#include <cstddef>
void volatile_memset(volatile void *s, int c, size_t n) {
volatile unsigned char *m = reinterpret_cast<volatile unsigned char *>(s);
while (n--) {
*m++ = c;
}
}
int main() {
char key[200];
volatile_memset(key, 0, sizeof(key));
}
但我认为在 C++ 中你可以:
#include <algorithm>
int main() {
char key[200];
std::fill_n<volatile char *>(key, sizeof(key)/sizeof(*key), 0);
}
我正在审查其他人的代码,我在析构函数中看到他正在覆盖一个缓冲区,该缓冲区是 class 中的一个 int 数组。我想编译器可能会优化它。
所以我做了一个小程序来测试,它确实优化了析构函数。
我发现的唯一简单解决方案是在析构函数上使用 attribute((optimize("O0")))。但是必须有一种方法可以在不使用特定于 gcc 的属性的情况下执行此操作。
我尝试过的事情:
将缓冲区设置为可变的——如果您直接在 for 循环(而不是 memset 或类似的)中设置缓冲区,这将起作用。但我真的不希望它不稳定,因为它不是。
attribute((optimize("O0"))) 有效但不可移植,而且有点丑。
用它自己的内存池制作我自己的 new/delete。我在想,如果编译器不知道内存是如何被使用的,它就不应该优化对它的写入。这不起作用的事实似乎是一个错误。
设置一个 class 唯一的工作就是覆盖另一个内存中的缓冲区。这行得通,但伙计,这是不必要的丑陋吗。
我想很多应用程序都想做这样的事情。不只是将键归零。肯定有人 运行 以前处理过这个问题,或者我只是做错了什么。
想法?
g++ memset.cpp -O1 -fsanitize=undefined -Wall -Wextra
#include <stdio.h>
#include <string.h>
class example{
public:
int key[100];
~example(){
printf("destructor\n");
memset(key,0, sizeof(key));
}
};
void print_memory(int *in){
printf ("\n");
for(int i =0; i < 10; i++){
printf(" %2d:a: %08x ",i, in[i]);
}
}
int main(){
example *ptr;
{
example *it = new example;
ptr = it;
// fill memory with something.
for(int i = 0; i< 100; i++){
it->key[i] = rand();
}
printf("Done randomizing.\n");
print_memory(it->key);
delete it;
}
printf("\ndeleted\n");
print_memory(ptr->key);
printf("\n");
}
输出: 完成随机化。
0:a: 6b8b4567 1:a: 327b23c6 2:a: 643c9869 3:a: 66334873 4:a: 74b0dc51 5:a: 19495cff 6:a : 2ae8944a 7:a: 625558ec 8:a: 238e1f29 9:a: 46e87ccd 析构函数
已删除
0:a: 00000000 1:a: 00000000 2:a: 0e57f010 3:a: 0000555b 4:a: 74b0dc51 5:a: 19495cff 6:a : 2ae8944a 7:a: 625558ec 8:a: 238e1f29 9:a: 46e87ccd
一种裸机方式是通过 volatile 访问它——那样它就不会被优化。 C 方式是:
#include <cstddef>
void volatile_memset(volatile void *s, int c, size_t n) {
volatile unsigned char *m = reinterpret_cast<volatile unsigned char *>(s);
while (n--) {
*m++ = c;
}
}
int main() {
char key[200];
volatile_memset(key, 0, sizeof(key));
}
但我认为在 C++ 中你可以:
#include <algorithm>
int main() {
char key[200];
std::fill_n<volatile char *>(key, sizeof(key)/sizeof(*key), 0);
}