ной значения, проверка логического условия и изменение переменной) могут быть опущены по желанию программиста, т. е. заменены на пустой оператор.
Запись for (int i = 0; i < 3; i++) {}; и
int i = 0; while (i < 3) {
i++;
};
эквивалентны.
Условие цикла do-while проверяется после того, как блок кода будет выполнен, другими словами, определенный кусок кода будет выполнен хотя бы один раз, тогда как в циклах с предусловием он может не выполниться вовсе. Синтаксически цикл записывается так:
do {
//выполнить хотя бы один раз
}while (true);
Отметим, что при приведенной записи код будет выполняться бесконечно, так как логическое условие состоит только из значения true. Чтобы выйти из цикла можно внутри блока воспользоваться уже знакомым оператором break, позволяющим прервать выполнение цикла из любого места. Кроме оператора break существует оператор continue, который завершает текущую итерацию и переходит к выполнению следующей итерации цикла.
2.4. Область видимости переменной
Объявление переменной вводит имя в область видимости (scope); это значит, что имя можно использовать лишь в ограниченной части программы. Для имени, объявленного в конкретном блоке кода (его называют локальным), область видимости простирается от точки объявления до конца содержащего это объявление блока.
Переменная называется глобальной, если она объявлена вне функции main и других блоков кода. Чтобы отличить глобальную переменную от локальной можно использовать два двоеточия перед ее именем. Лучше избегать использования глобальных переменных во избежание перекрытия имен и случайного изменения в коде значенияглобальной переменной вместо локальной.
Следующий код полностью поясняет все возможные ситуации:
int global = 5; // создание глобальной переменной
int main() {
16
int global = 4; // создание локальной переменной global; // =4, вызов локальной переменной ::global; // =5, вызов глобальной переменной
int outer = 0; // создание локальной переменной
{ // начало внутреннего блока
outer; // =0, вызов лок. переменной внешнего блока float outer = 3.0; // перекрытие имени внеш. перемен. outer; // =3.0 вызов лок. переменной типа float
int inner = 2; // создание лок. внутр. переменной inner; // =2, вызов лок. переменной
}
//inner; // ошибка! Такой переменной в этом блоке нет.
//float outer; // ошибка! Такая переменная уже существует
return 0;
};
2.5. Функция и ее аргументы
Функция в программировании – фрагмент программного кода (подпрограмма), к которому можно обратиться из другого места программы.
Передача аргументов в функции может быть по значению или по ссылке. В первом случае значение, которое используется в функции, является копией значения, которое передается в функцию. Во втором передается само значения без его копирования.
#include <iostream>
using namespace std;
void byValue(int a) { a = 2;
}
void byReference(int& a) { a = 2;
}
int main(int argc, char *argv[]) { int value = 6; byValue(value);
cout << "By value:\t\t" << value << '\n';
17
byReference(value);
cout << "By reference:\t" << value << '\n';
}
Вывод в консоли:
By value: 6 By reference: 2
После вызова метода, который передает аргумент по ссылке, значение переменной value изменяется. Таким образом, с одной стороны, имеется возможность не копировать значение (а процесс копирования может быть весьма затратен), но, с другой стороны, никаким образом не гарантируется неизменяемость значения переменной. Для того чтобы гарантировать в коде, что переданное значение не будет изменено внутри метода, к аргументу добавляют модификатор const:
void byReference(const int& a) { a = 2;
}
При компиляции проекта будет выведена следующая ошибка:
Untitled.cpp:10:4: error: read-only variable is not assignable a = 2;
~^
1error generated.
3.ПОЛЬЗОВАТЕЛЬСКИЕ СТРУКТУРЫ В C++
3.1. Запись структуры
Язык С++ обладает большим количеством встроенных типов данных, но на практике этих типов данных мало. Часто программисту необходима возможность создавать сложные типы данных, которые могли бы объединять несколько простых.
Предположим, что необходимо создать тип данных, который описывает человека. Опишем его с помощью структуры:
struct Person { string lastname; string firstname;
string middlename;
};
Структура Person состоит из трех полей структуры, каждый из которых является строкой. lastname, firstname и middlename хранят информа-
18
цию о фамилии, имени и отчестве соответственно. Теперь можно создать запись о человеке:
int main(int argc, char *argv[]) { Person p = Person(); p.lastname = "Pelevin"; p.firstname = "Maksim"; p.middlename = "Sergeevich";
}
Точка между переменной и полем в записи p.lastname является операцией доступа к полю. Обратите внимание, что для объектов, созданных динамически, эта операция выглядит как ->.
int main(int argc, char *argv[]) { Person *p = new Person(); p->lastname = "Pelevin"; p->firstname = "Maksim"; p->middlename = "Sergeevich";
p.middlename = "Sergeevich"; // error: reference type 'Person *' is a pointer; maybe you meant to use '->'?
delete p;
}
Выделенная в примере строчка приведет к ошибке компиляции.
3.2. О размере структуры
Посчитаем размер для следующей структуры:
struct A { int a; char b;
};
int main(int argc, char *argv[]) { cout << sizeof(A);
}
При этом возникает простой вопрос: если int занимает в памяти 4 байта, char – 1 байт, то сколько в памяти занимает тип данных A? 4 + 1 = 5. Тогда почему sizeof(A) возвращает число 8? Правильный ответ заключается в том, что внутреннее представление выравнивается таким образом, чтобы оно было кратно одному машинному слову (м.с.) процессора (напомним, что для 32-битных систем 1 м.с. = 4 байта, а для 64-битных – 8). Можно установить собственное количество байтов, по которому будет выравниваться размер структуры. Делается это с помощью специальных директив препроцессора
19
(здесь 1 – 1 байт: можно подставить любое понравившееся число, например 2, и тогда структура будет занимать 6 байт):
#pragma pack(push, 1) struct A {
int a; char b;
};
#pragma pack(pop)
4. ПРАКТИЧЕСКИЕ ЗАДАНИЯ
№ 1. Поиск показателя GC
Описание проблемы. Цепочка ДНК должна быть каким-либо образом обозначена в базе данных. Обычно используют метод обозначения, называемый формат FASTA. В этом формате цепочка представлена в виде строки, которая начинается со знака «>», за которым следует информация о названии и характере строки. Последующие строки содержат саму строку; следующая строка, начинающаяся со знака «>», указывает на обозначение следующей цепочки.
В нашей реализации цепочка в формате FASTA будет помечена ID «Rosalind_XXXX», где «XXXX» обозначает четырехзначное число между
0000 и 9999.
Дано: максимум 10 цепочек ДНК в формате FASTA (каждая длинной не более 1 тыс. знаков).
Возвращаемое значение: ID цепочки, имеющей наибольший показатель содержания GC (относительная величина содержания цитозина (C) и гуанина
(G) от общего числа нуклеотидов в цепочке, выраженная в процентах), за которым следует сам показатель цепочки с точностью до двух десятичных знаков.
Пример данных:
>Rosalind_Sequence_6404 CCTGCGGAAGATCGGCACTAGAATAGCCAGAACCGTTTCTCTGAGGCTTCCGGCCT T
CCCTCCCACTAATAATTCTGAGG >Rosalind_Sequence_5959
CCATCGGTAGCGCATCCTTAGTCCAATTAAGTCCCTATCCAGGCGCTCCGCCGAAG G
TCTATATCCATTTGTCAGCAGACACGC >Rosalind_Sequence_0808
CCACCCTCGTGGTATGGCTAGGCATTCAGGAACCGGAGAACGCTTCAGACCAGCCC GGACTGGGAACCTGCGGGCAGTAGGTGGAAT
20