#c #gcc #g #clang #destructor
#c #gcc #g #лязг #деструктор
Вопрос:
Я просматриваю чужой код и вижу, что в деструкторе он перезаписывает буфер, который является массивом int внутри класса. Я подумал, что, возможно, компилятор может оптимизировать это.
Итак, я создал небольшую программу для тестирования, и она оптимизирует деструктор.
Единственное простое решение, которое я нашел, — использовать атрибут ((optimize(«O0»))) для деструктора. Но должен быть способ сделать это без использования атрибутов, специфичных для gcc.
Что я пробовал:
-
Настройка буфера на изменчивость — это работает, если вы напрямую устанавливаете буфер в цикле for (не в memset или тому подобном). Но я действительно не хочу, чтобы это было изменчивым, поскольку это не так.
-
атрибут ((optimize(«O0»))) работает, но не переносим и выглядит довольно уродливо.
-
создание собственного нового / удаления с помощью собственного пула памяти. Я подумал, что если компилятор не знает, как используется память, он не должен оптимизировать запись в нее. Тот факт, что это не сработало, кажется ошибкой.
-
Настройка класса, единственной задачей которого является перезапись буфера в другой памяти. Это работает, но, черт возьми, это бесполезно уродливо.
Я думаю, что многие приложения захотели бы сделать что-то подобное. Не просто обнуление ключей. Наверняка кто-то сталкивался с этой проблемой раньше, или я просто делаю что-то не так.
Идеи?
g memset.cpp -O1 -fsanitize=не определено -Стена -Wextra
#include <stdio.h>
#include <string.h>
class example{
public:
int key[100];
~example(){
printf("destructorn");
memset(key,0, sizeof(key));
}
};
void print_memory(int *in){
printf ("n");
for(int i =0; i < 10; i ){
printf(" -:a: x ",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("ndeletedn");
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
Комментарии:
1. Вы никогда не вызываете деструктор of
example
. Вы должны явно вызватьdelete it;
или создатьexample
статически, используяexample it;
вместоexample* it = new it;
предпочтительно последнего (статически создать его), поскольку использование new / delete вообще является запахом кода.2. Насколько я знаю, люди обычно используют функции ОС, такие как
SecureZeroMemory
иexplicit_bzero
для этого, но я предполагаю, что вы ищете что-то в самом языке и стандартной библиотеке.3. Если класс не владеет буфером, компилятору будет сложнее решить, что код не имеет заметного эффекта, что может помешать оптимизации?
4. Печать массива после удаления указателя — это UB. Разве это не делает этот пример немного бесполезным?
5. Деструктор, похоже, является memsetting … первые 8 байт массива.
sizeof(int*)
махинации?
Ответ №1:
Простой способ — получить к нему доступ через 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);
}
или:
#include <algorithm>
#include <array>
int main() {
char key[200];
std::fill<volatile char *>(std::begin(key), std::end(key), 0);
}
Комментарии:
1. Хорошая идея! Мне это нравится.
2. Я пробовал оба, и они оба работают (как и ожидалось). Это самое простое решение, которое я видел.