1. Память компьютера. Можно считать, что это один большой массив, каждая ячейка -- 1 байт. При определении переменной под нее выделяется память -- несколько последовательных байт, количество равно размеру типа переменной (int -- 4 байта, char -- 1, long long -- 8). Со структурами чуть сложнее, но можно считать, что размер структуры равен сумме размеров всех элементов. Например, наша любимая struct pt { int x, y; }; занимает восемь байт, причем первые четыре всегда одна и таже переменная, последние четыре -- тоже всегда одна и таже (другая). В каком порядке -- не знаю, скорее всего сначала x, потом y. Узнать размер типа T -- sizeof(T). Возвращает количество байт, занимаемых типом T. Массив размера N типа T -- это большой непрерывный кусок памяти, где подряд выделена память под N переменных типа T. Таким образом, всего выделится N * sizeof(T) байт. Двумерный массив N на M -- это большой непрерывный кусок памяти, где подряд выделена память под N одномерных массивов размера M. и т.д Адрес переменной -- место (номер байта), где она хранится. Когда мы выделяем переменную (на статической памяти), компилятор как-то сам запоминает ее адрес. Когда мы выделяем массив, он запоминает лишь адрес первого (нулевого) элемента. 2. Указатели (pointer). Указатель -- это переменная, хранящая адрес другой переменной =). Указатель на что-то -- это указатель, хранящий адрес этого чего-то. Указатель на такой-то тип -- это указатель, хранящий адрес переменной такого типа. У указателя на тип T есть свой тип -- T*. Например, указатель на int -- это int*. Только звездочку надо писать перед каждой переменной: int *a, *b, *c; Поэтому я звездочку ставлю рядом с именем переменной, а не с типом. К указателю можно прибавлять и вычитать целое число: T* p1; ... T* p2 = p1 + 5; // Теперь p2 хранит номер байта, на 5 * sizeof(T) больший, чем p1. Указатели на один и тот же тип можно вычитать друг из друга -- получится количество элементов этого типа, влезающих между ними: T* p1, p2; ... int n = p2 - p1; Пусть есть указатель T* p; тогда можно делать p++; ++p; p--; --p; аналогично int'ам. Теперь. Пусть есть массив T a[100]; тогда a -- это указатель на первый (нулевой) элемент. А (a + 1) -- указатель на следующий, и т.д. Так, можно вывести первые n объектов: for (int i = 0; i < n; i++) cout << *(a + i) << ' '; или так: for (int *it = a; it != a + n; it++) cout << *it << ' '; (Последний вариант можно назвать итерированием по массиву a, поэтому указатели, с которыми такое целеобразно, еще называют итераторами (iterator)) То есть, когда ты пишешь sort(a, a + n) -- ты передаешь указатели на нулевой и на n-ный в 0-нумерации элементы. Есть три новые операции: 1. Взятие адреса: T a; T* p = &a; теперь p хранит адрес a. Например, в scanf мы передаем именно указатели на переменные. 2. Разыменовывание: T a = 1; T* p = &a; cout << *p; // выведется 1 *p = 6; // теперь a = 6 cout << a << ' ' << *p; // выведется 6 6 a = 10; cout << a << ' ' << *p; // выведется 10 10 *p возвращает ссылку (про это попозже) на ту переменную, на которую указывал указатель p. 3. Если это указатель на структуру или класс, то можно обратиться к его члену: pt a(-3, 45); pt *b = &a; cout << b->x << ' ' << b->y << endl; //-3 45 b->x = 0; cout << b->x << ' ' << b->y << endl; //0 45 cout << a.x << ' ' << a.y << endl; //0 45 Можно даже так: struct pt { ... //все как обычно double len() { return sqrt(x * x + y * y); } }; cout << b->len() << endl; //45 p->smth возвращает ссылку на поле smth той переменной, на которую указывает p; p->smth идентично (*p).smth И про массивы: насколько я понимаю, a[i] превращается в *(a + i) при компиляции. (А a[i][j] -- в *(a + i * size2 + j) -- (size2 -- размер второго измерения массива, поэтому размер второго и дальше измерений должны быть константы). Для вектора, соответственно, v[i] превращается в *(v.begin() + i). Указатель занимает в память 4 (32-битная машина), или 8 (64-битная) байт. Константа NULL -- указатель в никуда. при попытке выполнить к ней операции 2 или 3 получится runtime error. Можно динамически выделять память, используя зарезервированное слово new: int *a, *b, *c; a = new int; //указатель на int b = new int(5); //сразу инициализируем c = new int[10]; //массив из 10 элементов pt *p = new pt(-3, 5); //точка, так можно написать только если есть соответствующий конструктор Можно так же удалять, используя delete и delete[] (последний -- для массивов): delete a; delete b; delete[] c; delete p; //вызовется деструктор 3. Ссылки (reference). Это такой хитрый тип... Он чем-то похож на указатели. Они ссылаются на другие переменные. Ссылка на тип T -- это тип T&. Опять же, амперсанд надо писать перед каждой переменной: int &a, &b, &c; Весит столько же, сколько и указатели. Что они умеют: int a; int &ref(a); //инициализируем ссылку на a. Ссылку обязательно надо инициализировать при объявлении, иначе будет ошибка компиляции. a = 5; // пока ничего нового cout << ref << ' ' << a << endl; // 5 5 (можно использовать значение переменной, на которую ссылка) ref = 6; // присваиваем a = 6 cout << ref << ' ' << a << endl; // 6 6 (можно изменять значение переменной, на которую ссылка) pt a; pt &b(a); b.x = 3; b.y = 4; cout << a.x << ' ' << a.y << ' ' << a.len() << endl; // 3 4 5 cout << b.x << ' ' << b.y << ' ' << b.len() << endl; // 3 4 5 например, если из функции надо вернуть несколько параметров, то можно их передать по ссылке: void set_zeroes(int &a, int &b) { a = 0; b = 0; } set_zeroes(ans1, ans2); // теперь ans1 и ans2 равны 0 4. И про магическое слово const. Допустим, мы хочешь отсортировать точки по x. bool operator<(pt a, pt b) { return a.x < b.x; } pt p[100]; sort(p, p + n); Все прекрасно, за исключением одного: каждый раз при вызове оператора< скопируются две точки, чтобы передать их как параметры. Но зачем? Нам же только иксы сравнить надо! Давайте передавать ссылки, будет работать быстрее: bool operator<(pt &a, pt &b) ... Но так оно не скомпилируется =) Действительно, вдруг мы как-то изменяем a и b внутри operator<, это попортит всю сортировку. Выход -- написать так: bool operator<(const pt &a, const pt &b) ... Словом const мы говорим компилятору, что исходные объекты (на которые ссылаются a и b) не изменятся (они будут read-only). Так все будет работать. (Если, конечно, a и b не меняются внутри operator<, иначе будет ошибка компиляции). То же самое можно сделать с указателями: void output(const int *a) { cout << *a; } Но если мы вдруг попытаемся изменить *a: void set_zero(const int *a) { *a = 0; } То будет ошибка компиляции: error: assignment of read-only location ‘* a’