Форум программистов, компьютерный форум CyberForum.ru

C++

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

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

31.08.2008, 03:42. Просмотров 4469. Ответов 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++)
{
. . .
Это говорит о том, что такие ситуации аффектятся в случае использования нескольких переменных, манипулирования их адресами и именами.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
31.08.2008, 03:42     Циклы for. Проблема объявления/инициализации
Посмотрите здесь:

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

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

объявления - C++
в чем отличие такого определения float var = 1.f; от такого float var = 1.0f;

Объявления массива. - C++
Как увеличить количество переменных в массиве?

Объявления классов - C++
Класс может быть объявлен так: class MyClass { ... }; а может быть объявлен и так typedef class

Ошибки объявления - C++
Stack.h #pragma once class Stack { public: void push (StackPtr *,int); int pop(StackPtr*); int isEmpty(StackPtr);

По поводу объявления - Visual C++
Есть форма. При нажатии на кнопку из текстбокса значение сохраняется в динамические список. Где мне вписать объявление списка и его...

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

Цитата Сообщение от дуся Посмотреть сообщение
Sined, слушай, а билдер действительно нужно брать во внимание!!!...Классика, как никак!!!
А насчет совета, возьму во внимание.
дуся
255 / 7 / 1
Регистрация: 20.08.2008
Сообщений: 209
04.09.2008, 10:25     Циклы for. Проблема объявления/инициализации #23
Цитата Сообщение от Sined Посмотреть сообщение
А насчет совета, возьму во внимание.
Вот и замечательно!
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
04.09.2008, 17:20     Циклы for. Проблема объявления/инициализации
Еще ссылки по теме:

объявления друг в друге - C++
как это переделать чтобы работало typedef struct _Info { _proc CallBack; }*_pInfo; typedef _pInfo(*_proc)(); не...

Порядок объявления библиотек? - C++
Начал изучать С++ и столкнулся с такой проблемой: в книгах пишут, что при использовании класса string его надо явно объявлять, но у меня...

Место объявления функции - C++
Не могу сообразить, как определить такой конструктор в структуре(классе) struct Sales_data { string bookNo; unsigned units_sold =...

Синтакасис объявления функций - C++
... double func(double x, double a); int main(int argc, char *argv) ... double func - вещественный тип данных, если не ошибаюсь....

Различие объявления строк - C++
Изучая указатели, дошел до использования в строках. Все было понятно, за исключением нескольких фрагментов кода: Целый код для...


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

Или воспользуйтесь поиском по форуму:
Vourhey
Почетный модератор
6477 / 2252 / 123
Регистрация: 29.07.2006
Сообщений: 12,635
04.09.2008, 17:20  [ТС]     Циклы for. Проблема объявления/инициализации #24
Sined, ладно, нормально все
Закрываю тему. Думаю, что вопрос раскрыт достаточно. Всем спасибо за коменты
Yandex
Объявления
04.09.2008, 17:20     Циклы for. Проблема объявления/инициализации
Закрытая тема Создать тему
Опции темы

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