Отправляет email-рассылки с помощью сервиса Sendsay

Инвестирование с нуля

  Все выпуски  

Программирование на Си и С++ с нуля 140) Массивы и циклы


Школа программирования

140) Программирование на Си и С++ с нуля: Массивы и циклы

Письма.

Полезное письмо прислал Андрей (в будущем, если вы будете явно указывать в письме вашу фамилию, то я буду и ее ставить в рассылку). Возможно, начинающим не все в нем будет понятным, но все равно.

...Я сменил не только среду разработки, но и ОС (перешёл на Linux), так что, если раньше я переводил задания рассылки с Дельфи на Билдер, то теперь - с C++ Билдера на C++ Qt. ;) Трудновато, но очень увлекательно. Хотелось бы немного похвастаться успехами: во вложении - исходные тексты (кодировка Utf8) и скриншоты "кубика" - кроссплатформенного приложения (нормально откомпилировано в Fedora Core 5 и Mandriva Linux, а также в WinXP, теоретически должно работать и в Mac OS X и других ОС) по мотивам предыдущего выпуска рассылки.
Поскольку там исключительно кроссплатформенный код, то, соответственно, использованы исключительно стандартные Си-функции, в том числе rand / srand.
Альтернативой Борландовской Randomize(), пришедшей из Паскаля, является void srand(unsigned int seed), объявленная в заголовочном файле stdlib.h. В качестве аргумента она чаще всего принимает значение текущего времени, генерируемого таймером (заголовочный файл time.h). Таким образом, полный фрагмент кода для Билдера может выглядеть следующим образом:

  #include <stdlib.h>
  #include <time.h>
  //--------------------------------------------------------------------------------------------
  void __fastcall TMainForm::FormCreate(TObject *Sender)
  {
      srand(time(NULL));
  }

В Standard C++ тип AnsiString, введённый компанией Inprise / Borland для лучшей совместимости с программами, написанными на Object Pascal, разумеется, не поддерживается. Альтернативой является строковй тип, объявляемый в стандартном пространстве имён std:

  #include <iostream>
  #include <string>
  using namespace std;
  //---------------------------------------------------------------------------------------------------
  string sGreeting = "Привет!";
  или просто массив символов (для древних компиляторов):
  char sGreeting[] = "Привет!";

Есть способ обойтись без сообщения об ошибке, когда пользователь не сможет ввести в поле ничего, кроме цифр. В библиотеке Qt для этой цели существуют классы валидаторов (QIntValidator, QDoubleValidator), которыми я и воспользовался в "кубике". В Билдере же можно воспользоваться перечислимыми типами, включив в обработчик события OnKeyPress Editа следующий код:

  set <char, ' 0 ' , ' 9 ' > Dig;
  Dig << ' 0 ' << ' 1 ' << ' 2 ' << ' 3 ' << ' 4 ' << ' 5 ' << ' 6 ' << ' 7 ' << ' 8 ' << ' 9 ' ;
  if( !Dig.Contains(Key)) // Если нажата не клавиша с цифрой...
  {
      Key = 0; // ничего не вводим...
      Beep(); // ...а только гудим... ;)
  }

В C++Builder тоже есть компоненты для ввода значений по маске или только чисел. Они позволяют избежать ошибок преобразования, но мы их рассмотрим чуть попозже.

Код Андрея вместе со скриншотами программы-кубика для Линукса :) можно скачать тут.

  1. Относительно программной очистки поля ввода я использовал
  команду
  Edit->Text="" и еще обнаружил команду Edit->Clear().
  Чем эти команды отличаются?
  Андрей

Первый оператор присваивания заносит в свойство Text некоторое значение (пустую строку). Хотя фактически поле очищается, однако формально выполняется действие по присваиванию пустой строки. Clear - это команда, принадлежащая объекту Edit1 (такие команды называются методы), которая очищает содержимое поля Text. Лучше пользоваться готовыми командами, так как они реализованы чуть-чуть эффективнее.

Не нашел пока, как центрировать надписи в текстовом поле и поле ввода.

В обычном поле ввода нельзя, но можно в поле ввода по маске - мы его изучим попозже.
В поле-метке TLabel можно задать способ выравнивания в свойстве Alignment. Значение taLeftJustify - по левому краю, taCenter - по центру.

  2. Я также доработал бот, добавив в обработчик кнопки проверку
  заполнения поля ввода в начале и очистку поля ввода в конце:
  a=Edit1->GetTextLen();
  if( a==0)ShowMessage("Напишите, пожалуйста, что-нибудь");
  else
  {
  ...
  }
  Edit1->Text="";
  Теперь при пустом поле ввода ответ бота блокируется. Правда,
  любой набор
  букв и цифр позволяет эту проверку обойти.
  Бот можно было бы значительно улучшить, введя в него
  автоматические
  поздравления с праздниками.
  Подскажите в этой связи:
  а) какая функция позволяет проверять текущую дату, чтобы по ней
  выбирать
  праздники?

Текущую дату можно получить функцией Date() (время - функцией Time). Правда, она возвращает значение сложного типа TDateTime , мы их пока не изучали.

б) как лучше организовать базу данных праздников?

Это масштабный вопрос :) Но, надеюсь, в ближайших занятиях курса мы этой темы коснемся.

Я бы сделал так: завел файл с датами праздников и их названиями, считывал из него содержимое и по нему сверял с текущей датой. Это на самом деле несложно, но пока мы не добрались до файлов.

Также я не смог найти способ программного возврата курсора в поле ввода (после ответа бота курсор остается в поле Memo).

По-моему, нужна функция SetFocus - что-то типа:

   Memo1->SetFocus();

3. Я попытался перенести .exe файл с ботом на другой компьютер, но он там не запустился, а компьютер выдал ошибку: "Приложение не удалось запустить, поскольку vcl60.bpl не был найден". Как сделать, чтобы .exe файлы работали на других компьютерах тоже?

Можно, в принципе, поставлять вместе с файлом библиотеку vcl60.bpl - набор стандартных функций. Но чаще ее включают непосредственно в тело программы - для этого надо СНЯТЬ флажок с поля Use dynamic RTL (Project - Options - Linker - Linking; в разных версих окно с этим флажком может называться по разному), а также, вроде, отключить режим Build with runtime packages в разделе Packages.

4. Если в процессе отладки случайно закрыть форму и окно с кодом, то программу невозможно закрыть, а компьютер выключить, так как все блокируется сообщениями, что отладка продолжается и что нет доступа к ее прекращению. Приходится перезагружать компьютер кнопкой Reset. Можно ли как-нибудь остановить отладку по другому?

Прекращение отладки из среды выполняется клавишами CTRL+F2 или командой Run - Reset. Можно еще вызвать системый диспетчер задач - ctrl+alt+del, и с его помощью удалить зависшую оболочку.

Как программными методами можно изменять свойства объектов; то есть доступ к свойствам понятен, но не ясно какие нужно указывать параметры, например, цвет?

Значения свойств задаются константами. Константа напоминает переменную (именованное значение), только значение константы в отличие от переменной на всем протяжении работы программы неизменяемо.

Константа объявляется в программе так:

const имя = значение ;

Например:

const pi = 3.14;

Далее идентификатор pi можно использовать в качестве значения 3,14.

В C++Builder имеется множество констант, которые делают наглядными различные условные значения. В частности, цвет можно задавать обычным оператором присваивания (значение свойства в Инспекторе объектов - это просто перечень констант). Например:

   Label1->Color = clRed;

Решение домашних заданий начал, как всегда в одной форме, но сразу возник вопрос: а как разделить задачи? например сделать их в отдельных окнах, запускаемых кнопками, или на вкладках.
Александр

Лучше каждому заданию отводить отдельный проект.

Н и в одном проекте может быть несколько форм. Одна всегда главная (обычно та, которая создана первоначально), остальные запускаются программно. Добавление новой формы к проекту выполняют командой File - New - Form. Новую форму (она обычно получает имя Form2 и хранится в файлах Unit2.cpp/h/dfm) можно также проектировать в Проектировщике, но покажется она, только если ее сделать явно видимой. Для этого можно задействовать свойство Visible второй формы в обработчике нажатия на кнопку первой формы. Как-то так:

   Form2->Visible = true;

Давно хотел спросить - на С++ создаються игры или есть языки предназначенные для создания игр?
Константин.

90% (а может, и 99%) всех крупных коммерческих игр создаются на C++. Для этого используется графическая библиотека DirectX, реже - OpenGL. Это достаточно сложная задача (в первую очередь из-за необходимости изучения внутренних интерфейсов DirectX, которые к тому же меняются постоянно от версии к версии).

Вообще, С++ - это базовый язык при создании программ для всех версий Windows до Vista (в Vista лучше использовать язык C#).

Но есть пакеты, которые позволяют быстро создавать игровые программы среднего размера в одиночку. Это прежде всего BlitzBasic (о нем рассказывается в моей рассылке Школы программирования), а также система Flash-программирования. Они в десятки раз сокращают и упрощают время разработки, и с их помощью созданы тысячи успешных коммерческих игр.

  1. Я правильно понял - else это противоположность if ? Другими
  словами
  if( x > y){.....} else подразумевает x < y или это не всегда так.

Часть else выполняется, если условие ложно, а часть if - если условие истинно. else противоположно if, конечно.

   2. Вытекает из 1-го как лучше - if (x > y) ......... if (x < y) или if (x > y) ........
  else .......;
  В задании работают оба варианта.

Лучше второй, потому что в первом случае будет два раза выполнена проверка (кстати, надо еще учитывать возможность равенства, то есть проверять если x меньше y или x больше или равно y), а во втором ветвление распознается автоматически.

  Загорелся желанием сделать бота-говорилку.
  Застопорился в одном месте: как сделать, чтобы вводимые строчки
  в Memo1 были разноцветными? Т.е. я хочу, чтобы у человека текст
  был чёрным, а у бота - красным. Пробовал всталять
  Memo1->Font->Color=clRed; а после Memo1->Font->Color=clNone;
  Однако такая конструкция изменяет цвет во всём Memo1, а не
  конкретной строчки. Как быть? Прошу помочь...

Увы, я не знаю. Сам думал, как это сделать, но так ничего и не придумал.

Есть компонент TRichEdit, позволяющий форматировать отдельные абзацы текста, но он нетривиален в использовании.

Я бы задействовал обычный список просмотра, в котором выводил бы каждую строчку своим цветом - но для этого требуется тоже определенное знание способов рисования в C++Builder.

Кто-нибудь подскажет, как попроще реализовать такую полезную возможность?

И второй вопрос: можно ли сделать так, чтобы при поиске текста (например так: s.AnsiPos("Привет") не было привязки к регистру?
Роман

Для этого сам текст лучше преобразовывать к одному из регистров, и лишь потом его проверять. Например, к верхнему- в этом поможет функция AnsiUpperCase:

s = AnsiUpperCase( s );

Проверка: s.AnsiPos("ПРЕВЕД")

Кстати, если у вас проблемы с функцией Random, то оказывается в старых версиях C++Builder есть функция random() , да и randomize(), только с маленькой буквы они начинаются.

Мне прислали варианты нескольких ботов, но они великоваты для рассылки, рекомендую завести всем желающим соответствующую тему на нашем любимом сайте http://prog-begin.net.ru/.


Массивы.

Если в некоторой задаче надо обработать большое число значений - например, найти сумму тысячи переменных, то одно лишь описание переменных займет уйму усилий:

  int a, b, c, d, ab, ac, ad, ... ;

А чтобы найти их сумму, придется вручную составлять оператор с длиннющим выражением. Можно названия переменных сделать более наглядными:

  int x1, x2, x3, ..., x1000;

но принципиально это дела не меняет.

В языке С++ есть понятие массива - переменной, которая хранит не одно, а несколько значений, а доступ к любому из них осуществляется по номеру (он называется индекс).

Декларация массива выглядит так:

тип имя [ число-элементов ];

Например:

int x[1000];

Переменная x - это массив, состоящий из 1000 целых значений.

Доступ к любому элементу осуществляется по его номеру (индексу). В С++ принято, что самый первый элемент массива всегда имеет индекс 0, второй - индекс 1, и так далее. То есть нумерация элементов начинается с нуля. Другими словами, индекс элемента означает его сдвиг по отношению к началу массива - первый элемент имеет нулевой сдвиг, а 100-й элемент - сдвиг величиной 99.

Обращение к элементу массива записывается так:

имя-массива [ индекс ]

Например, доступ к самому первому элементу массива x запишется следующим образом:

x[0]

Доступ к последнему, сотому - так:

x[99]

Любой из элементов массива используется в программе как обычная переменная:

   int a[5];
   a[0] = 1;
   a[4] = 15;
   a[3] = a[4] - 2;

Массив может состоять из нескольких измерений. Выше описан массив из одного измерения - он представляет собой линейную последовательность значений. Если надо описать такую структуру данных, как таблица, используют двумерный массив или матрицу. Он декларируется так:

тип имя [ число-элементов-по-первому-измерению , число-элементов-по-второму-измерению ];

Фактически он описывает нечто вроде шахматной доски, двумерного координатного поля с заданными размерами. Первое измерение можно трактовать как ширину (число столбцов), а второе - как высоту (число строк).

Например:

int m[10,20];

Тут описана матрица m размером 10 столбцов на 20 строк, состоящая из 200 элементов. Доступ к ее элементам осуществляется типовым способом:

   m[0,0] = 5;
   m[0,3] = 4;
   m[9,19] = m[7,7] + 1;

Важнейшее свойство массивов заключается в том, что индексы элементов могут быть вычисляемыми. При обращении к элементу можно в качестве индекса задавать выражение целочисленного типа:

   int A[10];
   int i;
   i = 5;
   A[i] = 11;
   A[ i+1 ] = A[i] - 1;

В результате 6-й элемент массива A (с индексом 5) получит значение 11, а 7-й элемент (с индексом 6) - значение 10.

Пока что массивы не сильно упрощают задачу обработки большого числа значений. Конечно, вместо декларирования тысячи переменных теперь можно записать просто int x[1000]; , однако все равно пока придется вручную вводить каждый элемент с его индексом.

Автоматизировать данный процесс позволяет оператор цикла. Этот оператор многократно выполняет одну и ту же последовательность команд. Он записывается так:

   for( начало ; условие ; повтор )
     тело ;

Тело - это так называемое тело цикла - та команда или группа команд, охваченная фигурными скобками, которая будет многократно выполняться - а именно, до тех пор, пока значение условия истинно.

"Начало" в операторе цикла - это некоторый оператор, который выполняется однократно перед началом его работы. Условие - это логическое выражение. Перед каждым повтором цикла оно вычисляется, и если оно истинно, выполняется тело цикла. Следом за телом выполняется "повтор" - часть в заголовке оператора цикла, представляющая собой отдельный оператор.

Части начала и повтора (и условия, по большому счету) можно опускать, но точки с запятой между ними необходимо сохранять.

Например:

  int i;
   int n;
   n = 5;
   for( i=0; i<5 ; i=i+1 )
     n = n + 1;
   

Оператор цикла работает так. Сначала однократно выполняется начало - оператор i=0 . Затем проверяется условие i<5. Оно пока истинно (переменная i равна нулю), поэтому выполняется тело цикла n=n+1. Далее выполняется повторяющаяся часть заголовка i=i+1 - значение переменной i увеличивается на i. Снова проверяется условие i<5 - оно по-прежнему истинно, вновь вычисляется тело n=n+1, снова вызывается оператор i=i+1 - и так будет происходить до тех пор, пока значение переменной i не станет большим или равным 5. За это время переменная n примет значение 10. Как только условие становится ложным, управление передается следующему за for оператору.

В С++ принято сокращать запись операторов, и хотя это далеко не всегда повышает наглядность, в некоторых случаях сокращение все же оправдано.

Операторы ++ и -- увеличивают значение переменной на единицу. Например, запись i++ эквивалентна записи i=i+1 , а запись n-- эквивалентна записи n=n-1 . С учетом этого перепишем оператор цикла:

   for( i=0; i<5 ; i++ )
     n = n + 1;

Если в операторе присваиваня одна и та же переменная введена по обе части оператора = , то его запись можно сократить. Конструкция

переменная = переменная + выражение ;

(имя переменной в обеих частях одинаково) сокращается до вида

переменная += выражение ;

Аналогично и для арифметических операций -, *, /.

Оператор n=n+5 можно сократить до n+=5. Оператор x=x*2 - до вида x*=2.

Вновь сократим наш оператор:

   for( i=0; i<5 ; i++ ) n += 1;

Или так:

   for( i=0; i<5 ; i++ ) n ++;

Переменная, которая используется для учета числа повторов цикла, называется счетчик. В данном случае счетчиком цикла будет переменная i.

Чаще всего операторы цикла применяются для обработки массивов. В таком случае удобно задействовать счетчик в качестве индекса массива. В следующем примере каждому элементу массива A из пяти элементов присваивается его порядковый номер, начиная с единицы:

   int A[5];
   int i;
   for( i=0; i<5; i ++ )
     A[ i ] = i+1 ;

Элемент с индексом 0 получит значение 1, элемент с индексом 1 - значение 2, и так далее.

Следующий пример вычисляет сумму всех элементов массива B:

   int B[100];
   int sum;
   int i;
   // каким-то образом инициализируются элементы массива B...
   ...
   // суммируем элементы, текущая сумма накапливается
   // в переменной sum:
   sum=0;
   for( i=0; i<100; i++ )
     sum = sum + B[i];

Или, более сокращенный оператор цикла:

   for( i=0; i<100; i++ ) sum += B[i];

Тело цикла может состоять из нескольких команд, которые заключаются в фигурные скобки. Например, посчитаем одновременно сумму и произведение всех элементов массива:

   sum = 0; // здесь будет сумма
   mul = 0; // здесь будет произведение
   for( i=0; i<100; i++ )
     {
     sum += B[i];
     mul *= B[i];
     }


Таким образом, мы изучили ВСЕ ключевые возможности С++, с помощью которых можно теоретически запрограммировать алгоритм произвольной сложности. Это: типы данных и переменные, оператор присваивания, условный оператор, оператор цикла и массивы.

Как вывести значения массива на экран? Неплохо задействовать для этого список - компонент TListBox. Чтобы очистить его содержимое, используется команда Clear:

   ListBox1->Clear();

Чтобы добавить новый элемент (текстовую строку) в конец, используют такую запись:

   ListBox1->Items->Add("строка");

Например, выводим содержимое массива A из 10 элементов в список:

   AnsiString s;
   int i;
   ListBox1->Clear();
   for( i=0; i<10; i++ )
     {
     s = IntToStr( A[i] );
     ListBox1->Items->Add( s );
     }

Далее мы сосредоточимся на практике применения этих операторов и возможностях среды C++Builder, существенно упрощающих процесс программирования.

Задания. Достаточно сложные, поэтому если не выходит, просто поэкспериментируйте с заполнением массива случайными (или неслучайными :) значениями, их выводом в список и суммированием.

1. Найти наибольший и наименьший элементы массива из 100 элементов, заполненного случайными значениями (заполняйте с помощью функции Random/random нажатием на одну кнопку, а процесс поиска выполняем по нажатию на другую кнопку), и поменять их местами. Вывести номер и значение минимального и максимального элементов.

2. Реально сложное :) на сортировку - придумайте алгоритм сортировки значений массива - по убыванию или возрастанию, без разницы. Таких алгоритмов множество, но попробуйте сами, не заглядывая в готовые.


(c) 2004-2006 Сергей Бобровский bobrovsky as russianenterprisesolutions.com

Школа программирования с нуля
Все предыдущие выпуски базового курса всегда тут:
http://www.infiltration.ru/p/


Вышел мой учебный курс "Технологии Delphi. Разработка приложений для бизнеса".
http://shop.piter.com/book/978591180282/

Учебный курс рассчитан не только на разработчиков, но и на всех тех, кто хочет стать ИТ-менеджером. Для этого как минимум нужно иметь общее представление о современных технологиях разработки и их истории и владеть соответствующей терминологией.
В книге описаны десятки технологий, каждой из которых посвящены отдельные книги. Таким образом, купив одну мою книгу, вы существенно сэкономите :) В книге полностью описан язык Delphi (версия 2006, полностью совместимая с Turbo Delphi) для обеих платформ - Win32 и .NET. Охвачены также темы работы с файлами на этих платформах, создания файл-серверных, клиент-серверных, распределенных приложений, веб-программ (Indy, ASP.NET, веб-сервисы). Описаны языки SQL и OCL. Немало глав посвящены истории программирования и различных технологий. Особое внимание уделено созданию программ с помощью технологии ECO и языка моделирования UML - программы фактически рисуются, и теперь даже для создания корпоративных приложений и их переноса в Интернет не обязательно знать программирование!
Отдельная часть отведена технологиям организации групповой работы, управления требованиями, контроля версий, локализации и тестирования.
Тут подробнее про книгу.

Другие мои книги, которые пока доступны в продаже:


Дизайн рассылки: Алексей Голубев - Web-дизайн и web-программирование


В избранное