С Новым годом! Форум программистов, компьютерный форум, киберфорум
Наши страницы

C++

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 36, средняя оценка - 4.78
Vourhey
Почетный модератор
6486 / 2260 / 123
Регистрация: 29.07.2006
Сообщений: 12,536
#1

Циклы for. Проблема объявления/инициализации - C++

31.08.2008, 03:42. Просмотров 4580. Ответов 23
Метки нет (Все метки)

Эх, так и придется создавать новую тему. А то, блин, интересно ж...
Итак, я тут в недавней теме утверждал, что блок инициализации цикла for является внешним по отношению к его внутреннему блоку (блоку команд, то бишь).
CheshireСat's евангелие не соглашается со мной
Цитата Сообщение от CheshireCat Посмотреть сообщение
Сообщение от Vourhey
Просто переменная объявленная в инициализации цикла считается внешней по отношению к самому блоку цикла. То есть, она не внутренняя и не скрытая по отношению ко внешнему от цикла миру.
Гм. Евангелие 14882-2003 (песнь 3.3.2 стих 4) утверждает иное...
и предлагает открыть для этой байды тему. ОК. Открыл.

С чего возник базар? С того, что нужно, все-таки, выяснить чем же особенны такие конструкции (с объявлением переменных в заголовке цикла):
Код
	for (int i = 0; i < 5; i++)
	{
	}
Не долго думая, надо чапать в листинг дизассемблерный. Вся канитель проводилась в VS 2008. Состряпал такой код:
Код
#include <iostream>

using namespace std;

void main()
{
	int i = 1;
	for (int i = 0; i < 10; i++)
	{
	}
}
В листинге видим следующее:
Код
	int i = 1;
0041138E C7 45 F8 01 00 00 00 mov         dword ptr [i],1   инициализируем i
	for (int i = 0; i < 10; i++)
00411395 C7 45 EC 00 00 00 00 mov         dword ptr [i],0   инициализируем i ту, что в заголовке цикла
0041139C EB 09            jmp         main+37h (4113A7h)    проверяем условие выхода из цикла(cmp dword ptr [i],0Ah)
0041139E 8B 45 EC         mov         eax,dword ptr [i]     три следующие команды - инкремент
004113A1 83 C0 01         add         eax,1 
004113A4 89 45 EC         mov         dword ptr [i],eax 
004113A7 83 7D EC 0A      cmp         dword ptr [i],0Ah     проверка условия выхода
004113AB 7D 02            jge         main+3Fh (4113AFh)    уходим из цикла
	{
	}
004113AD EB EF            jmp         main+2Eh (41139Eh)    цикл
Как видно, обе переменные объявлены вне цикла. По байтам команд видно, что цикл работает с последней объявленной "внешней" переменной.
Уберем инициализацию объявление из заголовка.
Код
#include <iostream>

using namespace std;

void main()
{
	int i = 1;
	for (i = 0; i < 10; i++)
	{
	}
}
Дизассемблер скажет следующее:
Код
	int i = 1;
0041138E C7 45 F8 01 00 00 00 mov         dword ptr [i],1 
	for (i = 0; i < 10; i++)
00411395 C7 45 F8 00 00 00 00 mov         dword ptr [i],0 
0041139C EB 09            jmp         main+37h (4113A7h) 
0041139E 8B 45 F8         mov         eax,dword ptr [i] 
004113A1 83 C0 01         add         eax,1 
004113A4 89 45 F8         mov         dword ptr [i],eax 
004113A7 83 7D F8 0A      cmp         dword ptr [i],0Ah 
004113AB 7D 02            jge         main+3Fh (4113AFh) 
	{
	}
004113AD EB EF            jmp         main+2Eh (41139Eh)
Видно, что теперь он работает с единственной внешней переменной i.

Теперь можно провести небольшой эксперимент:
Код
#include <iostream>

using namespace std;

void main()
{
	int i = 1, *j;
	for (i = 0; i < 10; i++)
	{
		j = &i;
	}
	cout<<"J = "<<*j<<"  I = "<<i<<endl;
	cin.get();
}
Из вышенаписанного должно следовать, что внешняя переменная i будет увеличиваться до 10-ти. Соответственно, в j тоже будет десять. Если скомпилить и выполнить, то результат:
J = 10 I = 10.

А теперь засунем новую переменную В БЛОК цикла:
Код
#include <iostream>

using namespace std;

void main()
{
	int i = 1, *j;
	for (i = 0; i < 10; i++)
	{
		int i; i++;
		j = &i;
	}
	cout<<"J = "<<*j<<"  I = "<<i<<endl;
	cin.get();
}
Какой результат? Внутри цикла будет инкрементироваться переменная i десять раз. В итоге у нас получится ожидаемое:
J = 1 I = 10

И последнее:
Код
#include <iostream>

using namespace std;

void main()
{
	int i = 1, *j;
	for (int i = 0; i < 10; i++)
	{
		j = &i;
	}
	cout<<"J = "<<*j<<"  I = "<<i<<endl;
	cin.get();
}
Если я ошибаюсь и int i = 0 у нас происходит внутри блока цикла, то наша переменная i каждую итерацию должна обнуляться... Посмотрим:
J = 10 I = 1

Ах ты батюшки. Не обнулилась. А не сделала она этого потому что объявлена как вне блока цикла. На уровень выше самого блока команд цикла. И в исполнении цикла строка int i = 0 никакого участия не принимает. Абсолютно.
Если совсем скучно, то можно заглянуть в "Бархатный путь С++":
Дело в том, что тело оператора цикла for (оператор или блок операторов) имеет ограниченную область действия имён. А область действия имени, объявленного в операторе-инициализаторе, оказывается шире этой области.

Заголовок цикла for в C++ - центр управления циклом. Здесь следят за внешним миром, за тем, что происходит вне цикла. И потому все обращения к переменным и даже их новые объявления в заголовке цикла относятся к "внешней" области видимости. Следствием такого допущения (его преимущества далеко не очевидны) является правило соотнесения имени, объявленного в заголовке и области его действия.

По отношению к объявлению переменной в заголовке оператора цикла for, правило соотнесения гласит, что область действия имени, объявленного в операторе инициализации цикла for, располагается в блоке, содержащем данный оператор цикла for.
Вообщем, при должной сноровке и "удаче" можно огрести. Очень редко, но можно. Главное знать, что все, что происходит в заголовке цикла (в области инициализации) - это вне зоны действия его блока. И все эти действия не принимают участия в исполнении операторов цикла.

Дабы, это выглядело, как настоящая тема, вопрос: Где я ошибся? Кто, что думает по этому поводу? Может, в объявлении/инициализации в заголовке цикла нет ничего, на что стоило бы обращать внимание?

P. S. вот именно, блин, поэтому такое использование может принести сюрпризы (это ж какую конструкцию надо придумать), если не знать, как оно работает. Я не утверждаю, что так делать нельзя, или плохо. Так делать можно. Ничего страшного в этом нет абсолютно. Просто надо иметь ввиду.

P. P. S. CheshireСat, что на это скажет твое евангелие?

Добавлено через 28 минут 25 секунд
Кстати, как мне сразу в голову не пришло. Если мы уберем int i; внешний и оставим только в заголовке то:
Код
	for (int i = 0; i < 10;i++)
0041138E C7 45 F8 00 00 00 00 mov         dword ptr [i],0 
00411395 EB 09            jmp         main+30h (4113A0h) 
00411397 8B 45 F8         mov         eax,dword ptr [i] 
0041139A 83 C0 01         add         eax,1 
0041139D 89 45 F8         mov         dword ptr [i],eax 
004113A0 83 7D F8 0A      cmp         dword ptr [i],0Ah 
004113A4 7D 02            jge         main+38h (4113A8h) 
	{
	}
004113A6 EB EF            jmp         main+27h (411397h)
Если сравнить со вторым дизассемблерным листингом, то отличий - 0. Только удалилась строка с присваиванием переменной единицы. А так, все абсолютно идентично. Даже адреса те же.
В итоге мы получили ту же внешнюю по отношению к блоку операторов цикла переменную. Даже с аналогичным адресом То есть, на уровне ассемблера код не меняется. Тот же самый листинг получается из:
Код
. . .
int i;
for (i = 0; i < 10;i++)
{
. . .
Это говорит о том, что такие ситуации аффектятся в случае использования нескольких переменных, манипулирования их адресами и именами.
3
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
31.08.2008, 03:42
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Циклы for. Проблема объявления/инициализации (C++):

Синтаксис объявления freind, и компиляторы - C++
Одному так подавай, другому - так подавай. А как же стандарт? Неужели нет единого регламентированного синтаксиса объявления...

Почему для объявления функций порядок не имеет значения? - C++
Добрый день! Вот никак не могу понять почему порядок объявлений функций не строгий, а если функции описывать до ф-и main(), то там...

Избавление от списка инициализации - C++
На днях написал несколько классов с внушительными конструкторами. Наследовавшие от них классы обязаны вызывать эти конструкторы в списках...

Ошибка при инициализации regex_token_iterator - C++
Объясните почему тут ошибка ? #include &lt;iostream&gt; #include &lt;cstdio&gt; #include &lt;regex&gt; #include &lt;iterator&gt; using namespace...

Ошибки множественного объявления идентификатора - C++ Builder
Выдает вот такие ошибки Unit5.h(19): E2238 Multiple declaration for 'TForm3' Unit3.h(13): E2344 Earlier declaration of 'TForm3'

Перевод Pascal в С++, всего 3 объявления функции - C++ Builder
TMatrix=array of real; TCore=array of array of real; TRGB=record i,r,g,b:integer; end; ...

23
Vourhey
Почетный модератор
6486 / 2260 / 123
Регистрация: 29.07.2006
Сообщений: 12,536
03.09.2008, 13:25  [ТС] #16
Что там есть?

Я не пойму, че ты доказать хочешь, но не можешь...
0
Sined
9 / 7 / 2
Регистрация: 30.08.2008
Сообщений: 120
03.09.2008, 13:47 #17
Почему сразу хочу, но не могу.
Ты пишешь по первому листингу дизасемблера, что две переменные являються вне цыкла.

А ты попробуй такой код:

Код
void main()
{
	for(int i=0;i<5;i++)
	{
	}
	printf("%d",i);               [B][COLOR="Red"]//ошибка error C2065: 'i' : undeclared identifier
					//так как инициалезация переменной происходит
 					//внутри блока for[/COLOR][/B]
}
0
CheshireCat
Эксперт С++
2896 / 1245 / 78
Регистрация: 27.05.2008
Сообщений: 3,405
03.09.2008, 13:49 #18
Цитата Сообщение от Vourhey Посмотреть сообщение
Два. У меня наоборот на 2008 не компилится, а в билдере и g++ легко.
P. S. твои споры еще раз доказывают, что стандарта С++ единого нет.
А не путаешь ли ты две разные вещи:
1. существование Стандарта языка как регламентирующего документа - как должно быть,
2. различные реализации этого Стандарта независимыми производителями компиляторов, - что есть на самом деле?

Реализации могут отклоняться от требований Стандарта; насколько мне известно, нет ни одного компилятора, на все 100% соответствующего Стандарту.
0
Sined
9 / 7 / 2
Регистрация: 30.08.2008
Сообщений: 120
03.09.2008, 14:00 #19
Цитата Сообщение от CheshireCat Посмотреть сообщение
2. различные реализации этого Стандарта независимыми производителями компиляторов, - что есть на самом деле?

Так его, так...
Потому как Произвидитель Visual Studio 6 и Visual Studio 2005 один и тот-же, а стандарта нет.
0
Vourhey
Почетный модератор
6486 / 2260 / 123
Регистрация: 29.07.2006
Сообщений: 12,536
03.09.2008, 14:09  [ТС] #20
Цитата Сообщение от Sined Посмотреть сообщение
Почему сразу хочу, но не могу.
Ты пишешь по первому листингу дизасемблера, что две переменные являються вне цыкла.

А ты попробуй такой код:

Код
void main()
{
    for(int i=0;i<5;i++)
    {
    }
    printf("%d",i);               [B][COLOR=Red]//ошибка error C2065: 'i' : undeclared identifier
                    //так как инициалезация переменной происходит
                     //внутри блока for[/COLOR][/B]
}
О боже. Я молчу. Может, тебе выспаться? Читай внимательней выше. Я свое время, на тех, кто не читает, растрачивать не собираюсь.

CheshireCat, нет, я не путаю эти вещи. Единого стандарта С++ нет. Параллельно существуют несколько стандартов. Да короче, че я тут с тобой спорю... Даже, если бы и был, то практика уже все показала и подтвердила, что толку от них. Но его нет.
Есть, например: iso14882:2003/1998. Они существуют параллельно. Я молчу про различные их редакции.
Если ни один компилер на 100% не поддерживает ни один стандарт, то тем более, этот код становится менее безопасным.

Добавлено через 2 минуты 16 секунд
Sined, вот именно, что если нет стандарта единого, этор еще больше уменьшает безопасность приведенного мною кода. Короче, ты тут споришь, а сам ни одного аргумента не привел.
Хочешь настоящий стандарт увидеть? Иди и учи ada. Тогда с тобой поговорим. И ассемблер заодно...
Ты, по-моему, в бронепоезде сидишь...

g++ даже ошибку покажет умнее:
error: name lookup of `i' changed for new ISO `for' scoping
Эх, Sined, если ты на одной студии все гоняешь, то, навряд ли, можешь что-то новое мне открыть
Еще раз повторю, что откомпилированный:
Код
for(int i=1;i<5;i++)
{
      int i = 5;
}
говорит, что это и не внутренняя переменная.

Сейчас основные два стандарта поддерживаются. Никто не отменял ничего.

Так что, "написание по стандарту" в данном случае есть фигня. Так как, и та и эта запись идут по стандарту. Просто в одном случае я буду писать, если знаю, что никому не буду давать свой код, не буду его портировать. В противном случае (если я знаю, что придется портировать, разными компилерами компилить, на разных системах тестить), то мой мозг будет знать, что для данной ситуации, лучше объявить переменную вне цикла. Так я гарантированно задам для нее namespace.
2
дуся
255 / 7 / 1
Регистрация: 20.08.2008
Сообщений: 209
03.09.2008, 16:22 #21
Скажу товарищу спасибо за тему....Вчера среди ночи тупо сидела и пыталась сделать все то, что здесь наваяли(вместо того, чтобы заняться важными делами!)!!!...Последний раз так смеялась с преподом по программингу в универе...Когда все такие умные, все всё умеют, а к единому выводу прийти не могут!!!
Sined, слушай, а билдер действительно нужно брать во внимание!!!...Классика, как никак!!!
0
Sined
9 / 7 / 2
Регистрация: 30.08.2008
Сообщений: 120
04.09.2008, 00:41 #22
Цитата Сообщение от Vourhey Посмотреть сообщение
Эх, Sined, если ты на одной студии все гоняешь, то, навряд ли, можешь что-то новое мне открыть
Ну что я могу сказать, "новечек" одним словом
Я осознаю, что был не прав. И зачем я сюда полез спрашивается!!!

Цитата Сообщение от дуся Посмотреть сообщение
Sined, слушай, а билдер действительно нужно брать во внимание!!!...Классика, как никак!!!
А насчет совета, возьму во внимание.
0
дуся
255 / 7 / 1
Регистрация: 20.08.2008
Сообщений: 209
04.09.2008, 10:25 #23
Цитата Сообщение от Sined Посмотреть сообщение
А насчет совета, возьму во внимание.
Вот и замечательно!
0
Vourhey
Почетный модератор
6486 / 2260 / 123
Регистрация: 29.07.2006
Сообщений: 12,536
04.09.2008, 17:20  [ТС] #24
Sined, ладно, нормально все
Закрываю тему. Думаю, что вопрос раскрыт достаточно. Всем спасибо за коменты
0
04.09.2008, 17:20
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
04.09.2008, 17:20
Привет! Вот еще темы с ответами:

Инициализации ком порта - C++ Builder
Нужно прочитать данные с ком порта, но перед этим его открыть. У нас есть USB RS 485 , при подключении создается какой-то COM порт. Нужно...

Правила объявления инициализации - C++
Напишите основные - самые главные правила обьявления инициализации.

Отличие объявления, определения и инициализации - C++
Здравствуйте, товарищи. Читаю тут книжицу по C++, учусь потихоньку. И возник у меня вопрос нерядового характера: в чём...

Когда выделяется память под переменные - во время объявления или инициализации - C#
Привет! Вопрос такой: когда выделяется память под переменные - во время объявления или инициализации?


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
24
Закрытая тема Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.