2.3. Спецификаторы класса памяти
В C++ имеется четыре спецификатора класса памяти: auto, register, static, extern.
Спецификатор класса памяти может предшествовать объявлениям переменных и функций, указывая компилятору, как следует хранить переменные в памяти и как получать доступ к переменным или функциям. Переменные, объявленные со спецификаторами auto и register, являются локальными, а со спецификаторами static и extern — глобальными. Память для локальной переменной выделяется каждый раз при достижении блока, в котором объявлена переменная, и освобождается по завершении выполнения блока. Память для глобальной переменной выделяется один раз при запуске программы и освобождается, когда работа программы закончена.
Указанные четыре спецификатора определяют также область видимости переменных и функций, то есть часть программы, в пределах которой к идентификатору можно обратиться по имени. На область видимости переменной и функции влияет место ее объявления в программе. Если объявление расположено вне функции, уровень объявления является внешним, если же оно находится в теле функции — внутренним.
Спецификаторы класса памяти несколько различаются по смыслу в зависимости оттого, что объявляется — переменная или функция, а также от того, на каком уровне, внешнем или внутреннем, объявляется данный идентификатор.
Переменная, объявленная на внешнем уровне, является глобальной и по умолчанию имеет класс памяти extern. Внешнее объявление может включать инициализацию (явную либо неявную) или просто быть ссылкой на переменную, инициализируемую в другом месте программы.
static int iv; // по умолчанию неявно присваивается 0
static int iv=10; // явное присваивание
int ir=20; // явное присваивание
Область видимости глобальной переменной распространяется до конца программы. Обращение к переменной не может находиться выше той строки, в которой она объявлена.
Переменная объявляется на внешнем уровне только один раз. Если в одном из файлов создана переменная с классом памяти static, то она может быть объявлена под тем же именем с тем же спецификатором static в любом другом исходном файле. Так как статические переменные доступны только в пределах своего файла, конфликтов имен не возникнет.
С помощью спецификатора extern можно объявить переменную, которая будет доступна из любого места программы. Это может быть ссылка на переменную, описанную в другом файле или ниже в том же файле. Последняя особенность делает возможным размещение ссылок на переменную до ее инициализации.
2.4. Операции
C++ включает побитовые операции, операции инкрементирования и декрементирования, условную операцию, операцию запятая операции и комбинированного присваивания.
Побитовые операции работают с переменными как с наборами битов, а не как с числами. Эти операции используются в тех случаях, когда необходимо получить доступ к отдельным битам данных (при выводе графических изображений на экран). Побитовые операции применимы только к целочисленным значениям. В отличие от логических операций, с их помощью сравниваются не два числа целиком, а отдельные их биты. Основные побитовые операции: И (&), ИЛИ (|) и исключающее ИЛИ (^). Сюда можно также отнести унарную операцию побитового отрицания (~), которая инвертирует значения битов числа.
Операция & записывает в бит результата единицу только в том случае, если оба сравниваемых бита, равны 1, как показано в следующей таблице:
Бит 0 |
Бит 1 |
Результат |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
1 |
1 |
1 |
Эта операция часто используется для маскирования отдельных битов числа. Например: 0xF1 & 0x 35 = 0x 31.
Операция | записывает в бит результата единицу в том случае, если хотя бы один из сравниваемых битов равен 1, как показано в следующей таблице:
Бит 0 |
Бит 1 |
Результат |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
1 |
Эта операция часто применяется для установки отдельных битов числа. Например: 0x F1 | 0x 35 = 0x F5.
Операция ^ записывает в бит результата единицу в том случае, если сравниваемые биты отличаются друг от друга, как показано в следующей таблице:
Бит 0 |
Бит 1 |
Результат |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
Эта операция часто применяется при выводе изображений на экран, когда происходит наложение нескольких графических слоев.
Например: 0x F1 ^ 0x 35 = 0x C4.
В C++ существует две операции сдвига: << - сдвиг влево, >> - сдвиг вправо. Действие первой операции состоит в сдвиге битового представления целочисленной переменной, указанной слева от операции, влево на количество битов, заданное справа от операции. При этом освобожденные младшие биты заполняются нулями, а соответствующее количество старших битов утрачивается.
Сдвиг беззнакового числа на одну позицию влево с заполнением младшего, разряда нулем эквивалентен умножению числа на 2.
unsigned int iv=65; // младший байт: 01000001
iv<<=1; //младший байт: 10000010
cout<<iv; // будет выведено 130
Сдвиг вправо сопровождается аналогичными действиями, только битовое представление числа сдвигается на указанное количество битов вправо. Значения младших битов утрачиваются, а освободившиеся старшие биты заполняются нулями, если операнд беззнаковый, и значением знакового бита в противном случае. Таким образом, сдвиг беззнакового числа на одну позицию вправо эквивалентен делению числа на два:
unsigned int iv=10; // младший байт: 00001010
iv>>=1; // младший байт: 00000101
cout<<iv; // будет выведено 5
Увеличение (уменьшение) значения переменной на 1 очень часто встречаются в программах, поэтому разработчики языка С++ предусмотрели для этих целей специальные операции инкрементирования (++) и декрементирования (--).
Так, вместо строки iv+1; можно ввести строку iv++; или ++iv;
В ситуации, когда операция ++ является единственной в выражении, не имеет значения место ее расположение: до имени переменной или после него. Значение переменной в любом случае увеличится на единицу.
В процессе работы со сложными выражениями необходимо внимательно следить, когда именно происходит модификация переменной. Нужно различать префиксные и постфиксные операции, которые ставятся соответственно до или после имени переменной.
Например, при постфиксном инкрементировании i++ сначала возвращается значение переменной, после чего оно увеличивается на единицу. С другой стороны, операция префиксного инкрементирования ++i указывает, что сначала следует увеличить значение переменной, а затем возвратить его в качестве результата. Например:
k=++I; //i=4, k=4
k=i++; //i=4, k=3
k=--i; //i=2, k=2
k=i--; //i=2, k=3
В C++ представлены все стандартные арифметические операции: сложения (+), вычитания (-), умножения (*), деления (/) и деления по модулю (%). Первые четыре операции не требуют разъяснений. Суть операции деления по модулю:
int ia=3,ib=8,id;
id=ib % ia; // результат: 2
При делении по модулю возвращается остаток от операции целочисленного деления.
В C++ операция присваивания (=) может входить в состав других выражений. В результате выполнения операции присваивания возвращается значение, присвоенное левому операнду. Например, следующее выражение вполне корректно:
iv=8*(iw=5); //iv=40.
В данном случае сначала переменной iw присваивается значение 5, после чего это значение умножается на 8, а результат присваивается переменной iv.
Таблица 2. Комбинированные операции присваивания
Исходный оператор |
Эквивалент |
Комментарий |
var=var+3; |
var+=3; |
К переменной прибавляется 3 |
var=var-10; |
var-=10; |
Из переменной вычитается 10 |
var=var*3.14; |
var*=3.14; |
Переменная умножается на 3.14 |
var=var/2.5; |
var/=2.5; |
Переменная делится 2.5 |
var=var&0xF; |
var&=0xF; |
В переменной остаются только 4 младших разряда |
var=var|0xF; |
var|=0xF; |
В переменной устанавливаются 4 младших разряда |
var=var<<3; |
var<<=3; |
Переменная сдвигается влево на 3 разряда |
var=var>>5; |
var>>=5; |
Переменная сдвигается вправо на 5 разрядов |
var=var%2; |
Var%=2; |
Взятие остатка при делении var на 2 |
var=var+1; |
var++; |
Операция инкремента |
var=var-1; |
var--; |
Операция декремента |
Операции сравнения предназначены для проверки равенства или неравенства сравниваемых операндов. Все они возвращают true в случае установления истинности выражения и false в противном случае. Ниже перечислены операторы сравнения, используемые в языках С и C++.
Операция |
Выполняемая проверка |
== |
Равно |
!= |
Не равно |
> |
Больше |
< |
Меньше |
<= |
Меньше или равно |
>= |
Больше или равно |
Логические операции И (&&), ИЛИ (||) и НЕ (!) возвращают значение true или false в зависимости от логического отношения между их операндами. Так, операция && возвращает true, когда истинны (не равны нулю) оба его аргумента. Оператор || возвращает false только в том случае, если оба его аргумента ложны (равны нулю). Оператор ! инвертирует значение своего операнда с false на frue и наоборот.
Пример использования логических операций и операций сравнения приведен ниже.
#include "stdafx.h"
using namespace std;
main() {
float fa=2,fb=4;
cout<<"fa<fb "<<(fa<fb)<<"\n";
cout<<"fa>fb "<<(fa>fb)<<"\n";
cout<<"fa<=fb "<<(fa<=fb)<<"\n";
cout<<"fa>=fb "<<(fa>=fb)<<"\n";
cout<<"fa==fb "<<(fa==fb)<<"\n";
cout<<"fa!=fb "<<(fa!=fb)<<"\n";
cout<<"fa&&fb "<<(fa&&fb)<<"\n";
cout<<"fa||fb "<<(fa||fb)<<"\n";
getch();
}
Результат работы программы
Условная операция имеет следующий формат:
Условное выражение ? выражение1 : выражение2;
Если условное выражение true, то выполняется выражение1. Если условие false, то выполняется выражение2. Например,
var>7 ? х=11:у=7;
Часто выражения 1 и 2 используют одну и ту же переменную, которой присваивается либо одно, либо другое значение. Тогда условная операция записывается несколько иначе:
переменная=условное выражение ? выражение1 : выражение2;
Например,
char cS=x<=max?'Y':'N';
Если x<=max, то переменная cS принимает значение 'Y', если x>max, то cS будет равна 'N'.
Операция запятая (,) позволяет последовательно выполнить два выражения, записанные в одной строке. Результатом является значение выражения, расположенного справа от запятой. Синтаксис оператора следующий:
левое_выражение, правое_выражение
Чаще всего этот оператор применяется в цикле for, когда в условие цикла нужно включить проверку значений нескольких переменных. Например,
for(min=0,max=len-1;min<max;min++,max--) { … }
Операция sizeof служит для определения размера операнда в байтах. Она может использоваться как с обозначением переменной, так и с ее типом (в последнем случае операнд следует заключить в круглые скобки). Операция sizeof особенно полезна для определения размеров агрегатных переменных - массивов и структур. Например,
char cS[]="Операция sizeof";
int cSLen=sizeof cS;
Массив cS будет занимать 16 байт памяти.
Последовательность выполнения различных операций определяется компилятором. Если не учитывать порядок разбора выражения компилятором, могут быть получены неправильные результаты.
В таблице 3 перечислены все операции языка C++ в порядке снижения их приоритета и указано направление вычисления операндов (ассоциативность): слева направо или справа налево.
Таблица 3. Приоритет операций (от высокого к низкому)
Операция |
Описание |
Ассоциативность |
++ |
Постфиксный (префиксный) инкремент |
Слева направо |
-- |
Постфиксный (префиксный) декремент |
|
() |
Вызов функции |
|
[] |
Доступ к элементу массива |
|
-> |
Косвенный доступ к члену класса |
|
. |
Прямой доступ к члену класса |
|
! |
Логическое НЕ |
|
~ |
Побитовое НЕ |
|
- |
Унарный минус |
|
+ |
Унарный плюс |
|
& |
Взятие адреса |
|
* |
Раскрытие указателя |
|
sizeof |
Получение размерности выражения в байтах |
|
new |
Динамическое создание объекта |
|
delete |
Динамическое удаление объекта |
|
(тип данных) |
Приведение типа |
|
.* |
Прямой доступ к указателю на член класса (через объект) |
Слева направо |
->* |
Косвенный доступ к указателю на член класса (через указатель на объект) |
|
* |
Умножение |
Слева направо |
/ |
Деление |
|
% |
Деление по модулю |
|
+ |
Сложение |
Слева направо |
- |
Вычитание |
|
<< |
Сдвиг влево |
Слева направо |
>> |
Сдвиг вправо |
|
< |
Меньше |
Слева направо |
> |
Больше |
|
<= |
Меньше или равно |
|
>= |
Больше или равно |
|
== |
Равно |
Слева направо |
!= |
Не равно |
|
& |
Побитовое И |
Слева направо |
^ |
Побитовое исключающее ИЛИ |
Слева направо |
| |
Побитовое ИЛИ |
Слева направо |
&& |
Логическое И |
Слева направо |
II |
Логическое ИЛИ |
Слева направо |
?: |
Условное выражение |
Справа налево |
= |
Простое присваивание |
Справа налево |
*= |
Присваивание с умножением |
|
/= |
Присваивание с делением |
|
%= |
Присваивание с делением по модулю |
|
+= |
Присваивание со сложением |
|
-= |
Присваивание с вычитанием |
|
<<= |
Присваивание со сдвигом влево |
|
>>= |
Присваивание со сдвигом вправо |
|
&= |
Присваивание с побитовым И |
|
|= |
Присваивание с побитовым ИЛИ |
|
^= |
Присваивание с побитовым исключающим ИЛИ |
|
, |
Запятая |
Слева направо |
2.5. Стандартные библиотеки C++ и библиотечные
математические функции
Различным библиотечным функциям требуются различные заголовочные файлы. Заголовочные файлы, которые необходимы функции, указываются в ее описании. Например, функции sqrt() нужны объявления, содержащиеся в заголовочном файле math.h. В Microsoft Visual C++ Run-Time Library Reference перечислены все библиотечные функции и соответствующие заголовочные файлы.
Разработчиками компилятора C++ предусмотрены такие категории библиотек: классификации, преобразования, управления каталогами, диагностики, программы, ввода/вывода, интерфейсные, обработки, математические, управления памятью, управления процессом, стандартные, вывода текстовых окон, для обработки информации о времени и дате.
Стандартные библиотечные функции
int abs(int x);
double fabs(double x);
Возвращает целое (abs) или дробное (fabs) абсолютное значение аргумента, в качестве которого можно использовать выражение соответствующего типа.
double acos (double x);
double asin (double x);
double atan (double x);
long double acosl(long double x);
long double asinl(long double x);
long double atanl(long double x);
Возвращает выраженную в радианах величину угла, косинус, синус или тангенс которого передан соответствующей функции в качестве аргумента. Аргумент функции должен находиться в диапазоне от -1 до 1.
double cos (double x);
double sin (double x);
double tan (double x);
long double cosl(long double x);
long double sinl(long double x);
long double tanl(long double x);
Возвращает синус, косинус или тангенс угла. Величина угла должна быть задана в радианах.
double exp(double х);
long double exp(long double (x));
Возвращает значение, равное экспоненте аргумента (еx, где е — основание натурального логарифма).
double pow (double x, double y);
long double powl(long double (x), long double (y));
Возвращает значение, равное хУ.
double sqrt(double x);
Возвращает значение, равное квадратному корню из аргумента.
Заголовочный файл: <math.h>
int rand(void);
Возвращает случайное целое число в диапазоне от 0 до RAND_MAX. Перед первым обращением к функции rand необходимо инициализировать генератор случайных чисел. Для этого надо вызвать функцию srand.
void srand(unsigned x) ;
Инициализирует генератор случайных чисел. Обычно в качестве параметра функции используют переменную, значение которой предсказать заранее нельзя, например это может быть текущее время.
Заголовочный файл: <stdlib.h>
Приведенные ниже функции выполняют преобразование строк в числовое значение и чисел в строковое представление.
double atof(const char* s) ;
Возвращает дробное число, значение которого передано функции в качестве аргумента. Функция обрабатывает строку до тех пор, пока символы строки являются допустимыми. Строка может быть значением числа как в формате с плавающей точкой, так и в экспоненциальном формате.
int atoi(const char* s) ;
long atol(const char* s);
Возвращает целое соответствующего типа, изображение которого передано функции в качестве аргумента. Функция обрабатывает символы строки до тех пор, пока не встретит символ, не являющийся десятичной цифрой.
char *gcvt(double Значение, int Цифр, char* Строка);
Преобразует дробное число в строку. При преобразовании делается попытка получить указанное количество значащих цифр, а если это сделать невозможно, то число изображается в форме с плавающей точкой.
char* itoa (int Значение, char* Строка, int Основание);
char* ltoa (long Значение, char* Строка, int Основание);
char* ultoa(unsigned long Значение, char* Строка, int Основание);
Соответственно преобразуют целое, длинное целое и длинное беззнаковое целое в строку. Число изображается в указанной при вызове функции системе счисления.
Строка — указатель на строку, куда будет помещено изображение числа. Основание — задает основание системы счисления (от 2 до 36).
Максимальная длина строки, формируемой функцией itoa, — 17 байт, функциями Itoa и ultoa — 33 байта.
Заголовочный файл: <stdlib.h>
int sprintf(char *Строка, const char* Формат, СписокПеременных);
Выполняет форматированный вывод в строку.
СписокПеременных — разделенные запятыми имена переменных, задает переменные, значения которых должны быть выведены. Параметр Формат задает способ отображения значений переменных.
Действие функции sprintf аналогично действию функции printf, но вывод выполняется в строку-буфер, а не на экран.
Заголовочный файл: <stdio.h>
Библиотечные функции ввода-вывода
int printf(Формат, СписокПеременых);
Выводит на экран значения переменных. Формат вывода задается в строке форматирования, которая помимо спецификатора формата может содержать текст и управляющие символы. Значение первой переменной выводится в соответствии с первым спецификатором формата, второй — со вторым, и т. д.
Спецификаторы формата (необязательный параметр n задает ширину поля вывода).
Спецификатор |
Форма вывода |
%ni %nd |
Десятичное число со знаком |
%nu |
Беззнаковое целое десятичное число |
%n.mf |
Дробное число с десятичной точкой. Необязательный параметр m задает количество цифр дробной части |
%ne |
Дробное число с десятичной точкой или, если число не может быть представлено в форме с десятичной точкой, в экспоненциальной форме |
%ns |
Строка символов |
%nc |
Символ |
Управляющие и специальные символы.
Символ |
Действие |
\n |
Переводит курсор в начало следующей строки |
\t |
Переводит курсор в очередную позицию табуляции |
\\ |
Бэкслэш |
\’ |
Кавычка |
int scanf(const char* Формат, СписокАдресовПеременных);
Вводит с клавиатуры значения переменных, в соответствии с указанным спецификатором формата. Первая переменная получает значение в соответствии с первым спецификатором формата, вторая — со вторым и т. д.
Спецификатор |
Вводит |
%i %d |
Десятичное число со знаком |
%u |
Беззнаковое целое десятичное число |
%е %f |
Дробное число |
%s |
Строка символов |
%с |
Символ |
puts(const char* Строка);
Выводит на экран строку символов и переводит курсор в начало следующей строки экрана. В качестве параметра функции можно использовать строковую константу или строковую переменную.
Заголовочный файл: <stdio.h>
int putch(int с);
Выводит на экран символ.
Заголовочный файл: <conio.h>
int getch(void);
Возвращает код символа нажатой клавиши. Если нажата служебная клавиша, то функция getch возвращает 0. В этом случае, для того, чтобы определить, какая служебная клавиша нажата, нужно обратиться к функции getch еще раз.
Заголовочный файл: < conio.h >
Задача 1. Нахождение значения производной функции в точке.
Постановка задачи
Заданна функция . Найти ее производную в точке x=π/2 .
Для нахождения производной в точке используется известное выражение
.
Кроме того, так как π/2 ≈ 1,57 в качестве значения х выбираем 1,57.
Текст программы
#include <math.h>
#include <iostream>
int main()
{
double f1,f2,pf,x,dx;
dx=1.0e-11; // Выбираем приращение аргумента
x=1.57; // Выбираем точку для вычисления производной
f1=sin(x+dx); // Вычисляем значение функции в точке x+dx
f2=sin(x); // Вычисляем значение функции в точке x
pf=(f1-f2)/dx; // Находим значение производной
std::cout << "dsin(x)/dx=" << pf <<" x= "<<x;
getchar();
return 0;
}
Результат работы программы
Второй вариант написания программы. Вывод результатов в стиле С (с помощью функции printf).
#include <math.h>
#include <conio.h>
#include <stdio.h>
int main()
{
double f1,f2,pf,x,dx;
dx=1.0e-11;
x=1.57;
f1=sin(x+dx);
f2=sin(x);
pf=(f1-f2)/dx;
printf("dsin(x)/dx=%6.4f x=%4.2f",pf,x);
getch();
return 0;
}
Задания к задаче 1 лабораторной работы № 2
Таблица 1
№ |
Задание |
№ |
Задание |
1. |
12. |
||
2. |
13. |
||
3. |
14. |
||
4. |
15. |
||
5. |
16. |
||
6. |
17. |
||
7. |
18. |
||
8. |
19. |
||
9. |
20. |
||
10. |
21. |
||
11. |
22. |
Задача 2 Составить программу для нахождения значений выражений А и В по заданным значениям исходных данных x,y,z.
Исходные данные (по вариантам) находятся в таблице 2.
Таблица 2
№ |
Функции |
Исходные данные |
||
вар. |
|
x |
y |
z |
1 |
3.98 |
-1.62 |
0.52 |
|
2 |
-0.62 |
0.82 |
25 |
|
3 |
3.25 |
0.32 |
0.4 |
|
4 |
-0.62 |
3.32 |
5.4 |
|
5 |
17.4 |
10.3 |
0.82 |
|
6 |
1.62 |
-15.4 |
0.25 |
|
7 |
2.44 |
0.86 |
-0.16 |
|
8 |
0.33 |
0.02 |
32 |
|
9 |
3.25 |
4.2 |
-0.66 |
|
10 |
0.12 |
-8.75 |
0.76 |
|
11 |
1.53 |
-3.26 |
8.2 |
|
12 |
1.42 |
-1.22 |
3.52 |
|
13 |
3.74 |
-0.82 |
0.16 |
|
14 |
3.74 |
-0.82 |
0.16 |
|
15 |
0.42 |
-0.87 |
-0.47 |
|
16 |
-15.2 |
4.64 |
21.3 |
|
|
|
|
|
|
Продолжение таблицы 2
17 |
16.5 |
-2.75 |
0.15 |
|
18 |
-17.2 |
6.33 |
3.25 |
|
19 |
-2.23 |
-0.82 |
15.2 |
|
20 |
1.82 |
18.5 |
-3.29 |
|
21 |
47.8 |
-5.52 |
-2.3 |
|
22 |
-0.85 |
1.25 |
-0.22 |
|
23 |
3.74 |
-12.5 |
21.3 |
|
24 |
3.25 |
2.98 |
12.5 |
|
25 |
0.22 |
-6.72 |
10.2 |
|
26 |
12.6 |
-2.25 |
3.2 |
|
27 |
0.113 |
0.512 |
0.45 |
|
28 |
0.45 |
0.86 |
-0.16 |
|
29 |
0.98 |
-2.62 |
0.51 |
|
30 |
10.3 |
-6.2 |
1.12 |
Задание 3. (Дополнительно на оценку 11 и 12). Вычисление значения отношения.
Постановка задачи
Выпуклый четырехугольник ABCD задается длинами своих сторон и длиной диагонали AC. Новый четырехугольник A1B1C1D1 строится следующим образом: сторона AB продлевается в сторону вершины B так, чтобы полученный отрезок AB1 был в 2 раза длиннее отрезка AB, сторона BС продлевается в сторону вершины С так, чтобы полученный отрезок BС1 был в 2 раза длиннее отрезка BС и так далее (см. рис.).
По заданным значениям длин сторон AB, BC, CD, DA и длине диагонали AC вычислить отношение площади четырехугольника A1B1C1D1 к площади четырехугольника ABCD.
Контрольные вопросы
1.Поясните смысл понятия оператор.
2.Что понимается под типом данных?
3.Какая информация сообщается компилятору при объявлении переменных и констант?
4.Дайте определение выражения.
5.Укажите правила вычисления выражений.
6.Приведите примеры операций с одинаковым приоритетом.
7.Укажите операции с наивысшим и наименьшим приоритетом.
8.Перечислите ключевые слова, используемые при объявлении стандартных типов данных.