Предупреждение «может быть забит» на объекте C ++ с помощью setjmp

#include <setjmp.h>
#include <vector>

int main(int argc, char**) {
 std::vector<int> foo(argc);
 jmp_buf env;
 if (setjmp(env)) return 1;
}

Компиляция приведенного выше кода с GCC 4.4.1, g ++ test.cc -Wextra -O1, дает это сбивающее с толку предупреждение:

/usr/include/c++/4.4/bits/stl_vector.h: In function ‘int main(int, char**)’:
/usr/include/c++/4.4/bits/stl_vector.h:1035: warning: variable ‘__first’ might be clobbered by ‘longjmp’ or ‘vfork’

Строка 1035 из stl_vector.h находится в вспомогательной функции используется конструктором vector (n, value), который я вызываю при конструировании foo. Предупреждение исчезает, если компилятор может определить значение аргумента (например, это числовой литерал), поэтому я использую argc в этом тестовом примере, потому что компилятор не может определить значение этого параметра.

Я полагаю, что предупреждение может быть из-за того, что компилятор оптимизирует конструкцию вектора так, чтобы это на самом деле происходило после точки приземления setjmp (что, похоже, имеет место здесь, когда аргумент конструктора зависит от параметра функции).

Как мне избежать этой проблемы, желательно без необходимости разбивать часть setjmp на другую функцию?

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

8
задан Tronic 8 January 2010 в 07:02
поделиться

4 ответа

Правило заключается в том, что любая энергонезависимая, нестатическая локальная переменная в кадре стека, вызывающая setjmp может быть забита вызовом longjmp. Самый простой способ с этим справиться - убедиться, что вызываемый setjmp кадр не содержит таких переменных, о которых вы беспокоитесь. Обычно это можно сделать, поместив setjmp в функцию самостоятельно и передав в ссылках на вещи, которые были объявлены в другой функции, которая не вызывает setjmp:

#include <setjmp.h>
#include <vector>

int wrap_libcall(std::vector<int> &foo)
{
  jmp_buf env;
  // no other local vars
  if (setjmp(env)) return 1;
  // do stuff with your library that might call longjmp
  return 0;
}

int main(int argc, char**) { 
  std::vector<int> foo(argc);
  return wrap_libcall(foo);  
}

Заметьте также, что в этом контексте, clobbering на самом деле означает только сброс к значению, которое он имел при вызове setjmp. Так что если longjmp никогда не может быть вызван после модификации локали, вы тоже в порядке.

Редактирование

Точная цитата из спецификации C99 для setjmp:

Все доступные объекты имеют значения, и все остальные компоненты абстрактной машины имеют состояние, на момент вызова функции longjmp, за исключением того, что значения объекты автоматического хранения длительности, локальные для функции, содержащей в себе вызов соответствующего макроса setjmp, не имеющего типа volatile-qualified и были изменены между вызовом setjmp и вызовом longjmp. неопределенный.

20
ответ дан 5 December 2019 в 06:23
поделиться

Это не предупреждение, которое следует игнорировать, объекты longjmp() и C++ не ладят друг с другом. Проблема в том, что компилятор автоматически вызывает деструктор для вашего объекта foo. Функция longjmp() может обойти вызов деструктора.

Исключения C++ также разматывают кадры стека, но они гарантируют, что будут вызваны деструкторы локальных объектов. Нет такой гарантии от функции longjmp(). Выяснение того, будет ли функция longjmp() выполняться байтом, требует тщательного анализа локальных переменных в каждой функции, которая может быть досрочно прервана из-за функции longjmp(). Это непросто.

5
ответ дан 5 December 2019 в 06:23
поделиться

Как свидетельствуют линейку № 1035 в сообщении об ошибке, ваш фрагмент кода значительно упростил фактический код задачи. Вы зашли слишком далеко. Нет никакого ключи от того, как вы используете «первым». Проблема в том, что компилятор не может понять это даже в реальном коде. Боюсь, что значение «сначала» после ненулевого возврата от «SETJMP», может быть не то, что вы думаете, что это так. Это потому, что вы изменили его значение как до, так и после первого вызова (нулевого возврата) на «SETJMP». Если переменная была сохранена в реестре, значение, вероятно, будет отличаться от того, чтобы он был сохранен в памяти. Таким образом, компилятор является консервативным, давая вам предупреждение.

Чтобы взять слепой прыжок и ответить на вопрос, вы можете избавиться от предупреждающего сообщения, квалифицируя декларацию «первым» с «волатильным». Вы также можете попробовать сделать «первым» глобальным. Возможно, отбросив уровень оптимизации (-O флага), вы можете привести к тому, что компилятор сохраняют переменные в памяти. Это быстрые исправления, и могут на самом деле могут скрыть ошибку.

Вы должны действительно взглянуть на ваш код, и как вы используете «первым». Я возьму еще одно дикое предположение, и скажем, вы сможете устранить эту переменную. Может ли это имя, «сначала», значит, вы используете его, чтобы указать первый звонок (нулевой возврат), чтобы «SETJMP»? Если это так, избавьтесь от него - переправьте свою логику.

Если реальный код только что выходит на ненулевое возвращение из «SETJMP» (как в фрагменте), то значение «First» не имеет значения в этом логическом пути. Не используйте его с обеих сторон «SETJMP».

2
ответ дан 5 December 2019 в 06:23
поделиться

Быстрый ответ: отбросьте флаг -O1 или регресс компилятора на более раннюю версию. Либо один сделал предупреждение исчезнуть в моей системе. Я должен был построить и использовать GCC4.4, чтобы получить предупреждение в первую очередь. (Дарн Это огромная система)

нет? Я не думал не.

Я действительно не понимаю, что все C ++ делает свои объекты, и именно то, как они делится. Тем не менее, комментарий OP, что проблема не возникла, если постоянное значение использовалось вместо «argc» для размера вектора, дает мне возможность придерживаться моей шеи. Я нахожу на себя предположение, что C ++ использует указатель «__First» на DealLocation только тогда, когда первоначальное распределение не является константой. На более высоком уровне оптимизации компилятор использует реестры больше, и существует конфликт между ассигнованиями Pre- и Post-SetJMP ... Я не знаю, это не имеет смысла.

Общее значение этого предупреждения: «Вы уверены, что вы знаете, что вы делаете?» Компилятор не знает, знаете ли вы, какое значение «__First» будет при выполнении LongJMP и получите ненулевое возвращение из «SETJMP». Вопрос в том, является ли его значение после (без нуля) возврата, это значение, которое было помещено в буфер сохранения, или значение, которое вы создали после сохранения. В этом случае он сбивает с толку, потому что вы не знали, что вы используете «__First», и потому, что в такой простой программе нет (явных) изменений на «__First»

Компилятор не может проанализировать логический поток в Комплексная программа, поэтому она, по-видимому, даже не пытается для любой программы. Это позволяет, чтобы вы изменили значение. Так что это просто дает вам дружественные «головы-вверх». Компилятор - это второе, догадайся, пытаясь быть полезным.

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

Отредактируйте 1/21 --------

Мое оправдание (используя G ++ - MP-4.4 -Wextra -O1 Main.cpp):

#include <setjmp.h>
#include <vector>
#include <iostream>

int main(int argc, char**) {
    jmp_buf env;
    int id = -1, idd = -2;

    if ((id=setjmp(env)))
        idd = 1;
    else 
        idd = 0;
    std::cout<<"Start with "<< id << " " << idd <<std::endl;
    std::vector<int> foo(argc );

    if(id != 4)
        longjmp(env, id+1);

    std::cout<<"End with "<< id << " " << idd <<std::endl;
}

Нет предупреждений; A.OUT производится:

Начните с 0 0
Начните с 1 1
Начните с 2 1
Начните с 3 1
Начните с 4 1
Конец с 4 1

-1
ответ дан 5 December 2019 в 06:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: