Antonshka Опубликовано 15 мая, 2019 Поделиться Опубликовано 15 мая, 2019 (изменено) Привет всем, знающие люди, помогите понять причину следующей ситуации, отнявшей у меня несколько часов безрезультатного интернет поиска (результаты были, но я из них ничего не понял) Спойлер 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; } Изменено 15 мая, 2019 пользователем Antonshka Ссылка на комментарий Поделиться на другие сайты Поделиться
partoftheworlD Опубликовано 15 мая, 2019 Поделиться Опубликовано 15 мая, 2019 23 минуты назад, Antonshka сказал: - почему таким образом допускается писать код Switch/case из себя представляет обычный goto, а он не умеет создавать область видимости, поэтому надо руками создавать её, а это делается с помощью блока. Ссылка на комментарий Поделиться на другие сайты Поделиться
Antonshka Опубликовано 15 мая, 2019 Автор Поделиться Опубликовано 15 мая, 2019 3 минуты назад, partoftheworlD сказал: Switch/case из себя представляет обычный goto, а он не умеет создавать область видимости, поэтому надо руками создавать её, а это делается с помощью блока. Switch(выражение) { область видимости для Switch } Ты не эту область видимости имел ввиду? Если не трудно, объясни поподробнее то, что ты знаешь об этом вопросе. Я перерыл интернет, был на сайтах, на оф. сайтах, в разных книгах, - нигде нет понятного для меня ответа, но везде говорят как-бы на другом, понятном только для них самих языке. Ссылка на комментарий Поделиться на другие сайты Поделиться
partoftheworlD Опубликовано 15 мая, 2019 Поделиться Опубликовано 15 мая, 2019 1 минуту назад, Antonshka сказал: Если не трудно, объясни поподробнее то, что ты знаешь об этом вопросе. Просто почитай про области видимости и сразу все станет понятно. https://codelessons.ru/cplusplus/oblast-vidimosti-peremennyx-v-c-lokalnye-i-globalnye-peremennye.html Ссылка на комментарий Поделиться на другие сайты Поделиться
Antonshka Опубликовано 15 мая, 2019 Автор Поделиться Опубликовано 15 мая, 2019 (изменено) 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; // Допускается } Изменено 15 мая, 2019 пользователем Antonshka Ссылка на комментарий Поделиться на другие сайты Поделиться
Hack Опубликовано 15 мая, 2019 Поделиться Опубликовано 15 мая, 2019 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 Ссылка на комментарий Поделиться на другие сайты Поделиться
Antonshka Опубликовано 15 мая, 2019 Автор Поделиться Опубликовано 15 мая, 2019 6 минут назад, Hack сказал: Потому-что переменные находятся не в case а в начале switch Начало Switch исполняется всегда? Не зависимо от значения PARAM в выражении Switch(PARAM) ? Если исполняется всегда, и если переменные находятся не в case, но в начале Switch, то почему не выполняется инициализация переменной в этом начале, так же, как выполняется объявление? Почему в начале это выполняется int someval; а это нет int someval = 12345; Подскажи пожалуйста. Ссылка на комментарий Поделиться на другие сайты Поделиться
Hack Опубликовано 15 мая, 2019 Поделиться Опубликовано 15 мая, 2019 Если логично подумать то: Переменные которые ты записываешь в case, переносятся в начало switch и там выделяется стек под эти переменные. int someval = 12345; а так компилятор не понимает Ссылка на комментарий Поделиться на другие сайты Поделиться
partoftheworlD Опубликовано 15 мая, 2019 Поделиться Опубликовано 15 мая, 2019 2 часа назад, Antonshka сказал: Если это так, то это мне теперь понятно. угу именно так. Поэтому либо используешь глобальную область видимости инициализируя переменные вне switch, либо делаешь блоки в кейсах и создаешь локальную область видимости. 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
Antonshka Опубликовано 16 мая, 2019 Автор Поделиться Опубликовано 16 мая, 2019 10 часов назад, Hack сказал: Если логично подумать то: Переменные которые ты записываешь в case, переносятся в начало switch и там выделяется стек под эти переменные. int someval = 12345; а так компилятор не понимает На других сайтах я видел подобные ответы, именно, что так нельзя писать, а вот так можно. И спасибо тебе и им за такие ответы. Но я бы еще хотел понять причину этого разрешения и запрещения. Понять не ради любопытства, а ради самого понимания. Продолжаю искать ответ... Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 16 мая, 2019 Поделиться Опубликовано 16 мая, 2019 Все достаточно просто. При сборке компилятор все инициализированные переменные заталкивает в специальную секцию PE файла (вроде .data, но точно не помню). А тут он натыкается на кейс, и понимает, что в каком-то случае переменная может быть не инициализирована, а для неинициализированных переменных есть другая секция. Компилятор не понимает, куда ему заталкивать такую переменную и потому ругается. Ссылка на комментарий Поделиться на другие сайты Поделиться
Antonshka Опубликовано 16 мая, 2019 Автор Поделиться Опубликовано 16 мая, 2019 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 ? Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 16 мая, 2019 Поделиться Опубликовано 16 мая, 2019 Да, потому что он ее поместит в секцию неинициализированных переменных. И там уже неважно, когда она будет инициализирована. Ссылка на комментарий Поделиться на другие сайты Поделиться
Antonshka Опубликовано 16 мая, 2019 Автор Поделиться Опубликовано 16 мая, 2019 (изменено) 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; //допускается, так как копмилятор точно уверен, в какую секцию определить переменную } Изменено 16 мая, 2019 пользователем Antonshka Ссылка на комментарий Поделиться на другие сайты Поделиться
Hack Опубликовано 16 мая, 2019 Поделиться Опубликовано 16 мая, 2019 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 Ссылка на комментарий Поделиться на другие сайты Поделиться
Antonshka Опубликовано 16 мая, 2019 Автор Поделиться Опубликовано 16 мая, 2019 (изменено) 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; // допускается компилятором. Не однозначности нет } Изменено 16 мая, 2019 пользователем Antonshka Ссылка на комментарий Поделиться на другие сайты Поделиться
Hack Опубликовано 16 мая, 2019 Поделиться Опубликовано 16 мая, 2019 Может это из-за возможной оптимизации case 1: int value = 1 //Может он захочет в регистре только хранить, но ниже есть еще метка, где будет доступна эта переменная. break; case 2: value |= 5; break; Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 16 мая, 2019 Поделиться Опубликовано 16 мая, 2019 Как вариант поискать и смотреть техническую документацию по компилятору или изучать выполнение декомпилятора под дизассемблером. Там должно быть условие, которое выводит ошибку при парсинге строк кода. Если предположить, то сделали так специально, чтобы не было 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/ Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения