Результаты поиска
| |
nilrem | Дата: Воскресенье, 15 Ноября 2009, 17:12 | Сообщение # 941 | Тема: Курс : "Основы С++ для начинающих программистов игр." |
Просветленный разум
Сейчас нет на сайте
| Урок 5. Манипуляция с битами Думаю, раз биты такие важные, что с них мы начали наш урок, нужно познакомится с ними еще ближе. В языке С++ есть несколько операторов, с помощью которых можно проводить различные действия над битами: ~ Битовое Не. Меняет значение бита на противоположное. Вообще, если сравнить 1 с логическим true, а ноль с логическим false (как оно в общем-то и есть) здесь можно использовать аналогию с логическими операторами. Помните пример: Code !true возвратит false !false возвратит true Ну а вот битовый аналог: Code ~1 (true) возвратит 0 (false) ~0 (false) возвратит 1 (true) & Битовое И. Как и логический вариант, этот оператор возвращает 1 если оба аргумента равны единице, во всех остальных случаях 0. Code 1 (true) & 1 (true) возвратит 1 (true) 1 (true) & 0 (false) возвратит 0 (false) 0 (false) & 0 (false) возвратит 0 (false) Битовый & поочередно применяется ко всем битам числа. Вот как это работает: 11010 & 10101 равно 10000 Немного отформатируем, чтоб было понятнее: 11010 & 10101 равно 10000 Если перевести все в привычную нам десятичную систему, то будет так: 26 & 21 = 16 Вот такая вот арифметика) Воплотим ее в код: Code #include <iostream> using namespace std;
void main() { cout<< (26 & 21); cin.get(); } | Битовое ИЛИ. И опять знакомая ситуация. Если один из аргументов равен 1, то 1 и будет возвращено . Code 1 (true) | 1 (true) возвратит 1 (true) 1 (true) | 0 (false) возвратит 1 (true) 0 (false) | 0 (false) возвратит 0 (false) Пример: 11010 | 10101 равно 11111 26 | 21 = 31 Измените предыдущий пример и убедитесь, что я прав. ^ Исключающее ИЛИ. А вот это нечто новое. Такого мы еще не видели. Оператор ИЛИ возвращает единицу, только если один(только один) из аргументов равен 1. Code 1 (true) ^ 1 (true) возвратит 0 (false) 1 (true) ^ 0 (false) возвратит 1 (true) 0 (false) ^ 1 (true) возвратит 1 (true) 0 (false) ^ 0 (false) возвратит 0 (false) Пример: 11010 ^ 10101 равно 01111 В привычном нам, десятичном, виде: 26 ^ 21 = 15 Операторы сдвига Помимо логических, для работы с битами также используются операторы сдвига. Это уже знакомые нам << и >>. Они сдвигают все биты числа соответственно влево или вправо, заполняя освободившиеся нолем. Синтаксис использования этих операторов выглядит так: переменная << число битов переменная >> число битов здесь число битов, это значение, означающее на сколько нужно произвести сдвиг. Пример: 01011010 << 1 даст 10110100 а 01011010 >> 1 даст 00101101 Отлично, сдвигает. Нам то что с того. Ну например использование комбинация битовых операторов позволяет узнавать или изменять значение каждого бита на свое усмотрение. Для этого придется немного напрячь мозг. Чтобы понять приведенные далее коротенькие примеры необходимо знать то, что я излагал выше. Проверка бита Итак, в первую очередь разберемся, как узнать значение конкретного бита переменной v. Для этого используется вот такое очень простое выражение: Code (v & (1 <<n))? Бит = 1: Бит = 0; Эта запись, если вы помните прошлые уроки, использует условный оператор и может быть записана следующим образом: Code if(v & (1 << n)) { Бит = 1; } else { Бит = 0; } Где: v - переменная, n - номер бита. Работает оно так. Сначала с помощью оператора сдвига создается переменная, в которой интересующий нас бит равен 1. Для этого переменная 1 имеющая двоичное представление 00000001, сдвигается на нужное количество битов влево. Допустим нам нужно узнать значение второго бита. Вспомним, что биты нумеруются от ноля. (1 << 2) 00000001 << 2 Результат сдвига 00000100 Далее применяем битовый И. Если у переменной второй бит 1 то в результате выражения: Code 11111111 & 00000100 Получим
00000100 то есть (true) Если же второй бит равен нолю то: Code 11111011 & 00000100 Получим
00000000 то есть (false) Ну а дальше в действие вступает условный оператор, который определяет правдивое выражение или ложное и присваивает соответствующую цифру. Далее я продемонстрирую как с помощью описанного выше способа выполнить преобразование из десятичной системы счисления в двоичную. Code #include <iostream> using namespace std;
void main() { setlocale(0,""); cout<<"введите число от 0 до 255 "; int i; cin>>i; for(int n=7;n>=0;n--) { cout<<(i & (1 << n) ? 1 : 0); } cin.get(); cin.get(); } Намного проще, не правда ли. Если кому-то непонятно, замените строку: Code cout<<(i & (1 << n) ? 1 : 0); на: Code if(i & (1 << n)) { cout<<1; } else { cout<<0; } Теперь дошло? Установка бита в 1 Проверять биты мы научились. Менять их значение еще проще. Здесь даже условный оператор не нужен. Установка бита в 1 выполняется с помощью такого выражения: Здесь, как и в прошлом случае используется сдвиг для получения переменной, в которой значение конкретного бита 1, остальных 0. Далее используется битовый оператор ИЛИ. Если у переменной второй бит и так 1 то в результате выражения: 11111111 | 00000100 Получим 11111111 Если же второй бит равен нолю то: 000000000 | 00000100 Получим 00000100 Далее результат присваивается исходной переменной. Пример кода: Code #include <iostream> using namespace std;
void main() { setlocale(0,""); int i=0; cout<<"i = "<<i<<" = "; for(int n=7;n>=0;n--) { cout<<(i & (1 << n) ? 1 : 0); }
i |= (1 << 0); // устанавливаем нулевой бит в 1 cout<<"\nустанавливаем нулевой бит в 1\n"; cout<<"i = "<<i<<" = "; for(int n=7;n>=0;n--) { cout<<(i & (1 << n) ? 1 : 0); }
i |= (1 << 7); // устанавливаем 7 бит в 1 cout<<"\nустанавливаем 7 бит в 1\n"; cout<<"i = "<<i<<" = "; for(int n=7;n>=0;n--) { cout<<(i & (1 << n) ? 1 : 0); }
cin.get(); cin.get(); } Здесь, вместо строки у меня записано Для сокращения записи я воспользовался составным присваиванием |=. Установка бита в 0 А теперь обнулим бит с помощью: (Здесь тоже составное присваивание &=) И вновь используется сдвиг, для получения переменной, в которой значение конкретного бита 1, остальных 0. Далее с помощью битового НЕ значение всех битов меняется на противоположное. ~00000100 дает 11111011 Затем, как и в случае проверки, используется битовый И. Если у переменной второй бит 1 то в результате выражения: 01010101 & 11111011 Получим 01010001 Если же второй бит уже равен нолю, то там и менять нечего: 10101010 & 11111011 Получим 10101010 Этот код нужно добавить в приведенный выше пример перед cin.get();. Code i &= ~(1 << 0); // обнуляем нулевой бит cout<<"\nобнуляем нулевой бит\n"; cout<<"i = "<<i<<" = "; for(int n=7;n>=0;n--) { cout<<(i & (1 << n) ? 1 : 0); }
i &= ~(1 << 7); // обнуляем 7 бит cout<<"\nобнуляем 7 бит\n"; cout<<"i = "<<i<<" = "; for(int n=7;n>=0;n--) { cout<<(i & (1 << n) ? 1 : 0); } Вот мы и научились управлять данными на таком тонком уровне как биты. Для полного счастья нам не хватает только добраться до памяти, а конкретно до тех ячеек, в которых вместе со своими битами хранятся переменные. Чем-то подобным мы далее и займемся. Урок 6. Указатели На этой лекции я решил подвести черту под типами данных. Поэтому настала очередь рассказать еще об одном – об указателях. Этот тип данных является чем то вроде камня преткновения в языке программирования С++. Благодаря указателям программы приобретают необычайную гибкость и непревзойденную скорость, но благодаря им же появляются многочисленные серьезные, трудно ловимые баги и ошибки. Итак, указатель это переменная, хранящая в себе адрес другой переменной. Чтобы понять, что я имею ввиду нужно знать, что значит адрес переменной. Адрес Как вы уже знаете, память компьютера состоит из битов, которые в свою очередь собраны в байты. У каждого байта имеется свой адрес, представленный в виде шестнадцатеричного числа. Каждая переменная занимает в памяти от одного до нескольких байтов. Так вот, адресом переменной становится адрес ее первого байта. Чтобы получить адрес переменной используется оператор взятия адреса - &. Этот оператор нам уже попадался под видом битового И. Теперь же с его помощью мы будем получать адрес. Действие, которое этот оператор выполняет, зависит от способа его записи. Если он стоит между двумя переменными то это И, если он стоит перед одной переменной то это оператор взятия адреса. Операторы которые применяются всего к одной переменной называются унарными. Использование указателей. Как получить адрес, мы уже знаем. Теперь познакомимся с синтаксисом объявления указателя. Code тип_данных *имя_переменной Здесь ключевую роль играет унарный оператор звездочка *. С помощью которого переменная и становится указателем. Как и при объявлении других переменных, здесь также есть тип данных. Здесь указывается тип данных переменной, на которую ссылается указатель. Пример Code int i=1564; int *pi; // обьявление указателя pi = & i; // взятие адреса пример можно сократить Code int i=1564; int *pi= & i; // обьявление указателя и взятие адреса Здесь в имени указатели, согласно венгерской нотации, используется префикс p. Чтобы не запутаться в своей программе и знать что переменная это указатель, советую вам использовать его всегда. Теперь напишем небольшую программу, которая выдаст нам адреса нескольких переменных. Code #include <iostream> using namespace std;
void main() { setlocale(0,""); int s=50; int i=456; double d=4.1456;
int *ps = &s; int *pi = &i; double *pd = &d;
cout<<"адрес переменной s "<<ps<<'\n'; cout<<"адрес переменной i "<<pi<<'\n'; cout<<"адрес переменной d "<<pd<<'\n';
cin.get(); cin.get();
} Хорошо, объявлять и присваивать адрес указателю мы научились. Но как получить значение, хранящееся по определенному адресу. Для этих целей используется оператор разыменования указателя, и это все та же звездочка *. Если в предыдущем примере в строках отображения адреса добавить звездочки, программа вместо адреса напечатает значения. Добавьте этот код в предыдущий пример: Code cout<<"\nзначение переменной по указателю ps "<<*ps<<'\n'; cout<<"значение переменной по указателю pi "<<*pi<<'\n'; cout<<"значение переменной по указателю pd "<<*pd<<'\n'; Все просто. С помощью разыменования указателя можно не только получить, но и изменить значение переменной. Делается это так: Code int i=456; int *pi = &i; *pi = 30; Понять запись можно так. Вначале с помощью звездочки мы обращаемся к значению и присваиваем ей новое. Пример кода будет дальше. Теперь я приведу пример неосторожного использования указателей. Вот что случится, если случайно, посредством указателей, присвоить переменной с типом int значение с типом double: Code #include <iostream> using namespace std;
void main() { setlocale(0,""); int s=50; int i=456; int a=785;
cout<<"s "<<s<<'\n'; cout<<"i "<<i<<'\n'; cout<<"a "<<a<<'\n';
double *pd = (double*)&i; *pd = 13.4568;
cout<<"s "<<s<<'\n'; cout<<"i "<<i<<'\n'; cout<<"a "<<a<<'\n';
cin.get(); cin.get(); } Здесь Присваивание значения через указатель. В этой программе есть непонятная нам строка. Code double *pd = (double*)&i; а конкретно (double*)&i. Подобная запись называется явным преобразованием типов. Указав при присваивании перед переменной в скобках новый тип, мы получим значение указанного типа. Пример: Code int i = (int)3.14; // преобразовываем float в int int i = (int)'a'; // char в int char c = (char)100; // и наоборот В некоторых случаях программа может делать преобразования самостоятельно (неявное преобразование). А иногда, как и в примере с указателями, это нужно делать вручную, иначе компилятор выдает ошибку. Но вернемся к нашему примеру, что же будет в результате присваивания int значения double. Первое - значение переменной будет некорректным. Второе – при работе программы возникнет ошибка, связанная с порчей памяти, как и в примере с массивами. Что ж, думаю на этом со знакомством с указателями можно завязывать. Но мы к ним еще не раз вернемся. Позже мы узнаем, зачем же они нужны и сможем раскрыть всю их мощь. Послесловие Ну и как обычно пару слов на конец. Лекция была сложной, и содержала много новой информации. Поэтому вам еще не раз придется к ней возвращаться. Большая часть сведений вам пока не понадобится, разве что для общего понимания. Преобразования систем счисления, манипуляция с битам нужна только в особых случаях. А вот знания про массивы и указатели будут нужны постоянно. Я надеюсь, что вы прочитали и поняли все, но если что-то пропустили или не совсем поняли, ничего страшного. Можно прочитать и пройтись по примерах еще раз, при этом особое внимание уделяя как раз массивам и указателям. Ну и если что, спрашивайте. На этом все, до встречи. ЗЫ: Я надеюсь, что вас не напугали возникшие сложности, и мы еще встретимся. Ведь так?
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Воскресенье, 15 Ноября 2009, 16:58 | Сообщение # 942 | Тема: Курс : "Основы С++ для начинающих программистов игр." |
Просветленный разум
Сейчас нет на сайте
| Урок 3. Размер данных и диапазон значений. Модификаторы типа. Как мы уже знаем байт состоит из восьми битов. Поскольку количество этих самых битов небольшое, то и, естественно, что записать с их помощью много чисел не получится. Поэтому размер переменных увеличивают до нескольких байтов. При этом занимаемое ими в памяти место увеличивается. Теперь нам необходимо возвратится к уже изученным типам данных и узнать о них то, о чем я раньше умолчал. Сейчас я расскажу, сколько каждый тип данных занимает места в памяти и в каком диапазоне он может принимать значение. Но прежде чем это делать, нужно объяснить еще один момент. Из школьных уроков математики вы должны знать, что существую положительные и отрицательные числа. В языке С++ такие числа относятся к знаковым, то есть предполагается что перед числом стоит плюс или минус. Ну и поскольку есть знаковые, то можно предположить что есть и беззнаковые, То есть всегда положительные, почему так, поймете дальше. Такие числа имеют отдельное обозначение - unsigned. Это ключевое слово ставится при объявлении переменной перед типом. Знаковые переменные тоже имеют обозначение - signed, но поскольку переменная по умолчанию считается знаковой, то его можно не использовать. Пример: а запись: идентична: ПРЕДУПРЕЖДЕНИЕ!!! Применение unsigned к float или double превращает тип переменной в int. Ключевые слова signed и unsigned называются модификаторы(или спецификаторы) типа. Кроме названных есть еще несколько спецификаторов. Например, ключевые слова short и long которые применяются по отношению к целочисленному типу и увеличивают или уменьшают диапазон значений ну и размер. Теперь я приведу небольшую таблицу в которой представлены типы даны, занимаемый ими размер и диапазон значений. Стоп. Лучше мы напишем для этого программу. Чтобы это сделать, нам необходимо узнать, как получить размер переменной определенного типа и как вычислить диапазон. С размером все просто. В языке С++ есть специальная функция, которая умеет это делать – sizeof(). Ну а диапазон нам придется определять вручную, с помощью цикла. Помните, как мы это делали для генератора случайных чисел. Здесь будет нечто подобное, но мы воспользуемся интересным свойством переменных. Допустим, переменная имеет диапазон от 0 до 255. И сейчас ей присвоено максимальное значение 255. Как вы думаете, что будет, если прибавить к нему единицу? Значение переменной изменится на 0. Переменная будто закольцована сама на себя, и за пределы допустимого значения никогда не выйдет. Так и будет изменяться по кругу, в ту или иную сторону. Закольцованность работает в обе стороны, то есть если переменная равна нолю и отнять единицу, то значение станет 255. В общем вот код: Code #include <iostream>
using namespace std;
void main () { setlocale(0,"");
bool b; char c; int i=0; short int si=0; long int li=0; unsigned int ui=0; unsigned short int usi=0; unsigned long int uli=0; float f=0; long float lf=0; double d=0; long double ld=0;
cout<<"\tТип данных\t\t"<<"Размер\t"<<"Диапазон значений\n\n";
cout<<"\t"<<"bool"<<"\t\t\t"<<sizeof(b)<<"\n"; cout<<"\t"<<"char"<<"\t\t\t"<<sizeof(c)<<"\n"; int min=0; int max=0; do { ++i; if(i>max) max=i; else { if(i<min) min=i; } } while(i!=0); cout<<"\t"<<"int"<<"\t\t\t"<<sizeof(i)<<"\t"<<min<<" ... "<<max<<"\n"; min=0; max=0; do { ++si; if(si>max) max=si; else { if(si<min) min=si; } } while(si!=0); cout<<"\t"<<"short int"<<"\t\t"<<sizeof(si)<<"\t"<<min<<" ... "<<max<<"\n";
long int lmin=0; long int lmax=0; do { ++li; if(li>lmax) lmax=li; else { if(li<lmin) lmin=li; } } while(li!=0); cout<<"\t"<<"long int"<<"\t\t"<<sizeof(li)<<"\t"<<lmin<<" ... "<<lmax<<"\n";
unsigned int uimin=0; unsigned int uimax=0; do { ++ui; if(ui>uimax) uimax=ui; else { if(ui<uimin) uimin=ui; } } while(ui!=0); cout<<"\t"<<"unsigned int"<<"\t\t"<<sizeof(ui)<<"\t"<<uimin<<" ... "<<uimax<<"\n";
unsigned short int usimin=0; unsigned short int usimax=0; do { ++usi; if(usi>usimax) usimax=usi; else { if(usi<usimin) usimin=usi; } } while(usi!=0); cout<<"\t"<<"unsigned short int"<<"\t"<<sizeof(usi)<<"\t"<<usimin<<" ... "<<usimax<<"\n";
unsigned long int ulimin=0; unsigned long int ulimax=0; do { ++uli; if(uli>ulimax) ulimax=uli; else { if(uli<ulimin) ulimin=uli; } } while(uli!=0); cout<<"\t"<<"unsigned long int"<<"\t"<<sizeof(uli)<<"\t"<<ulimin<<" ... "<<ulimax<<"\n";
cout<<"\t"<<"float"<<"\t\t\t"<<sizeof(f)<<"\n"; cout<<"\t"<<"long float"<<"\t\t"<<sizeof(lf)<<"\n"; cout<<"\t"<<"double"<<"\t\t\t"<<sizeof(d)<<"\n"; cout<<"\t"<<"long double"<<"\t\t"<<sizeof(ld)<<"\n";
cin.get();
}
Главную работу здесь выполняет вот этот кусочек, который повторяется несколько раз с некоторыми изменениями, в основном касающимися типа данных: Code int min=0; int max=0; do { ++i; if(i>max) max=i; else { if(i<min) min=i; } } while(i!=0); cout<<"\t"<<"int"<<"\t\t\t"<<sizeof(i)<<"\t"<<min<<" ... "<<max<<"\n";
Работает он как и в пример с генератором случайных чисел. Цикл начинается с нуля и завершается когда будет пройден круг и переменная вновь станет равна нолю. Тип переменных, в которых хранится минимальное и максимальное значение, должно соответствовать проверяемому типу, иначе оно может туда не поместится. Ну а в последней строке происходит отображение результата, в ней же с помощью sizeof вычисляется размер переменной в байтах. Вот такая табличка получится в результате работы нашей программы: Считать диапазон float и других последующих типов я не стал. Программа и так работает несколько минут, а тронь я float, работала бы несколько дней. Дело в том, что при одинаковом размере с типом int, float имеет диапазон от -1.0Е+37 до 1.0Е+37.(Если кто не знает что это значит, объясняю. +37 означает что нужно передвинуть точку вправо (от единицы) на тридцать сем знаков, подставив ноли. В результате получится число в котором 38 цифр. (Как его назвать я даже не знаю. Знаю есть квинтиллион, вроде 18 нолей, так ведь и это очень мало) Куда тут int с его 4 миллиардами. Ладно. Как видно из получившейся у нас таблицы, некоторые типы данных при одинаковом размере отличаются диапазоном значений. В первую очередь это касается знаковых и беззнаковых. Сейчас я объясню, почему так происходит. Если число имеет определенный знак, не важно, плюс это или минус, то естественно, что этот знак должен где-то храниться. В переменной под знак резервируется крайний левый бит, также называемый старшим битом. Если он равен 0 то переменная положительная(знак + ), если 1 то отрицательная(знак -). Потеря всего одного бита уменьшает значение переменной в два раза. Она как бы делится пополам - одна половина для положительных, другая - для отрицательных чисел, что вы и можете видеть. Упомянув о спецификаторах типа не возможно не рассказать еще об одном – const. Объявленную с его помощью переменную невозможно изменить. Такую переменную называют константой, и используют для хранения определенных постоянных значений. При объявлении константы ей нужно сразу же присваивать значение. Пример: Если где-то далее в коде вы попытаетесь ее изменить, компилятор сразу же сообщит об ошибке. Если вы не присвоите константе значения сразу, а попытаетесь сделать это позже, опять же получите ошибку. Так делать нельзя: Code const int p; p = 3,14; // ошибка. Изменить константу нельзя. Урок 4. Массивы Сегодняшняя лекция полностью посвящена данным, поэтому сейчас я расскажу еще об одном их типе. Хотя чисто технически это не самостоятельный тип, а способ представления уже известных. Очень часто программы содержат множество, тысячи и миллионы, переменных. Чтобы хоть как-то облегчить жизнь программистам некоторые близкие по смыслу переменные можно объединять в последовательности. Такую последовательность называют массивом. Одномерный массив Одномерный массив это список, последовательность близких по смыслу переменных. Как и любые другие типы данных, массив также нужно объявлять. Синтаксис объявления такой: Тип_данных имя_массива[размер массива]; Здесь: Тип данных может быть любым известным вам типом. Ограничений нет. Имя массива также может быть любым, главное чтобы оно соответствовало описанным ранее требованиям к именам. Размер массива – здесь внутри квадратных скобок указывается, сколько именно переменных будет содержаться в массиве. Каждая такая переменная называется элементом массива. Теперь, зная синтаксис, объявим массив, состоящий из 10 элементов типа int: Доступ к конкретному элементу массива выполняется с помощью его индекса – порядкового номера. Для этого просто указываем индекс в квадратных скобках: Здесь мы обратились к элементу с индексом пять. Но не к пятому по порядку. Важно запомнить что отсчет в языке программах на языке С++ начинается с 0. То есть самый первый элемент имеет индекс 0, второй – 1. И так далее. Так что в нашем массиве с 10 переменными, индекс последней переменной будет 9. Инициализация массива Свежесозданный массив является пустым. Значение, которое содержат его элементы непредсказуемо и может быть каким угодно в диапазоне объявленного типа данных. Поскольку пустые массивы, с содержащими мусор элементами, никому не нужны, их нужно как то заполнить. Этот процесс называется инициализацией массива. Инициализировать массив можно несколькими способами. Например, можно просто присваивать значение каждому элементу. Code ar[0] = 10; ar[3] = 17; Если всем элементам массива нужно присвоить определенное значении или последовательность значений, то это удобно делать в цикле. Вот пример кода: Code #include <iostream> using namespace std;
void main() { setlocale(0,""); // подобная запись устанавливает в консоли язык, используемый в системе по умолчанию. int ar[10]; // обьявляем масив for(int i=0;i<10;i++) { ar[i]=0; // присваиваем значение элементам } for(int i=0;i<10;i++) // цикл, воторый выводит значение елементов { cout<<"Элемент ar["<<i<<"] = "<<ar[i]<<'\n'; } cin.get(); } Как он работает, видно из комментариев. Есть еще один способ инициализации массива. Он используется во время его объявления. Вот синтаксис: Тип_данных имя_массива[размер массива] = {список_значений}; Code #include <iostream> using namespace std;
void main() { setlocale(0,""); int ar[10]={1,2,3,4,5,6,7,8,9,10}; // обьявляем и инициализируем массив for(int i=0;i<10;i++) { cout<<"Элемент ar["<<i<<"] = "<<ar[i]<<'\n'; } cin.get(); } Количество значений в строке {1,2,3,4,5,6,7,8,9,10} желательно, но не обязательно должна соответствовать размеру массива. Кстати, в целях экономии места подобным способом можно объявить массив, не указывая его размер. Code Тип_данных имя_массива[ ] = {список_значений}; В таком случае размер массива будет равен количеству значений. То есть в результате объявления: будет создан массив, содержащий три элемента. Таким способом часто пользуются при объявлении массивов символов, или по-другому строк. Code [b]Массив символов[/b] Строка это одномерный массив, каждый элемент которого относится к типу данных char, а последний символ нулевой (‘\0’) . image004.png Объявлять символьный массив можно как любой другой массив, например, так: Code char str[7]={'q','w','e','r','t','y','\0'}; Чтобы не заморачиваться с нулевым символом и размером лучше всего использовать следующий способ: здесь размер задается сам, а благодаря использованию кавычек нулевой символ подставляется автоматически. Кстати этот самый нулевой символ используется для того, чтобы программа знала где строка заканчивается. То есть чтобы при печатании строки Code cout << "чтобы программа знала, что остановится нужно после слова здесь"; Я проиллюстрирую это примером: Code #include <iostream> using namespace std;
void main() { setlocale(0,""); char str[]="qwerty"; // обьявляем и инициализируем массив int i=0; while(str[i]!='\0') // пока это не нулевой символ { cout<<str[i++]<<'\n'; } cin.get(); } Здесь в цикле печатаются символы, до тех пор, пока не будет достигнут нулевой символ. Приблизительно так и работает cout. Раньше мы вводили к компьютер только числа, сейчас же настало время научиться вводить текст. Делается это с помощью все того же оператора cin. Просто необходимо создать соответствующую переменную, то есть массив символов, и передать ей ввод. Code #include <iostream> using namespace std;
void main() { setlocale(0,""); char str[50]; // обьявляем массив cin>>str; // считываем строку cout<<"вы ввели: "<<str; //отображение
cin.get(); cin.get(); } Заметьте, при передаче ввода указывается только имя массива без использования квадратных скобок. То же самое и при выводе с помощью cout. И еще количество введенных символов не должно превышать заявленный размер массива. Сейчас поясню, почему. Элементы массива размещаются в памяти компьютера друг за другом в смежных ячейках, то есть так, как это показано выше на рисунке. Если количество символов окажется больше, чем размер массива, программа без зазрения совести запишет их последующие ячейки. При это данные, которые были записаны в них ранее будут безвозвратно утеряны. В результате произойдет сбой в работе программы. Так что будьте внимательны. Создавая массив, заранее просчитывайте, сколько места понадобится. Описанная здесь проблема касается не только строк, это проблема любых массивов вообще. Многомерный массив Помимо простых одномерных массивов существуют и многомерный. Если одномерный можно сравнить с несколькими коробками, содержащими определенные предметы-значения, то многомерный массив это коробки в которых содержатся другие коробки. И так далее. Объявляются они следующим образом: Code Тип_данных имя_массива[размер_1] [размер_2]… [размер_N]; Пример: Code float map[10][10] int terra[5][50][300]; char text[100][2][5][500][3][15654][45][10][15]; Мерность массива может быть сколько угодной, но чаще всего используются двухмерные. Их я и буду использовать для примера. Зачем же могут понадобится многомерные массивы. Примеров на самом деле можно придумать сколько угодно, но я остановлюсь на том, что ближе всего к разработке игр. Допустим, есть следующий массив: Вот как его можно представить графически: На что похоже. Ну, например, на урезанное шахматное поле. Таким способом, сделав массив 8 на 8 элементов можно представить шахматную доску, или если брать шире, то вообще любое игровое поле. А содержащееся значение могут, например, означать силу или количество находящихся там существ, или просто тип юнита. Инициализируют многомерные массивы также как и обычные. Разве что есть небольшое отличие в записи. Code int map[5][5]= {{1,2,3,4,5}, {2,50,43,75,10}, {3,44,45,1,9}, {4,16,8,5,1}, {5,5,8,8,7}}; Здесь числа собраны в группы по количеству элементов. Теперь, чтобы лучше понять, что представляют из себя многомерные массивы, напишем небольшую программу. Поскольку урок серьезный, напишем мы таблицу умножения) Вот код. Он небольшой, так что разобраться сможете: Code #include <iostream> using namespace std;
void main() { setlocale(0,"");
int tab[10][10]; //обьявляем массив tab[0][0]=0; for(int i=1;i<10;i++) { tab[i][0]=i; // заполняем верхнюю строку tab[0][i]=i; // заполняем левый крайний столбик }
for(int y=1;y<10;y++) { for(int x=1;x<10;x++) { tab[x][y]=x*y; // заполняем всю таблицу } }
for(int y=0;y<10;y++) { for(int x=0;x<10;x++) { cout<<tab[x][y]<<'\t'; // печатаем таблицу } cout<<"\n\n"; }
cin.get();
} Укажу лишь на вложенные цикл: Code for(int y=1;y<10;y++) { for(int x=1;x<10;x++) { tab[x][y]=x*y; // заполняем всю таблицу } } Именно так здесь заполняются «коробки, вложенные в коробки». Завершая с массивами, выполняю данное ранее обещание и привожу код преобразования из десятичной системы в двоичную: Code #include <iostream> using namespace std;
void main() { setlocale(0,""); cout<<"введите число от 0 до 255 "; int i; cin>>i; int c[8]; //массив в котором будут хранится биты (1 или 0) int ic=7; for(int x=0;x<8;x++) { c[x]=0; // обнуляем элементы массива } while(i!=0) // пока результат деления не равен 0 { c[ic--]=i%2; //присваиваем элементу массива остаток, являющийся битом i=i/2; // делим на 2 } for(int x=0;x<8;x++) { cout<< c[x]; //отображаем результат }
cin.get(); cin.get(); } Здесь массив используется для хранения значений битов. Если бы нам не требовалось отобразить их в обратном порядке до полученного при делении, то мы бы могли обойтись без массива. Также обращаю ваше внимание на строку: здесь используется инкремента в постфиксной записи, поэтому сначала вычисляется все выражение и только после переменная ic уменьшается на единицу. Для преобразования типов существует намного более простой код. Его я приведу, когда мы научимся работать персонально с каждым битом.
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Среда, 11 Ноября 2009, 09:27 | Сообщение # 943 | Тема: Анимация боя на мечах в максе |
Просветленный разум
Сейчас нет на сайте
| IHTI А если один и тот же меч возьмут существа разного роста или телосложения. Как быть в таком случае? Делать еще один набор анимаций для каждого варианта?
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Воскресенье, 08 Ноября 2009, 07:43 | Сообщение # 944 | Тема: Бан лог |
Просветленный разум
Сейчас нет на сайте
| gtasa_love - бан 2 месяца за обсуждение действий и оскорбление администрации. Удалил бан Нилрема, накинул еще 2 месяца за оскорбление Алхимика+ создание темы в несоответствующем разделе. Vinchensoo
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Пятница, 06 Ноября 2009, 23:19 | Сообщение # 945 | Тема: Фэнтезийная РПГ на GameMaker'е |
Просветленный разум
Сейчас нет на сайте
| Quote (Assasin) Строго судить не буду, но кидай ГМ и учи какой нить ЯП, так как на ГМ хорошую РПГ не сделаешь. Но с ГМ у него все же больше шансов, чем учить язык. Да и кто сказал что первая игра должна быть хорошей. Она должна просто быть. Quote (anisimov) Карту сделать просто. Воспользуйтесь советом А. Сапковского. Для создания карт лучше воспользоваться специализированными программами, такими как Fractal_Mapper или Campaign Cartographer. ЁжеГ, Так какая конкретно тебе нужна помощь? Если просто нужны люди то тебе в раздел "Вакансии и работа"
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Пятница, 06 Ноября 2009, 23:05 | Сообщение # 946 | Тема: Zurg. Заражение |
Просветленный разум
Сейчас нет на сайте
| В рамках курса "Основы С++ для начинающих программистов игр" делаю небольшую игру. Уроки на ее основе начнутся с шестого, или седьмого(как получится) когда возьмемся за концепции объектно-ориентированного программирования. Это будет текстовая пошаговая тактическая ролевая игра с псевдографическим интерфейсом. О как завернул. На данный момент мне нужна помощь в проработке концепции и мира игры. Ниже приведен тот стержень, на который нужно опираться, подкидывая мне идеи. Мир игры Мир игры поделен на планы(этажи), на каждом из которых живут разные существа. На самом нижнем плане обитают зурги, но об этом никто не знает, поскольку хода туда нет. Как то один обезбашенный техноид Мифлос задался целью соединить все планы Всеобщим тоннелем. Тоннель был построен и им воспользовались не имевшие ранее доступа на верхние планы зурги. Первой их жертвой стал сам Мифлос. Используя доступные ему средства, зурги и смогли быстро заразить большинство нижних планов. Техническая часть Уровень(план) представляет из себя квадрат Х на Х клеток. В каждой клетке может находится элемент ландшафта, препятствие вроде куста, дерева, строения, какого-нибудь устройства, и существо( монстр, нпс и тд). Зайдя на клетку с постройкой, в нее можно войти, при этом вместо схемы уровня будет отображаться схема внутренностей помещения. На каждом зараженном плане находится Корень – место, где формируются споры зургов. Пока корень не уничтожен, монстры на карте будут возрождаться. Каждый план имеет легальный ход на другие планы(Всеобщий тоннель) который неплохо охраняется, и несколько секретных, которые предстоит найти в процессе приключений. Сюжет Вы находитесь на одном из средних планов. Каком, зависит от выбранного класса. Зурги уже прорастили Корень в вашем плане и начали заражение. Сопротивляться им бессмысленно, поэтому приходится покинуть родные места и перебраться выше. Но и там уже началось заражение. Вы решаете хоть как то попытаться противостоять этому. В первую очередь вам предстоит найти способ сопротивляться воздействию зургов(для каждого класса он свой), а затем додуматься, как уничтожить корень. После чего остается только запечатать тоннель на этом плане и, спустившись ниже, вновь уничтожит корень и так далее. Конечно, на каждом плане должны быть свои заморочки. (На данный момент сюжет кажется мне скудным, а сама концепция планов - скучной. Нужно ее как-то оживить.) Управление в игре Поскольку игра текстовая, управление осуществляется введение простых команд, вроде движения, атаки общения и т.д. Бой Бой происходит на поле 10х10 клеток. В самом начале на поле только вы и ваш враг. Противники ходят по очереди, перемещаясь, атакуя с дистанции или призывая существ. Для призыва используются, в зависимости от расы: руны, - нужно сначала купить, потом заряжать. запчасти, - нужно покупать. После использования пропадают, если нет навыка разборки. письма. – доступны после заключения союза с определенной расой. Существо призывается не сразу, а через несколько ходов. и побеги у зургов. Кроме того обладающие магией могут использовать ее посредством свитков. Свитки покупаются, на использование расходуется мана. Зурги, при наличии на поле особого юнита(пока не придумал) могут заражать чужих существ. Для восстановления жизни можно использовать зелья. В бою юниты ближнего боя перемещаются в указанную точку и атакуют оказавшегося рядом врага(или нескольких в зависимости от способностей). Если рядом по пути следования враг, движение прекращается. Стрелки атакуют дистанционно. (В общем, система боя похожа на Героев, но во время своего хода нужно задействовать всех юнитов, а не согласно инициативы как в HoMM. Ну и юниты призываются на поле(как в карточных играх), а не все время с предводителем). Расы Люди Стовны – каменные существа. Нежить – мертвецы. Зурги – разумная плесень. Заражает и паразитирует на любых живых и оживленных существах. При этом полностью подчиняет их. Способны разрастаться в колонию, принимая вид некого растения. Гигантская колония становится корнем – местом где формируются, дозревают и распространяются споры. Юнит Папоротник – накопитель спор(на самом деле зараженный Нургал). Фишраны – водяные существа. Обитают только на водных планах, на других почти не встречаются. Наименее уязвимы для зургов. Странно, но разумная плесень почему-то не любит воды. Нургалы – разумные растения. Вероятно, родственные зургам. Легко поддаются заражению У каждой расы имеются свои «боевые» существа. У каждой расы должно быть: 1. Минимум один город на плане. 2. Нечто вроде ратуши. 3. Минимум 5 боевых юнитов. Классы Техноид – специалист в механике. Использует различные технические штучки Владаш – мастер призыва различных существ Пирос – маг, сокрушающий врагов с помощью огня. Некрот – некромант по-нашему. Крушар – воин, полагающийся на силу и большое оружие. Мечан – искусный воин, специализирующийся на различных мечах и другом небольшом оружии. Характеристики существ Жизнь Скорость – клеток за ход Мана - энергия для использования свитков Урон - наносимый урон Защита - процент и вероятность блокирования урона Уровня нет, как и опыта. За каждый бой дается несколько кристаллов, которые можно тратить на повышение навыков, характеристик. Юниты Механизмы: Манекен, Баллиста, Паровой Молот, Танк, Крупнокалиберная пушка Нежить: Зомби, Скелет, Вампир, Мара, Лич Ресурсы Золото – дается за выполнение заданий, продажу кристаллов силы. Светной газ – основной источник энергии для механизмов. Хранится в герметичных капсулах. Радиоактивный, может быть причиной мутаций. Производится специальными конденсаторами техноидов. Газ можно купить или получить в качестве вознаграждения. Также можно купить конденсатор, но потом нужно его посещать и забирать выработанный газ. Кристаллы силы – бонусы за выигранную битву. Используются в основном для повышения навыков и характеристик. Строения Город Поселок Деревня Дом Замок Шахты Стовнов Портал Конденсатор – батарейки для техноидов Громоотвод – зарядка рун Фабрика – производство запчастей Порт – приобретение плавсредств Башня магов – место торговли свитками и зарядки рун. Кладбище Это не все, что пока есть, но думаю хватит. Чем именно вы мне можете помочь: • Нужно придумать расы и классы, а также существ, живущих на различных планах. • В частности нужно юниты зургов. Штук пять. • Придумать им всем навыки и способности.(два, три на юнит) • Нужно придумать ресурсы и способы их добычи. • Механика для боя будет использоваться моя, немного модифицированная(упрощенная) MuRoS, но можете предложить что-то другое. • А вообще сгодятся любые идеи, даже самые бредовые. Я сейчас на курсах, так что особой обратной связи не ждите. Ближайшие две недели(это если повезет, а не повезет то до декабря) у меня ограниченный доступ в интернет. Сильно ограниченный. Так что творите, придумывайте. Пользователи, чьими идеями я воспользуюсь, будут внесены в титры и в благодарность получат от меня +. Жду ваших предложений.
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Пятница, 06 Ноября 2009, 17:02 | Сообщение # 947 | Тема: Dragon Age: Origins |
Просветленный разум
Сейчас нет на сайте
| Осталось 0 дней. Игра вышла! Взять можно где то в магазинах, но это только для жителей столиц. На Озоне за 900 р. - http://www.ozon.ru/context/detail/id/4591552/ Правда ждать придется. Или на http://torrents.ru Жаль что я сейчас не дома. Так что, люди, покупаем/качаем, играем и отписываемся здесь. А я лопну от зависти.
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Пятница, 06 Ноября 2009, 14:34 | Сообщение # 948 | Тема: Мобильная ММОРПГ |
Просветленный разум
Сейчас нет на сайте
| Quote (Vinchensoo) Или ты думаешь, что умные дядьки не додумались сделать ограбление корованов?)))) Нет, не зря такой игры еще нет Там можно посмотреть список - Игры, в которых можно грабить корованы
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Пятница, 06 Ноября 2009, 14:12 | Сообщение # 949 | Тема: Ищу работу сценариста для РПГ/Стелялок/стратегий |
Просветленный разум
Сейчас нет на сайте
| Quote (Девяностых) А он (Феромон) супер-проф. сценарист\писака, что может взяться за обучение новичков? Есть такая народная мудрость: Кто умеет - тот делает, кто не умеет - учит других.)
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Пятница, 06 Ноября 2009, 14:06 | Сообщение # 950 | Тема: Анимация боя на мечах в максе |
Просветленный разум
Сейчас нет на сайте
| Quote (ALBAKOR) это кость, привязанная к ладлни А что мешает ее отвязать или сделать две кости - одну для меча в ножнах, другую в руке. Quote (Vinchensoo) ALBAKOR, сделай отдельно заанимированный меч, И соответственно анимировать персонально все оружие? А если 50 мечей только внешним видом отличаются? А затем еще и мучится с синхронизацией анимаций.
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Суббота, 31 Октября 2009, 17:15 | Сообщение # 951 | Тема: Игра про Винча! |
Просветленный разум
Сейчас нет на сайте
| Предупреждаю, если и на этой странице будет столько же флуда или оффтопа, как на первой, тема уедет во флейм.
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Суббота, 31 Октября 2009, 11:55 | Сообщение # 952 | Тема: Обсуждение курса "Основы С++ для начинающих..." |
Просветленный разум
Сейчас нет на сайте
| Quote (Kamskii) nilrem, ты писал курс для MS VS 2008.У меня есть версия 2005.Они очень сильно отличаются? Вообще-то есть мнение, что 2008 это на самом деле 2005 sp2. Конечно, отличия есть, самое приятное это локализация. 2008 студия и часть справки к ней переведена на русский, что несомненно, намного удобнее, особенно для NET программистов, поскольку в основном именно эта часть и переведена. А так различия небольшие, вернее они незаметные для начинающего. Я использую Microsoft Visual Studio Team System 2008, вот она то как раз отличается от обычной. В ней много дополнительных инструментов, чтобы ускорить и облегчить разработку приложений.
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Пятница, 30 Октября 2009, 19:44 | Сообщение # 953 | Тема: Самый маленький компилятор C++ |
Просветленный разум
Сейчас нет на сайте
| http://www.rsdn.ru/article/devtools/devtools.xml Из того, что там представлено мне больше нравится Dev-C++ и MinGW.
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Пятница, 30 Октября 2009, 13:11 | Сообщение # 954 | Тема: Нужен программист! Родители не разрешают вкладывать деньги |
Просветленный разум
Сейчас нет на сайте
| Не, реальнее, что просто в другом годовом поясе живет)
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Пятница, 30 Октября 2009, 13:00 | Сообщение # 955 | Тема: Нужен программист! Родители не разрешают вкладывать деньги |
Просветленный разум
Сейчас нет на сайте
| Quote (Vinchensoo) (15:54:21 19/01/2008) Что это за даты?
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Пятница, 30 Октября 2009, 12:42 | Сообщение # 956 | Тема: Виртуальный мир и игра на его основе. |
Просветленный разум
Сейчас нет на сайте
| PavelZ, Хоть описание и достойное, но оно не соответствует правилам раздела. Читай и редактируй. Или перенести в другой раздел?
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Пятница, 30 Октября 2009, 12:32 | Сообщение # 957 | Тема: Как делать игры на с++, превратись из новичка в профи |
Просветленный разум
Сейчас нет на сайте
| vicmad, Книги на английском по программированию игр на питоне есть на http://www.flazx.com/category36.php
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Четверг, 29 Октября 2009, 22:06 | Сообщение # 958 | Тема: Как делать игры на с++, превратись из новичка в профи |
Просветленный разум
Сейчас нет на сайте
| Заметил сегодня интересную вещь. Книги по OpenGL, как бы это лучше выразить, более техничны. Если по ДХ авторы чуть ли не все время "сюсюкают" с читателем, выдавая тонны мусора, то по опенгл просто излагают нужный материал. Образ мышления у них разный, что ли?
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Четверг, 29 Октября 2009, 18:34 | Сообщение # 959 | Тема: winter-space |
Просветленный разум
Сейчас нет на сайте
| doxl, Оформление темы не соответствует правилам раздела http://gcup.ru/forum/9-1844-1 Исправь. А чтоб его..., автор уже в бане.
Windmill 2
WindMill 2D Game Engine
|
|
| |
nilrem | Дата: Четверг, 29 Октября 2009, 15:13 | Сообщение # 960 | Тема: Я хочу научиться делать 3D игры - драки |
Просветленный разум
Сейчас нет на сайте
| boks1993, Для тебя, наверное, самый простой способ это конструктор, например -Realm Crafter.
Windmill 2
WindMill 2D Game Engine
|
|
| |
|