Как поручить компилятору VC ++ не указывать константу? - PullRequest
4 голосов
/ 20 апреля 2019

В моей программе на C ++ есть следующая глобальная константа:

const int K = 123456 ;

Когда я компилирую программу, результирующий исполняемый файл содержит буквальное значение 123456 во всех местах, где оно используется (десяткираз).

Но, если я удаляю квалификатор const, значение 123456 появляется только один раз во всем исполняемом файле (в разделе .data).
Это результат Iищу.Я хочу, чтобы значение 123456 появлялось только один раз, чтобы его можно было изменить, просто отредактировав файл .exe с помощью редактора HEX.

Однако я не хочу удалять квалификатор const, посколькуЯ хочу, чтобы компилятор не позволил мне случайно изменить константу в исходном коде.

Можно ли как-то указать компилятору не указывать значение этой константы в инлайн?


Причина, по которой мне нужно это сделать, заключается в том, что исполняемый файл легко модифицируется студентами, которым будет поручено «взломать» пример программы для изменения ее поведения.Упражнение должно быть достаточно простым для неопытных людей.

Ответы [ 3 ]

5 голосов
/ 20 апреля 2019

Если вы не хотите вставлять K, поместите это в заголовочный файл:

extern const int K;

Это означает, что «К определено где-то еще». Затем поместите это в файл cpp:

const int K = 123456;

Во всех местах, где используется K, компилятор знает только, что K является const int объявленным extern союзником. Компилятор не знает значение K, поэтому оно не может быть встроено. Компоновщик найдет определение K в файле cpp и поместит его в раздел .data исполняемого файла.

В качестве альтернативы вы можете определить K следующим образом:

const volatile int K = 123456;

Это означает, что «К может волшебным образом измениться, поэтому вам лучше не принимать его значение». Это имеет эффект, аналогичный предыдущему, так как компилятор не встроит K, потому что он не может предположить, что K всегда будет 123456. Предыдущий подход потерпит неудачу, если LTO будет включен, но в этом случае будет работать volatile.

Должен сказать, это действительно странная вещь. Если вы хотите, чтобы ваша программа настраивалась, вы должны поместить значение K в текстовый файл и затем прочитать файл при запуске.

2 голосов
/ 20 апреля 2019

Простейшим вариантом является, вероятно, объявление его как глобального без const, поэтому компилятор не может предположить, что он все еще имеет значение статического инициализатора.

int K = 123456;

Даже оптимизация во время компоновки не может знать, что библиотечная функция не имеет доступа к этой глобальной функции, при условии, что вы вызываете ее в своей программе.

Если вы использовали static int K = 123456;, компилятор мог заметить, что никакие функции в модуле компиляции не записывают значение, и ни одна из них не передает и не возвращает его адрес, поэтому экранирующий анализ для всего модуля компиляции может обнаружить, что это было постоянный и может быть оптимизирован.

(Если вы действительно хотите, чтобы это было static int K;, включите глобальную функцию, такую ​​как void setK(int x){K=x;}, которую вы никогда не вызываете. Без Link-Time Optimization, компилятор должен будет предположить, что что-то вне этого модуля компиляции могло бы вызвать эта функция и была изменена K, и любой вызов функции, определение которой не видно, может привести к такому вызову.)


Помните, что volatile const int K = 123456; может значительно ухудшить оптимизацию, чем сделать ее не const, особенно если у вас есть выражения, использующие K несколько раз.

(Но любой из них может сильно повредить, в зависимости от того, какие оптимизации были возможны. Распространение констант может быть огромной победой.)

Компилятор должен выдавать asm, который загружает ровно K один раз за каждый раз, когда C абстрактная машина читает его. (Например, чтение K считается видимым побочным эффектом, например, чтение из порта MMIO или местоположения, на котором установлена ​​аппаратная точка наблюдения.)

Если вы хотите, чтобы компилятор загружал его один раз за цикл, и предполагали, что K является инвариантом цикла, то код, который его использует, должен выполнить int local_k = K;. Вам решать, как часто вы хотите перечитывать K, т. Е. Какую область вы делаете / повторять local_k = K в.

На x86 использование операнда источника памяти, который остается горячим в кеше L1d, вероятно, не является большой проблемой для производительности, но предотвратит автоматическую векторизацию.

Причина, по которой мне нужно это сделать, заключается в том, что исполняемый файл легко модифицируется учащимися, которым будет поручено «взломать» пример программы для изменения ее поведения. Упражнение должно быть достаточно простым для неопытных людей.

Для этот вариант использования, да volatile именно то, что вы хотите. Повторное чтение всех данных из памяти на месте делает это немного проще, чем следование значению, кэшированному в регистре.

А производительность по сути не имеет значения, и вам не понадобится автоматическая векторизация. Вероятно, просто легкая оптимизация, чтобы студентам не приходилось разбираться с сохранением / перезагрузкой всего после каждого оператора C ++. Вроде бы у gcc -Og было бы идеально.

С MSVC, возможно, попробуйте -O1 или -O2 и посмотрите, не смущает ли это что-нибудь. Я не думаю, что у него есть варианты для какой-то, но не слишком агрессивной оптимизации, это может быть либо отладочная сборка (хорошая для пошагового выполнения исходного кода C ++, плохая для чтения asm), либо полностью оптимизированная для размера или скорости.

0 голосов
/ 20 апреля 2019

Попробуйте объявить константу как переменную.Это должно привести к единственному изменяемому значению, которое не будет вставлено.

...