Указатели, поясните тупому.

Уважаемые. Прошу помощи! Никак до меня не дойдет великое значение указателей.
Для чего они собственно нужны?

Из урока на этом сайте:

"...играя в тот же Battlefield, геймер в каждый новый момент времени видит различные объекты на экране монитора, например сейчас я стреляю во врага, а через долю секунды он уже падает убитым, создавая вокруг себя множество спецэффектов, таких как пыль, тени, и т.п.
Естественно, все это занимает какое-то место в оперативной памяти компьютера. Если не уничтожать неиспользуемые объекты, очень скоро они заполнят весь объем ресурсов ПК.
По этим причинам, в большинстве языков, в том числе и C/C++, имеется понятие указателя."

Не могу связать одно с другим. Очистку памяти от объектов (переменных) и наличие указателей.

Может автор хотел сказать, что обычные переменные нельзя удалить из памяти и поэтому вводятся указатели?

string a1=«hiya!»;
string sp1 = &a1;
cout <<
sp1 << endl;

Что должно вывестись на экран? номер ячейки памяти,где хранится а1 или значение переменной а1?

Чем больше читаю и смотрю примеров, тем больше тупею.

(1) Очистка памяти и указатели связаны достаточно косвенно ))

(2) Обычные переменные (автоматические) очень здорово удаляются из памяти. Причём автоматически ))

(3) А самому проверить свой код религия не позволяет? В нём, кстати, ошибка. Должно быть так:

    string a1 = "hiya!";
    string *sp1 = &a1;
    cout << sp1 << endl;

Будет выведен адрес переменной a1.

Но с типом (точнее говоря, с классом) string не всё так просто. Поскольку это не элементарный тип вроде int или char. Например, фрагмент

    string a1 = "hiya!";
    string *sp1 = &a1;
    cout << *sp1 << endl;

выведет строку hiya!. А фрагмент

    string a1 = "hiya!";
    char *sp1 = (char *)&a1;
    cout << *sp1 << endl;

выведет первый символ строки «hiya!», букву h? А вот и нет. Скорее всего будет выведен какой-то другой символ (с вероятностью 255/256). Потому что последовательность байт «hiya!» в объекте типа string начинается не с адреса &a1.

Поэтому экспериментировать лучше с элементарными типами.

(4) Указатель — это адрес памяти, по которому располагается значение определённого типа. Тип важен, поскольку в адресной арифметике в С/С++ изменение указателя на единицу изменит адрес, хранящийся в указателе, на размер размер типа данных. Например, увеличение на 1 указателя char *char_ptr приведёт к увеличению адреса на 1 (sizeof(char)), а увеличение на 1 указателя int *int_ptr приведёт к увеличению адреса на 4 (sizeof(int)).

На указателях в С/С++ базируется работа с массивами. Например, оператор int array[5] выделяет участок памяти, достаточный для размещения пяти значений типа int и присваивает переменной array адрес этого участка памяти. Т.е. array — это константный указатель на int. Обращение array[0] эквивалентно *array, а array[1]*(array + 1) и т.д.

На указателях в С/С++ базируется работа с динамической памятью. Оператор new выделяет из кучи (heap) область памяти и возвращает указатель на неё. Оператор delete получает указатель на выделенную из кучи область памяти и освобождает её (возвращает в кучу). В статье имелся ввиду именно этот случай использования указателей.

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

Спасибо. что-то понял, что-то пока нет. А код свой я проверял. у меня выводилась строка «hiya!». А мне казалось, что должно выводить адрес памяти, куда записано «hiya!». Оказывается надо писать для вывода адреса cout << sp1 << endl; а у меня cout << *sp1 << endl;

Очень сложно все для меня. Сложно все осмыслить. Например. Читаю строку -понятно, читаешь вторую строку — тоже понятно. А посмотришь на две строки вместе — какой-то бред)).

Указателям нужно уделять особое внимание это очень важная и тонкая тема. Если не поймешь сейчас конструкции типа

    int* ptr, i;    // здесь ptr - это указатель, а i - переменная типа int
    i = 10;     // переменной присваиваем значение
    ptr = &i;   // а вот указателю можно присвоить только адрес переменной
    cout << *ptr / 2 << endl;   // а здесь разыменовываем указатель *ptr и таким образом
                        // с указателем можно работать как с обычной переменной i

то дальше ты «ногу сломишь», в конструкциях типа

    char* str[5][10];
    *(*(str + 1) + 7) = "a";
    cout << str[1][7] << endl;

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

char* str = new [5][10];

здесь придется сделать нечто подобное

        // создание динамического двумерного массива
            int** matrix = new int*[5]; // matrix - это указатель на указатель на тип int
            // в количестве пяти указателей - это строки
            for (int i = 0; i < 5; ++i)
                *(matrix + i) = new int[10];    // а теперь в каждой строке выделяем память под десять значений типа int - это столбцы
            // либо в нотации массивов matrix[i] = new int [10];

        // инициализация матрицы случайными числами
            srand(static_cast<int>(time(NULL)));

            for (int i = 0; i < 5; ++i){
                for (int j = 0; j < 10; ++j)
                    *(*(matrix + i) + j) = rand() % 41 - 20;
                // либо в нотации массивов matrix[i][j] = rand() % 41 - 20;
            }

            cout << *(*(matrix + 2) + 4) << endl;
            // либо в нотации массивов cout << matrix[2][4] << endl;

и это далеко не самые сложные вещи. Ведь библиотека STL практически вся построена на работе с указателями. Так-что не слаживай руки и вперед на покорение, только кажущихся непокоримыми, указателей :)))

Юрий, здесь у тебя звёздочка лишняя:

    char str[5][10];           // <- должно быть так
    *(*(str + 1) + 7) = "a";
    cout << str[1][7] << endl;

fitter, обычно, конечно, так *(*(str + 1) + 7) = "a" не пишут, а используют нотацию массивов. А вот при реализации динамических структур данных типа стеков, одно- и двусвязных списков, деревьев без указателей не обойтись.

Просто надо запомнить, что указатель — это адрес памяти, по которому лежит значение. Доступ к значению — операция разыменования указателя *ptr. Если есть переменная, которой присвоено значение, то получить на это значение указатель — операция взятия адреса ptr = &var; в этом случае var и *ptr будут адресоваться к одной и той же области памяти.

Спасибо ребята! Чтоб я без вас делал! Сейчас буду практикой осмысливать выше написанное.
Только разве не надо звездочку ставить около ptr ? «операция взятия адреса ptr = &var»

Только разве не надо звездочку ставить около ptr ? «операция взятия адреса ptr = &var»

Не надо. Здесь ptr предназначено для хранения адреса, и результат операции &var — как раз является адресом.

А вот в конструкции

int var = 10;
int *ptr = &var;

звёздочка нужна, поскольку это описание переменной с инициализацией: выражение int *ptr описывает переменную ptr как указатель на int, а часть = &var присваивает адрес переменной var переменной ptr.

Указатель — тип данных для хранения адреса памяти. Для определения указателя на переменную типа int используют подстановку * после названия типа.

Т.е., при инициализации указателя, символ * определяет именно тип данных, как указатель.

int* pointer = new int();

Переменная pointer может хранить только значение адреса памяти (вернее, его диапазона, размером sizeof(int) байт).

Чтобы получить значение, которое находится по адресу, на который указывает указатель, используют оператор разыменовывания указателя — *. И если в первом случае, при инициализации переменной-указателя, этот символ определял тип данных, то при разыменовывании указателя происходит совсем другая операция.

Оператор & используется для взятия адреса переменной. Если применить его к указателю, то он вернет адрес самого указателя, который, в свою очередь хранит другой адрес.

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

Пожалуйста, оформляйте исходный код в соответствии с правилами разметки. Для того, чтобы вставить код в комментарий, скопируйте его в текстовое поле ниже, после чего выделите то, что скопировали и нажмите кнопку «код» в панели инструментов. Иначе ваш код может принять нечитаемый вид.

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.