pragma pack(push) без соответствующего pop приводит к разбиению стека

Я использовал #pragma pack(push, 2) в начале структуры в файле заголовка, но забыл соответствующий #pragma pack(pop). После включения этого файла заголовка я включил fstream. При созданииofstreamобъект, я вижу разбивание стека. Подробности точного сценария и кода приведены ниже.

Я прошел курс C++ и написал код для проекта. Моя программа падала из-за разбивания стека. Я попытался найти очевидные ошибки переполнения, но не нашел. Я изменил почти весь свой код, чтобы он напоминал код, предоставленный инструктором. Единственная разница заключается в порядке включения файлов заголовков. Я включил свои файлы заголовков, за которыми следуетfstream в то время как инструктор включил fstreamзаголовочный файл вверху. Тем не менее, у меня была такая же проблема. Так что я изменил даже порядок файлов заголовков и вуаля, проблема исчезла.

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

Найдя функцию, я использовал gdb, чтобы установить наблюдение за канареечным значением, используемым моей программой для проверки разбития стека. Я обнаружил разбиение стека в конструктореofstream объект.

В этот момент я знал, что какой-то заголовочный файл, включенный ранее fstreamмешал этому. Итак, теперь я проверил все свои файлы заголовков на предмет каких-либо глупых ошибок и нашел структуру, которой предшествует#pragma pack(push, 2) но без соответствующих #pragma pack(pop). Эта структура должна была быть записана как двоичный файл. Исправив это, проблема была решена.

Поскольку весь проект не имеет значения, я воспроизвел проблему с помощью простого фрагмента кода. Хотя проблема решена, хотелось бы знать, почему это произошло. Я это понимаюpragma packДиректива использовалась, чтобы компилятор не вставлял отступы внутри структуры, поскольку структура должна была быть записана в двоичный файл. Опускаяpack(pop)в конце структуры использует то же самое для всех последующих файлов заголовков. Но может ли это привести к тому, что конструктор ofstream будет писать за кадром стека?

Я использую gcc v7.4.0 в Ubuntu 18.04.

/* code.cpp */ 
#include "header.h"
#include <fstream>
using namespace std;

int main(){
    ofstream fout;
    fout.open("file.ext", ios::out|ios::binary);
    fout.close();
    return 0;
}

Заголовочный файл

#ifndef HEADER_H_
#define HEADER_H_

#pragma pack(push, 2)
struct something{
    int a;
};
//#pragma pack(pop)
//Uncommenting the above line solves the problem

#endif

1 ответ

Решение

Поскольку pragma pack влияет на макет экземпляров класса, ваша версия ofstreamне выглядит так же, как тот, который использовался для компиляции вашей стандартной библиотеки. Формально у вас есть нарушение ODR, что приводит к неопределенному поведению.

На практике функции из вашей среды выполнения C++ работают вслепую с данными с неправильным макетом, поэтому имеет смысл только фейерверк. В частности, ожидается разбиение стека, поскольку упакованный класс короче распакованного, поэтому запись в конец экземпляра переполняет пространствоmainзарезервированный для него стек стека.

Другие вопросы по тегам