2014 dxdy logo

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

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




Начать новую тему Ответить на тему
 
 crosses initialization
Сообщение08.11.2021, 03:26 


06/04/18

323
Используется синтаксис 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 
Заслуженный участник


16/02/13
4194
Владивосток
Что именно здесь любопытно и что такое UB?

 Профиль  
                  
 
 Re: crosses initialization
Сообщение08.11.2021, 08:55 
Заслуженный участник
Аватара пользователя


01/08/06
3127
Уфа
UB здесь точно есть, в обоих случаях.
Почему в одном случае компилятор выдаёт предупреждение, а в другом нет — вопрос к конкретному компилятору. Разные компиляторы имеют разные настройки по умолчанию, и разные ключи для того, чтобы включать/выключать проверки.


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

 Профиль  
                  
 
 Re: crosses initialization
Сообщение08.11.2021, 09:45 


06/04/18

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

 Профиль  
                  
 
 Re: crosses initialization
Сообщение08.11.2021, 10:15 
Заслуженный участник
Аватара пользователя


16/07/14
9143
Цюрих
В первом случае код некорректен (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 
Аватара пользователя


28/10/21
100
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 


06/04/18

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

 Профиль  
                  
 
 Re: crosses initialization
Сообщение09.11.2021, 04:10 
Аватара пользователя


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


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

 Профиль  
                  
 
 Re: crosses initialization
Сообщение09.11.2021, 04:17 


06/04/18

323
Я верно понимаю, что переходы по меткам, условия, switch+case инструкции не влияют на объявление и определение переменных ? То есть они в любом случае будут объявлены, и их можно использовать с точки объявления до конца блока ?

 Профиль  
                  
 
 Re: crosses initialization
Сообщение09.11.2021, 04:48 
Аватара пользователя


28/10/21
100
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 ] 

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group