2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




 
 crosses initialization
Сообщение08.11.2021, 03:26 
Используется синтаксис C++
#include <iostream>

int main() {
    goto label0;
    int x = 5;
    label0:
    std::cout << &x << '\n';
    return 0;
}
main.cpp:4:10: note: from here
4 | goto label0;
| ^~~~~~
main.cpp:5:9: note: crosses initialization of ‘int x’

Любопытно, что если определить, но не инициализировать переменную $x$, то код скомпилируется нормально, даже без warning-ов:
Используется синтаксис C++
#include <iostream>

int main() {
    goto label0;
    int x; //int x = 5;
    label0:
    x = -10;
    std::cout << x << '\n';
    return 0;
}
Почему так происходит, и нет ли здесь UB ?

 
 
 
 Re: crosses initialization
Сообщение08.11.2021, 08:51 
Что именно здесь любопытно и что такое UB?

 
 
 
 Re: crosses initialization
Сообщение08.11.2021, 08:55 
Аватара пользователя
UB здесь точно есть, в обоих случаях.
Почему в одном случае компилятор выдаёт предупреждение, а в другом нет — вопрос к конкретному компилятору. Разные компиляторы имеют разные настройки по умолчанию, и разные ключи для того, чтобы включать/выключать проверки.


Виноват, прочитал только это:
Qlin писал(а):
если определить, но не инициализировать переменную $x$
Но не это:
Qlin писал(а):
Используется синтаксис C++
    x = -10;
поверил на слово, что переменная $x$ не инициализируется, а она фактически инициализируется.

 
 
 
 Re: crosses initialization
Сообщение08.11.2021, 09:45 
iifat в сообщении #1538183 писал(а):
Что именно здесь любопытно
Что переменной можно присвоить значение, пройдя мимо её объявления и определения.

 
 
 
 Re: crosses initialization
Сообщение08.11.2021, 10:15 
Аватара пользователя
В первом случае код некорректен (ill-formed), во втором корректен.
Стандарт C++, 6.7.3 писал(а):
A program that jumps from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer
(жирный шрифт мой - mihaild)
Причина - у переменной с инициализацией lifetime начинается после инициализации, а в первом случае мы её пропускаем, и соответственно у нас есть видимая с automatic storage duration, у которой не начался lifetime, ни к чему хорошему это привести не может.

 
 
 
 Re: crosses initialization
Сообщение09.11.2021, 00:42 
Аватара пользователя
Qlin в сообщении #1538175 писал(а):
Почему так происходит


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

Кстати, это ограничение распространяется только на объекты с автоматическим классом памяти. На локальные статические объекты оно не распространяется, то есть вы при желании можете таки получить неинициализированный объект со всеми вытекающими

Используется синтаксис C++
#include <iostream>
#include <string>
 
int main()
{
  goto skip;
  static std::string s;
skip:
  std::cout << s[0] << std::endl;
}


Код:
23080 Segmentation fault      (core dumped)


http://coliru.stacked-crooked.com/a/ab0582086fb89c80

---

В С нет такого ограничения на прыжки через инициализацию, но там нет и классов с нетривиальным конструктором, то есть там последствия такого прыжка сведутся лишь к мусору в объектах фундаментальных типов. Однако, эта же логика присутствует в С в несколько другой ипостаси: запрет на прыжки через объявления, связанные с вариабельно-модифицируемыми (variably modified) типами, т.е. через VLA-типы

http://port70.net/~nsz/c/c11/n1570.html#6.8.4.2p2
http://port70.net/~nsz/c/c11/n1570.html#6.8.6.1p1

Используется синтаксис C
int main(void)
{
  int n = 42;
  goto skip;
  typedef char A[n];
  skip:;
}


Такая программа является ошибочной из-за прыжка через вариабельно-модифицируемый typedef

Код:
error: jump into scope of identifier with variably modified type
    4 |   goto skip;

 
 
 
 Re: crosses initialization
Сообщение09.11.2021, 04:07 
worm2 в сообщении #1538184 писал(а):
Qlin писал(а):
Используется синтаксис C++
    x = -10;
поверил на слово, что переменная $x$ не инициализируется, а она фактически инициализируется.
Присваивание является инициализацией ?

 
 
 
 Re: crosses initialization
Сообщение09.11.2021, 04:10 
Аватара пользователя
Qlin в сообщении #1538318 писал(а):
worm2 в сообщении #1538184 писал(а):
Qlin писал(а):
Используется синтаксис C++
    x = -10;
поверил на слово, что переменная $x$ не инициализируется, а она фактически инициализируется.
Присваивание является инициализацией ?


Нет, присваивание, разумеется, не является инициализацией. По каковой причине ошибка пропадает, если вы замените инициализацию на "эквивалентное" присваивание.

 
 
 
 Re: crosses initialization
Сообщение09.11.2021, 04:17 
Я верно понимаю, что переходы по меткам, условия, switch+case инструкции не влияют на объявление и определение переменных ? То есть они в любом случае будут объявлены, и их можно использовать с точки объявления до конца блока ?

 
 
 
 Re: crosses initialization
Сообщение09.11.2021, 04:48 
Аватара пользователя
Qlin в сообщении #1538320 писал(а):
Я верно понимаю, что переходы по меткам, условия, switch+case инструкции не влияют на объявление и определение переменных ? То есть они в любом случае будут объявлены, и их можно использовать с точки объявления до конца блока ?


Ну в С и С++ всегда было так: локальная переменная видна от точки объявления до конца своего блока. Метки на это никак не влияют.

А дальше начинаются вопросы времени жизни, которые в С и С++ отличаются.

В языке С локальная переменная (кроме VLA) всегда начинает свою жизнь с самого начала блока. Неважно, где в блоке она объявлена, но существует она сразу с самого начала блока. Вы просто не видите ее имени до той точки, в которой она объявлена.

В языке С++, если я ничего не упускаю в современных формулировках, время жизни не может распространяться по блоку назад, выше точки объявления.

Используется синтаксис C
int main(void)
{
  int *p = 0;

back:
  if (p != 0)
  {
    printf("%d\n", *p); // В С - все в порядке, в С++ - UB
    return 0;
  }

  int a = 42;
  p = &a;
  goto back;
}

 
 
 [ Сообщений: 10 ] 


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group