Выражение должно иметь тип указателя. Набор для практики


. Указатель – это объект, содержащий адрес другого объекта и позволяющий косвенно манипулировать этим объектом. Обычно указатели используются для работы с динамически созданными объектами, для построения связанных структур данных, таких, как связанные списки и иерархические деревья, и для передачи в функции больших объектов – массивов и объектов классов – в качестве параметров.
Каждый указатель ассоциируется с некоторым типом данных, причем их внутреннее представление не зависит от внутреннего типа: и размер памяти, занимаемый объектом типа указатель, и диапазон значений у них одинаков. Разница состоит в том, как компилятор воспринимает адресуемый объект. Указатели на разные типы могут иметь одно и то же значение, но область памяти, где размещаются соответствующие типы, может быть различной:

  • указатель на int, содержащий значение адреса 1000, направлен на область памяти 1000-1003 (в 32-битной системе);
  • указатель на double, содержащий значение адреса 1000, направлен на область памяти 1000-1007 (в 32-битной системе).

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

Int *ip1, *ip2; complex *cp; string *pstring; vector *pvec; double *dp;

Указатель обозначается звездочкой перед именем. В определении переменных списком звездочка должна стоять перед каждым указателем (см. выше: ip1 и ip2). В примере ниже lp – указатель на объект типа long, а lp2 – объект типа long:

Long *lp, lp2;

В следующем случае fp интерпретируется как объект типа float, а fp2 – указатель на него:

Float fp, *fp2;

Оператор разыменования (*) может отделяться пробелами от имени и даже непосредственно примыкать к ключевому слову типа. Поэтому приведенные определения синтаксически правильны и совершенно эквивалентны:

//внимание: ps2 не указатель на строку! string* ps, ps2;

Можно предположить, что и ps, и ps2 являются указателями, хотя указатель – только первый из них.
Если значение указателя равно 0, значит, он не содержит никакого адреса объекта.
Пусть задана переменная типа int:

Int ival = 1024;

Ниже приводятся примеры определения и использования указателей на int pi и pi2:

//pi инициализирован нулевым адресом int *pi = 0;
// pi2 инициализирован адресом ival
int *pi2 = &ival;
// правильно: pi и pi2 содержат адрес ival
pi = pi2;
// pi2 содержит нулевой адрес
pi2 = 0;

Указателю не может быть присвоена величина, не являющаяся адресом:

// ошибка: pi не может принимать значение int pi = ival

Точно так же нельзя присвоить указателю одного типа значение, являющееся адресом объекта другого типа. Если определены следующие переменные:

Double dval; double *ps = &dval;

то оба выражения присваивания, приведенные ниже, вызовут ошибку компиляции:

// ошибки компиляции // недопустимое присваивание типов данных: int* <== double* pi = pd pi = &dval;

Дело не в том, что переменная pi не может содержать адреса объекта dval – адреса объектов разных типов имеют одну и ту же длину. Такие операции смешения адресов запрещены сознательно, потому что интерпретация объектов компилятором зависит от типа указателя на них.
Конечно, бывают случаи, когда нас интересует само значение адреса, а не объект, на который он указывает (допустим, мы хотим сравнить этот адрес с каким-то другим). Для разрешения таких ситуаций введен специальный указатель void, который может указывать на любой тип данных, и следующие выражения будут правильны:

// правильно: void* может содержать // адреса любого типа void *pv = pi; pv = pd;

Тип объекта, на который указывает void*, неизвестен, и мы не можем манипулировать этим объектом. Все, что мы можем сделать с таким указателем, – присвоить его значение другому указателю или сравнить с какой-либо адресной величиной. (Более подробно мы расскажем об указателе типа void в разделе 4.14 .)
Для того чтобы обратиться к объекту, имея его адрес, нужно применить операцию разыменования, или косвенную адресацию, обозначаемую звездочкой (*). Имея следующие определения переменных:

Int ival = 1024;, ival2 = 2048; int *pi = &ival;

// косвенное присваивание переменной ival значения ival2 *pi = ival2;
// косвенное использование переменной ival как rvalue и lvalue
*pi = abs(*pi); // ival = abs(ival);
*pi = *pi + 1; // ival = ival + 1;

Когда мы применяем операцию взятия адреса (&) к объекту типа int, то получаем результат типа int*
int *pi = &ival;
Если ту же операцию применить к объекту типа int* (указатель на int), мы получим указатель на указатель на int, т.е. int**. int** – это адрес объекта, который содержит адрес объекта типа int. Разыменовывая ppi, мы получаем объект типа int*, содержащий адрес ival. Чтобы получить сам объект ival, операцию разыменования к ppi необходимо применить дважды.

Int **ppi = π int *pi2 = *ppi;
cout << "Значение ival\n" << "явное значение: " << ival << "\n"
<< "косвенная адресация: " << *pi << "\n"
<< "дважды косвенная адресация: " << **ppi << "\n"

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

Int i, j, k; int *pi = &i; // i = i + 2
*pi = *pi + 2; // увеличение адреса, содержащегося в pi, на 2
pi = pi + 2;

К указателю можно прибавлять целое значение, можно также вычитать из него. Прибавление к указателю 1 увеличивает содержащееся в нем значение на размер области памяти, отводимой объекту соответствующего типа. Если тип char занимает 1 байт, int – 4 и double – 8, то прибавление 2 к указателям на char, int и double увеличит их значение соответственно на 2, 8 и 16. Как это можно интерпретировать? Если объекты одного типа расположены в памяти друг за другом, то увеличение указателя на 1 приведет к тому, что он будет указывать на следующий объект. Поэтому арифметические действия с указателями чаще всего применяются при обработке массивов; в любых других случаях они вряд ли оправданы.
Вот как выглядит типичный пример использования адресной арифметики при переборе элементов массива с помощью итератора:

Int ia; int *iter = &ia; int *iter_end = &ia;
while (iter != iter_end) {
do_something_with_value (*iter);
++iter;
}

Упражнение 3.8

Даны определения переменных:

Int ival = 1024, ival2 = 2048; int *pi1 = &ival, *pi2 = &ival2, **pi3 = 0;

Что происходит при выполнении нижеследующих операций присваивания? Допущены ли в данных примерах ошибки?

(a) ival = *pi3; (e) pi1 = *pi3; (b) *pi2 = *pi3; (f) ival = *pi1; (c) ival = pi2; (g) pi1 = ival; (d) pi2 = *pi1; (h) pi3 = &pi2;

Упражнение 3.9

Работа с указателями – один из важнейших аспектов С и С++, однако в ней легко допустить ошибку. Например, код

Pi = &ival; pi = pi + 1024;

почти наверняка приведет к тому, что pi будет указывать на случайную область памяти. Что делает этот оператор присваивания и в каком случае он не приведет к ошибке?

Упражнение 3.10

Данная программа содержит ошибку, связанную с неправильным использованием указателей:

Int foobar(int *pi) { *pi = 1024; return *pi; }
int main() {
int *pi2 = 0;
int ival = foobar(pi2);
return 0;
}

В чем состоит ошибка? Как можно ее исправить?

Упражнение 3.11

Ошибки из предыдущих двух упражнений проявляются и приводят к фатальным последствиям из-за отсутствия в С++ проверки правильности значений указателей во время работы программы. Как вы думаете, почему такая проверка не была реализована? Можете ли вы предложить некоторые общие рекомендации для того, чтобы работа с указателями была более безопасной?

Введение

В языке С кроме базовых типов разрешено вводить и использовать производные типы. Стандарт определяет три способа получения производных типов:

Массив элементов заданного типа;

Указатель на объект заданного типа;

Функция, возвращающая значение заданного типа.

Язык С предусматривает два типа указателей – указатели на объекты и указатели на функции, причем указатели на объекты могут быть типизированными или без типа.

Каждая переменная в программе – это объект, имеющий имя и значение. В машине имя переменной соответствует адресу участка памяти, а значение – содержимому памяти. Чтобы получить адрес переменной в явном виде используют унарный оператор &. Оператор неприменим к выражениям, константам, битовым полям, регистровым переменным или внешним объектам (файлам).

Для сохранения, преобразования и передачи адресов переменных в языке С введены переменные типа "указатель".

Опр: Указатель – это переменная, значением которой служит либо адрес объекта некоторого типа, либо пустой адрес NULL.

Для определения и описания указателей используется унарный оператор косвенной адресации "*":

Здесь c,i,f – переменные-указатели на объекты символьного, целого и вещественного типов соответственно - машинные адреса этих объектов. Операндом косвенной адресации всегда является указатель. Результат этой операции – содержимое объекта, который адресует указатель. В нашем случае *c – символьная переменная, *i – переменная целого типа, а *f- переменная вещественного типа.

3.3 Действия с указателями. Адреса указатели и массивы

Допустимы следующие основные операции над указателями:

Объявление указателя;

Инициализация указателя;

Получение адреса самого указателя;

Унарные операции изменения значения указателя;

Аддитивные операции;

Оператции сравнения.

Рассмотрим эти операции подробнее на примерах.

char *c,ch="A";/

int *i,*k,ii,kk,date=1998;

float *f,ff=123.456;

i=&date; /*однотипное присваивание*/

i=k;/*однотипное присваивание */

c=NULL;/*приваивание пустого адреса*/

int a=3;//Инициализация целой переменной

int* b=&a;// Инициализация указателя адресом целой переменной

int *c(&a);// Инициализация указателя адресом целой переменной

int *d=c;// Инициализация указателя другим указателем

Замечание: Инициализация указателей при объявлении является средством повышения надежности кода. Если неясно, какой адрес должен быть выбран для содержимого объявляемого указателя, то допустима инициализация указателя нулевым значением или макросом NULL:


int* j = NULL;//Допустимо стандартом 1998

int*k=0; //Допустимо стандартом 1998

Можно объявлять:

Не константые указатели на не константы:

тип * имя_указателя; - можно изменять и указатель и переменную, на которую он указывает;

Не константные указатели на константы:

const тип* имя_указателя; - можно изменять указатель, но нельзя изменять переменную, на которую он указывает;

Константные указатели на не константы:

тип* const имя_указателя; нельзя изменять указатель, но можно изменять переменную, на которую он указывает;

Константные указатели на константы:

const тип* const имя_указателя; нельзя изменять указатель, нельзя изменять переменную, на которую он указывает.

int *const p1 = &c;//OK

int *const p2 = &d;//NOK – здесь указатель константный, но указывать он может на переменную, а не на константу.

Иногда требуется совместить адреса объектов разных типов. В этом случае используется механизм "приведения типов":

c=(char *)k;/*c указывает на целое как на символ*/

void* vpa = (void*)&a;//Преобразование int* в void* стиль С

void* vp= (void*)pa;//Преобразование int* в void* стиль С

void* vp= reinterpret_castpa;//Преобразование int* в void* стиль С++

Подобно другим переменным, указатели имеют имя, адрес в памяти и значение. Поэтому выражение &ИМЯ_УКАЗАТЕЛЯ определяет, где в памяти размещен указатель:

С помощью унарных арифметических операторов -– и ++ значения указателей меняются по разному, в зависимости от типа данных указателя. Единицей в этом случае служит размер в байтах, занимаемый объектом конкретного типа. Оператор инкремента приведет к увеличению значения адреса на 1 для типа char, 2 для типа int, 4 для типа float и т.д.

Аддитивные операторы применимы к указателям с некоторыми ограничениями. Нельзя суммировать указатели, но можно прибавить целую величину. Вычисляемое значение снова зависит от типа объекта, с которым связан указатель, как и в предыдущем случае.

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

Тип разности указателей определяется по разному в зависимости от компилятора. Поэтому в заголовочном файле stddef.h определено имя ptrdiff_t с помощью которого обозначается тип разности в конкретной реализации.

#include

#include

int x;/*массив из 5 элементов*/

printf("\n j=%d",(int)j);/*явное преобразование типа*/

Результат j=4.

Арифметические операторы и указатели

Унарные адресные операторы "*" и "&" имеют более высокий приоритет, чем арифметические операторы. Рассмотрим пример.

float a=4.0, *u, z;

Результат: a=10, z=5, u не изменилось.

При использовании адресной операторы "*" рекомендуется применять круглые скобки для предотвращения случайного сочетания знаков деления и разыменования:

a/*u ; /* символ начала комментария*/

a/(*u); /* корректно*/

Унарные операторы "*","--","++" имеют одинаковый приоритет и при размещении рядом выполняются справа налево.

#include

#define print(y,x) printf("\n"#y" равно %d "#x" равно %u",y,x)

int x={0,2,4,6},*i,y;

i=&x; /*i адрес x*/

y=*i; /*y=0 i=&x*/

y=*i++; /*y=0 i=&x*/

y=++*i; /*y=3 i=&x*/

y=*++i; /*y=4 i=&x*/

y=(*i)++; /*y=4 i=&x*/

y=++(*i); /*y=6 i=&x*/

Результаты работы для BORLAND C++ 3.1

y равно 0 i равно 65518

y равно 0 i равно 65520

y равно 3 i равно 65520

y равно 4 i равно 65522

y равно 4 i равно 65522

y равно 6 i равно 65522

Указатели и операторы сравнения

К указателям можно применять операторы сравнения и использовать указатели в отношениях. Но сравнивать указатели можно только либо с другими указателями того же типа, либо с константой NULL, либо с нулем. Допустимые действия над указателями сведем в таблицу.

Указатели на объекты

В общем случае синтаксис определения указателя на объект:

Тип*Описатель;

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

Тип задает тип объекта, адрес которого будет содержать определяемая переменная и может соответствовать базовому, пустому (свободному, родовому, то есть типу void), перечислению, структурному типу и типу объединения. Реально указатель на void ни на что не указывает, но обладает способностью указывать на область любого размера после его типизирования каким-либо объектом.

Описатель – это идентификатор, определяющий имя объявляемой переменой типа указатель или конструкция, которая организует непосредственно доступ к памяти. Описателю обязательно должна предшествовать звездочка (*).

Знак "*" является унарной операцией косвенной адресации, его операнд – указатель, а результат – адрес объекта, на который указывает операнд. Адресация является косвенной , так как обращение к области памяти осуществляется не напрямую по адресу (например, 1А2В), а через объект, которому в памяти соответствует определенный участок. Объем памяти, который выделяется для хранения данных, определяется типом данных и моделью памяти. Для приведенной на рисунке 2 модели памяти адресом переменной типа float с именем summa является 0012FF48, адресом переменной типа int с именем date является 0012FF54, адресом переменной типа char с именем ch является 0012FF63.

Машинный адрес 0012FF48 0012FF49 0012FF4A 0012FF4B 0012FF54 0012FF55 0012FF56 0012FF57 0012FF63
байт байт байт байт байт байт байт байт байт
Значение в памяти 2.015*10 -6 "G"
Имя summa date ch

Рис. 2. Адресация типов в С++

Примеры определения указателей:

/*указатель Р может содержать адрес объекта типа int*/

/*указатель s может содержать адрес объекта типа float*/

Синтаксис объявления указателя на объект базового типа:

Тип*ИмяУказателя;

где ИмяУказателя – идентификатор.

Например,

char *s; //переменная s – указатель на объект типа char

double *x; /*переменная х – указатель на объект типа

double, вещественного числа с плавающей

точкой удвоенной точности*/

int *k, *ff; //k, ff – указатели на объекты целого типа

int *p, y; /*р – указатель на объект типа int,

y – целочисленная переменная и не является

указателем*/

int x, *p; /*х – целочисленная переменная и не является

указателем,

р – указатель на объект типа int*/

Например:

int i;//целая переменная

const int ci=1; //целая константа

int *pi; //указатель на целую переменную

const int *pci; //указатель на целую константу

int *const cpi; //указатель-константа на целую переменную

const int *const cpc;

При объявлении указателя его можно сразу проинициализировать (задать значение):

int *pi=&i; //указатель на целую переменную

const int *pci=&ci; //указатель на целую константу

int *const cpi=&i;

//указатель-константа на целую переменную

const int *const cpc=&ci;

//указатель-константа на целую константу

Если модификатор const относится к указателю (т.е. находится между именем указателя и *), то он запрещает изменение значения указателя, а если он находится слева от типа (т.е. слева от *), то он запрещает изменение значения, на которое указывает указатель.

Способы инициализации указателя

1. Присваивание указателю адреса области памяти существующего объекта:

· с помощью операции получения адреса:

· с помощью проинициализированного указателя

2. Присваивание указателю адреса области памяти в явном виде:

char *cp=(char*)0х В800 0000;

где 0х В800 0000 – шестнадцатеричная константа, (char*) – операция приведения типа.

3. Присваивание указателю пустого значения:

int *N=NULL; или int *N=0;

Спецификатор указателя при форматированном выводе

Если на экран необходимо вывести адрес, следует применять спецификатор %p.

%p – спецификатор указателя.

Этот спецификатор формата заставляет функцию printf() выводить на экран адрес, формат которого совместим с типом адресации, принятой в компьютере.

Операции с указателями

С указателями можно выполнять следующие операции:

· разыменование (*) – получение значения величины, адрес которой хранится в указателе;

· взятие адреса (&);

· присваивание;

· арифметические операции

§ сложение указателя только с константой,

§ вычитание: допускается разность указателей и разность указателя и константы,

§ инкремент (++) увеличивает значение указателя на величину sizeof(тип);

§ декремент (--) уменьшает значение указателя на величину sizeof(тип);

· сравнение;

· приведение типов.

Пример 1. Демонстрация ситуации, когда указатели различных типов указывают на одно и то же место в памяти. Однако при разыменовании получаются разные результаты.

// Выбор данных из памяти с помощью разных указателей

// Использование функций приведения типов

#include "stdafx.h"

#include

using namespace std;

unsigned long L=12345678;

char *cp=(char*)&L;

int *ip=(int*)&L;

long *lp=(long*)&L;

cout <<"\n&L = "<<&L;

cout <<"\nL = "<

cout <<"\n*cp = "<<*cp;

cout <<"\n*ip = "<<*ip;

cout <<"\n*lp = "<<*lp;

system("pause");

Пример 2.

//Операции над указателями

#include "stdafx.h"

#include

using namespace std;

int _tmain(int argc, _TCHAR* argv){

int *sa, *sb, *sc;

cout << "a = "; cin >> a;

cout << "b = "; cin >> b;

sb=&b;//инициализация указателей через взятие адреса

//присваивание указателю значения выражения

ca=sa;//присваивание указателю значения другого указателя



*sa=12;//присваивание указателю значения константы

cout << "\n*ca = " << *ca;

cout << "\n*sa = " << *sa;

cout << "\n*cb = " << *cb;

cout << "\n*sb = " << *sb;

cout << "\n*sc = " << *sc;

cout << "\nca = " << ca;

cout << "\ncb = " << cb;

cout << "\nsc = " << sc;

cout << "\na = " << a;

cout << "\nb = " << b;

cout << "\nc = " << c;

cout << "\n&a = " << &a;

cout << "\n&b = " << &b;

cout << "\n&c = " << &c;

cout << "\n*&a = " << *&a;

cout << "\n*cb-*ca = " << *cb-*ca;

cout << "\n*cb+*ca = " << *cb+*ca;

*cb=+2; //сложение с константой

cout << "\ncb = " << cb;

cb++; //инкремент

cout << "\ncb = " << cb;

ca--; //декремент

cout << "\ncа = " << ca;

/*разность указателей - разность их значений, деленная

на размер типа в байтах*/

cout << "\ncb-ca = " << cb-ca;

system("pause");

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

Пример 3.

#include "stdafx.h"

#include

using namespace std;

int _tmain(int argc, _TCHAR* argv){

//сравниваем указатели

if (xptr == yptr)

cout << "Указатели равны\n";

cout << "Указатели неравны\n";

//сравниваем значения, на которое указывает указатель

if (*xptr == *yptr) {

cout << "Значения равны\n";

cout << "Значения неравны\n";}

system("pause");

В приведенном примере результат первой операции сравнения будет ложным, а второй – истинным, поскольку переменные x и y имеют одно и то же значение.

Ключевые термины

Адрес объекта – это адрес области оперативной памяти, по которому хранится объект в соответствии с особенностями представления типа.

Инициализация указателя – это определение значения указателя.

Косвенная адресация – этообращение к области памяти не напрямую, по адресу, а через объект, которому в памяти соответствует определенный участок.

Непосредственно производные типы – это типы, которые являются производными от некоторых существующих типов, реализуя типы указателей, ссылки, функции преобразования типов.

Переименование типов – это задание нового имени для существующего типа.

Производные типы данных – это типы, полученные на основе более простых базовых типов.

Разыменование – это операция получения значения объекта, адрес которого хранится в указателе;

Составные производные типы – это типы, являющиеся производными от различных существующих или ранее объявленных типов.

Указатель – это именованный объект, предназначенный для хранения адреса области памяти.

Указатель на константу – это указатель на такой объект, значение которого нельзя изменить в процессе выполнения программы.

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

Указатель-константа на константу – это указатель, для которого невозможно изменение как самого указателя, так и значения адресуемого объекта.

Краткие итоги

1. В языке С++ производные типы данных классифицируют в зависимости от построения на производные и непосредственно производные.

2. Для экономии памяти и времени, затрачиваемого на обращение к данным, в программах используют указатели на объекты.

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

4. Указатель может быть константой или переменной, а также указывать на константу или переменную.

5. Указатель типа void указывает на область памяти любого размера. Разыменование такого указателя необходимо проводить с операцией приведения типов.

6. До первого использования в программе объявленный указатель необходимо проинициализировать.

7. С помощью указателей можно выполнять косвенную адресацию объектов.

8. Над указателями определены операции: разыменование, взятие адреса, декремент, инкремент, увеличение (уменьшение) на целую константу, разность, определение размера.

9. Над указателями определены операции сравнения.

Набор для практики

Вопросы

1. Почему указатель не может существовать как самостоятельный тип?

2. С какой целью в программе может быть использован указатель типа void?

3. Что будет являться результатом разыменования указателя типа void без приведения типов?

4. Как изменится значение указателя после применения к нему операции инкремента (декремента)?

5. Почему для указателей определены сложение и вычитание только с целыми константами?

6. В чем отличие указателя на константу от указателя-константы?

7. Два указателя разных типов указывают на одно и то же место в памяти. Сравните результаты операций разыменования и взятия адреса с такими указателям. Сравните значения указателей.

8. Если объект занимает в памяти несколько байтов, то какой адрес является значением указателя на этот объект?

9. Каким образом при разыменовании указателей становится известно, сколько байтов памяти доступно?

Упражнения

1. Наберите коды программ из Примеров 1-3 . Выполните компиляцию и запуск программ.

2. В программе определите и инициализируйте переменную типа double, указатель double * и указатель типа void *. Присвойте указателям адрес переменной. Напечатайте адрес переменной, значения указателей и значения, получаемые при разыменовании указателей. Чтобы продемонстрировать роли и последовательность выполнения унарных операций получения адреса & и разыменования *, выведите на печать значение выражения *&имя_переменной.

3. Задано натуральное число. Разместите в памяти последовательно все его цифры, используя указатели и операции над ними.

4. Определите и инициализируйте переменную типа double. Определите указатели char *, int *, double *, void *, инициализируйте их адресом переменной. Напечатайте значения указателей, их размеры и длины участков памяти, которые связаны с выражениями, разыменовывающими указатели.

Литература

1. Керниган, Б. Язык программирования Си / Б. Керниган, Д. Ритчи. – М.: Вильямс, 2007. – 304 с.

2. Подбельский, В.В. Практикум по программированию на языке Си: учеб. пособие / В.В. Подбельский. – М.: Финансы и статистика, 2004. – 576 с.

3. Подбельский, В.В. Программирование на языке Си: учеб. пособие / В.В. Подбельский, С.С. Фомин. – М.: Финансы и статистика, 2004. – 600 с.

4. Подбельский, В.В. Язык Си++: учеб. пособие / В.В. Подбельский. – М.: Финансы и статистика, 2005. – 560 с.

5. Романов, Е.Л. Практикум по программированию на языке С++: учеб. пособие / Е.Л. Романов. – СПб: БХВ-Петербург, 2004. – 432 с.

6. С/С++. Структурное программирование: практикум / Т.А. Павловская, Ю.А. Щупак. – СПб: Питер, 2004. – 239 с.

Выбор редакции
Н. С. Хрущёв со своей первой женой Е. И. Писаревой. В первый раз Никита Хрущёв женился ещё в 20-летнем возрасте на красавице Ефросинье...

Черехапа редко балует нас промокодами. В июле наконец-то вышел новый купон на 2019 год. Хотите немного сэкономить на страховке для...

Спор можно открыть не раньше чем через 10 дней, после того как продавец отправит товар и до того как Вы подтвердите получение товара, но...

Рано или поздно, каждый покупатель сайта Алиэкспресс сталкивается с ситуацией, когда заказанный товар не приходит. Это может случится из...
12 января 2010 года в 16 часов 53 минуты крупнейшее за последние 200 лет землетрясение магнитудой 7 баллов в считанные минуты погубило,...
Незнакомец, советуем тебе читать сказку "Каша из топора" самому и своим деткам, это замечательное произведение созданное нашими предками....
У пословиц и поговорок может быть большое количество значений. А раз так, то они располагают к исследованиям большим и малым. Наше -...
© Зощенко М. М., наследники, 2009© Андреев А. С., иллюстрации, 2011© ООО «Издательство АСТ», 2014* * *Смешные рассказыПоказательный...
Флавий Феодосий II Младший (тж. Малый, Юнейший; 10 апр. 401 г. - † 28 июля 450 г.) - император Восточной Римской империи (Византии) в...