Перейти к содержанию

Определение переменной в Switch (c++)


Рекомендуемые сообщения

Привет всем,

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

Спойлер

	int value = 123;
	switch (value)
	{
	case 1: 
		int ival_1 = 1;
	case 2:
		int ival_2 = 1;
	}

 

 

Не понятно почему в case 1 нельзя писать

int ival_1 = 1;

но можно писать

int ival_1;

Объяснение из книги, - "в случае если value не будет равно 1, то будет пропущена метка case 1, следовательно и инициализация переменной ival_1 также будет пропущена."

Но ведь если пропускается метка case 1, то тогда зачем и заботится об инициализации ival_1?

 

Вот что более всего непонятно

- почему в case 1, вместо int ival_1 = 1 допускается написать int ival_1; а затем использовать эту int ival_1 в case 2 (например ival_1 = 456), ведь согласно логике в случае пропуска метки case 1, переменная ival_1 в выражении ival_1 = 456 будет не объявлена прежде.

- почему таким образом допускается писать код

Спойлер

	int value = 123;
	switch (value)
	{
	case 1: 
        {
		int ival_1 = 1;
        }
	case 2:
		int ival_2 = 1;
	}

 







 

Изменено пользователем Antonshka
Ссылка на комментарий
Поделиться на другие сайты

 

23 минуты назад, Antonshka сказал:

- почему таким образом допускается писать код

Switch/case из себя представляет обычный goto, а он не умеет создавать область видимости, поэтому надо руками создавать её, а это делается с помощью блока.

 

 

Ссылка на комментарий
Поделиться на другие сайты

3 минуты назад, partoftheworlD сказал:

 

Switch/case из себя представляет обычный goto, а он не умеет создавать область видимости, поэтому надо руками создавать её, а это делается с помощью блока.

 

 

Switch(выражение)

{

область видимости для Switch

}

Ты не эту область видимости  имел ввиду?

 

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

 

Ссылка на комментарий
Поделиться на другие сайты

1 минуту назад, Antonshka сказал:

Если не трудно, объясни поподробнее то, что ты знаешь об этом вопросе.

Просто почитай про области видимости и сразу все станет понятно.

 

https://codelessons.ru/cplusplus/oblast-vidimosti-peremennyx-v-c-lokalnye-i-globalnye-peremennye.html

Ссылка на комментарий
Поделиться на другие сайты

35 минут назад, partoftheworlD сказал:

Просто почитай про области видимости и сразу все станет понятно.

 

https://codelessons.ru/cplusplus/oblast-vidimosti-peremennyx-v-c-lokalnye-i-globalnye-peremennye.html

Спасибо, просмотрел, это все мне уже известно, но почему-то от этого я все равно не понимаю решения вопроса темы.

 

Вот какие мои мысли пока что

в коде ниже, Switch имеет свою область видимости А, и int ival_1 = 1; имеет свою область видимости Б.

Теперь ival_1 невидима в области А совсем. Это значит, что и проблемы из-за её пропуска также теперь нет совсем.

Switch(value)
{ // Область А начало
case 1: 
        { //Область Б начало
		int ival_1 = 1;
        } //Область Б конец
} // Область А конец

Если это так, то это мне теперь понятно.

Остались непонятны другие вопросы описанные в начале темы.

 

Наример

Спойлер

	int VALUE = 2;
	switch (VALUE)
	{
	case 1: // сase 1 пропускается так как VALUE == 2
		int someval; //Пропускается обьявление someval
	case 2:
		someval = 1; // Почему допускается присваивание, ведь case 1 была пропущена? Как компилятор знает что такое someval?
	}
// Если каким то образом копилятор обьявил someval в пропущенном им case 1, то отчего он не смог тогда и инициализировать
// someval в этом же case 1, в предыдущем примере скрипта? То есть - 

	int VALUE = 2;
	switch (VALUE)
	{
	case 1: // Пропускается
		int someval = 12345; // Пропускается, но копилятором не допускается
	case 2:
		someval = 1; // Допускается
        int anotherint = 6789; // Допускается
	}

 

 

Изменено пользователем Antonshka
Ссылка на комментарий
Поделиться на другие сайты

14 минут назад, Antonshka сказал:

 

  Скрыть контент


	int VALUE = 2;
	switch (VALUE)
	{
	case 1: // сase 1 пропускается так как VALUE == 2
        {
		int someval; //Пропускается обьявление someval
        }
	case 2:
		someval = 1; // Почему допускается присваивание, ведь case 1 была пропущена? Как компилятор знает что такое someval?
	}

 


Потому-что переменные находятся не в case а в начале switch	

switch(value)
{
int a;
int b;
case 1:
break;
}

 

  • Спасибо 1
Ссылка на комментарий
Поделиться на другие сайты

6 минут назад, Hack сказал:

Потому-что переменные находятся не в case а в начале switch	

Начало Switch исполняется всегда? Не зависимо от значения PARAM в выражении Switch(PARAM) ?

 

Если исполняется всегда, и если переменные находятся не в case, но в начале Switch, то почему не выполняется инициализация переменной в этом начале, так же, как выполняется объявление?

 

Почему в начале это выполняется

int someval;

а это нет

int someval = 12345;

Подскажи пожалуйста.

Ссылка на комментарий
Поделиться на другие сайты

Если логично подумать то:

Переменные которые ты записываешь в case,  переносятся в начало switch и там выделяется стек под эти переменные.

int someval = 12345; а так компилятор не понимает

 

Ссылка на комментарий
Поделиться на другие сайты

2 часа назад, Antonshka сказал:

Если это так, то это мне теперь понятно.

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

  • Спасибо 1
Ссылка на комментарий
Поделиться на другие сайты

10 часов назад, Hack сказал:

Если логично подумать то:

Переменные которые ты записываешь в case,  переносятся в начало switch и там выделяется стек под эти переменные.

int someval = 12345; а так компилятор не понимает

 

На других сайтах я видел подобные ответы, именно, что так нельзя писать, а вот так можно. И спасибо тебе и им за такие ответы.

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

 

Продолжаю искать ответ...

 

Ссылка на комментарий
Поделиться на другие сайты

Все достаточно просто. При сборке компилятор все инициализированные переменные заталкивает в специальную секцию PE файла (вроде .data, но точно не помню). А тут он натыкается на кейс, и понимает, что в каком-то случае переменная может быть не инициализирована, а для неинициализированных переменных есть другая секция. Компилятор не понимает, куда ему заталкивать такую переменную и потому ругается.

Ссылка на комментарий
Поделиться на другие сайты

28 минут назад, Xipho сказал:

Все достаточно просто. При сборке компилятор все инициализированные переменные заталкивает в специальную секцию PE файла (вроде .data, но точно не помню). А тут он натыкается на кейс, и понимает, что в каком-то случае переменная может быть не инициализирована, а для неинициализированных переменных есть другая секция. Компилятор не понимает, куда ему заталкивать такую переменную и потому ругается.

 

Значит ли из написанного тобою, что

Спойлер

int VALUE = 2;
	switch (VALUE)
	{
	case 1: // сase 1 пропускается так как VALUE == 2
		int someval; //Пропускается обьявление someval
	case 2:
		someval = 1; // Почему допускается присваивание, ведь case 1 была пропущена? Как компилятор знает что такое someval?
	}

 

компилятору не важно, что пропускается объявление переменной someval в case 1, так как он уже знает что такое someval, ведь он узнал о ней еще во время сборки, и поэтому компилятору допускается в case 2 выполнить присвоение someval = 1 ?

 

 

 

 

 

 

Ссылка на комментарий
Поделиться на другие сайты

43 минуты назад, Xipho сказал:

Да, потому что он ее поместит в секцию неинициализированных переменных. И там уже неважно, когда она будет инициализирована. 

О, спасибо тебе, как же я долго искал этот ответ. Спасибо всем кто откликнулся.

 

Итог сообщенных мне, ваших знаний

- переменные объявляются и переменные инициализируются во время сборки

- переменные во время сборки распределяются на две секции: секция для инициализированных, и секция для не инициализированных.

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

int value = 0;
Switch (value)
{	
  case 1: // возможен пропуск
		int v1 = 123; // неоднозначная ситуация, не допускается. Компилятор придет в замешательство во время сборки, пытаясь определить секцию
		int v2; //допускается такое объявление, так как оно выполняется всегда, во время сборки, независимо от возможного пропуска
  		int v3;
  case 2: // возможен пропуск
	    int v4 = 123; // неоднозначная ситуация, не допускается. Компилятор придет в замешательство во время сборки, пытаясь определить секцию
  		int v5; //допускается такое объявление, так как оно выполняется всегда, во время сборки, независимо от возможного пропуска
  		v2 = 789; // допускается присвоение, так как v2 объявлена выше/раньше, во время сборки
 		int v3; // не допускается, так как в одной области видимости (область Switch в данном случае) нельзя иметь переменные с одинаковым именем
  case 3: // пропуска быть не может, так как это последняя метка оператора Switch, неоднозначности нет
  		int v6 = 123; //допускается, так как копмилятор точно уверен, в какую секцию определить переменную
}

 

 

 

Изменено пользователем Antonshka
Ссылка на комментарий
Поделиться на другие сайты

32 минуты назад, Antonshka сказал:

 

Значит ли из написанного тобою, что

  Скрыть контент


int VALUE = 2;
	switch (VALUE)
	{
	case 1: // сase 1 пропускается так как VALUE == 2
		int someval; //Пропускается обьявление someval
	case 2:
		someval = 1; // Почему допускается присваивание, ведь case 1 была пропущена? Как компилятор знает что такое someval?
	}

 

Компилятор не пропускает объявление переменной, так как он на уровне  switch а не case.

case не является коробкой для объявление переменных.

 

В этом случае в секцию data ничего не заносится, заносится только в text в виде инструкции.

 

mov eax, 1

mov [ebp-14], eax   записывается в стек  someval = 1

Ссылка на комментарий
Поделиться на другие сайты

40 минут назад, Hack сказал:

Компилятор не пропускает объявление переменной, так как он на уровне  switch а не case.

case не является коробкой для объявление переменных.

А, всё, разобрался. Объявление переменной не пропускается.

Так как case не создает здесь свою область, но сам он (case) находится в области switch, то значит и это объявление переменной также находится в области switch и не относится к case.

Значит инициализации и объявления переменных всех case-s (case-s  не имеющих свою собственную область) также находятся в области switch, в начале.

 

А инициализировать переменную в последней метки case оператора switch, допускается потому, что эта метка не может быть пропущена (хотя в тоже время может и не выполниться).

	int value = 123;
	switch (value)
	{
	case 1: 

	case 2:
		int ival_2 = 8; // допускается компилятором. Не однозначности нет
	}

 

Изменено пользователем Antonshka
Ссылка на комментарий
Поделиться на другие сайты

Может это из-за возможной оптимизации

case 1:
int value = 1 //Может он захочет в регистре только хранить, но ниже есть еще метка, где будет доступна эта переменная.
break;
case 2:
value |= 5;
break;
 

Ссылка на комментарий
Поделиться на другие сайты

Как вариант поискать и смотреть техническую документацию по компилятору или изучать выполнение декомпилятора под дизассемблером. Там должно быть условие, которое выводит ошибку при парсинге строк кода.


Если предположить, то сделали так специально, чтобы не было

cmp eax, value
je label1 ; альтернатива "switch/case"
; ...
label1:
sub esp, 4  ; инициализация типа int a = 5
mov [ebp+4], 5

по "протоколу" возможно никогда не делают прыжки на sub esp после прыжка просто так. Это делается, как я предполагаю, в начале функции или в фигурных скобках обозначающих поостранство имен внутри case, или под switch до первого case, или на последнем case... В общем как захотели компилятор написать так и написали. Либо по каким-то причинам техническим, либо по "своим" правилам. А может чтобы упростить разработку и сроки "не дописали", просто ошибку вывели.

 

Статья про switch case под дизассемблером на трех компиляторах. Там, конечно, нет вариантов как в этой теме,но  их можно самостоятельно под отладкой посмотреть
 

Спойлер

https://wasm.in/blogs/identifikacija-switch-case-break.106/

 

Ссылка на комментарий
Поделиться на другие сайты

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.