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

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

Войти
Регистрация
Восстановить пароль
Другие темы раздела
C++ Управление камерой через USB http://www.cyberforum.ru/cpp/thread11657.html
Существует ли библиотека для управления камерой через USB ? (камера Panasonic DVD-юшная)
C++ Чем отличается Visual C++ от Borland C++? мужики у меня тут вопрос я конечно полный нуб еще:eek: а чем отличается Visual C++: от Borland C++ ? вроде ето один и тотже язык программирования http://www.cyberforum.ru/cpp/thread11200.html
Как использовать pascal модуль в TubroC++? C++
Есть модуль, написанный на TurboPascal. Как использовать в приложении на си? Есть возможность сделать из него какую-нибудь библиоткеку, понятную для С? Среда - чистый DOS, не эмуляция.
C++ Как работать с реестром?
Привет! Я должен написать программу, которая не разрешает вставлять дискеты, флэшки, диски в комп. Но подобными вещами пока не занимался. Решил начать с того, что прога будет выдавать сообщение, типа "вы вставили диск..." ну или как-то так... только пока не начал. Если кто-то когда-то писал такое, может что-то посоветуете. Или может подскажете, где хорошо описана работа с реестром, я попробую...
C++ Подскажите сайты с Open Source проектами, в которых можно поучаствовать http://www.cyberforum.ru/cpp/thread10540.html
Люди дайте пожалуйста ссылки на сайты где есть открытые проекты на С++ в которых можно поучаствовать. Чо та гугл молчок).спс.
C++ 16-разрядная подсистема MS-DOS куда надо писать, не знаю, поэтому спрошу тут. ошибка вылазиет не в моей проге, а после того, как последняя скомпилировалась, причем в отдельном окошке: '16-разрядная подсистема MS-DOS Borand c++ for Dos процессор NTVDM обнаружил недопустимую инструкцию. CS:0000 IP:0077 OP:f0 37 05 10 02 Для завершения работы приложения нажмите кнопку "Закрыть".' подробнее

Показать сообщение отдельно
Vourhey
Почетный модератор
6473 / 2248 / 123
Регистрация: 29.07.2006
Сообщений: 12,635

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

31.08.2008, 03:42. Просмотров 4393. Ответов 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++)
{
. . .
Это говорит о том, что такие ситуации аффектятся в случае использования нескольких переменных, манипулирования их адресами и именами.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
 
Текущее время: 17:43. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru