Форум программистов, компьютерный форум CyberForum.ru

Указатели - C++

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 27, средняя оценка - 4.63
Hagrael
БТР - мой друг
 Аватар для Hagrael
331 / 273 / 2
Регистрация: 07.01.2010
Сообщений: 1,932
23.06.2011, 20:08     Указатели #1
1) Указатель можно инициализирвоать только с помощью операции *p=&a? А как записать адрес переменной в простую переменную (я пытался это делать через операцию b=&a, но компилятор ругается, говорит, что операция &a возвращает указатель.
2) Почему имеет значение тип указателя? Ведь это просто ссылка на переменную.

И еще один вопрос, не касающийся указателей:
3) Как программа узнает, какие места ОЗУ ей можно занимать (не заняты др. программой), а какие - нет.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
sandye51
программист С++
 Аватар для sandye51
677 / 579 / 39
Регистрация: 19.12.2010
Сообщений: 2,016
23.06.2011, 20:12     Указатели #2
Цитата Сообщение от Hagrael Посмотреть сообщение
1) Указатель можно инициализирвоать только с помощью операции
нет. Варианты
C++
1
2
3
4
int a = 4;
int* p1 = &a;
int* p2 = p1;
int* p3 = new int[size];
Цитата Сообщение от Hagrael Посмотреть сообщение
Ведь это просто ссылка на переменную.
нет. Это ее адрес.

Цитата Сообщение от Hagrael Посмотреть сообщение
Как программа узнает, какие места ОЗУ ей можно занимать (не заняты др. программой), а какие - нет.
читай про структуру ВАП и управление памятью в ОС
OstapBender
 Аватар для OstapBender
581 / 519 / 35
Регистрация: 22.03.2011
Сообщений: 1,585
23.06.2011, 20:19     Указатели #3
важно также, что не *p=&a,
а p=&a;

если не при объявлении конечно
Kastaneda
Модератор
Эксперт С++
 Аватар для Kastaneda
4236 / 2769 / 218
Регистрация: 12.12.2009
Сообщений: 7,104
Записей в блоге: 1
Завершенные тесты: 1
23.06.2011, 20:20     Указатели #4
Цитата Сообщение от sandye51 Посмотреть сообщение
нет. Варианты...
и даже так:
int *p=(int*)123456;


Цитата Сообщение от Hagrael Посмотреть сообщение
Почему имеет значение тип указателя?
А как компилятор узнает, сколько байт по этому адресу ему нужно использовать (прочитать/записать)? Плюс арифметика указателей:
C++
1
2
int *ptr;
++ptr;//указатель увеличился на sizeof(int), а не на 1
silent_1991
Эксперт C++
4938 / 3014 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
23.06.2011, 20:21     Указатели #5
OstapBender, а при объявлении звёздочка относится к типу, а не к переменной, так что всё равно p = &a;
OstapBender
 Аватар для OstapBender
581 / 519 / 35
Регистрация: 22.03.2011
Сообщений: 1,585
23.06.2011, 20:27     Указатели #6
silent_1991, да я понял, что про объявление не совсем точно написал. ну все равно чисто визуально может показаться что пишется *p = ... =)
Nameless One
Эксперт С++
 Аватар для Nameless One
5753 / 3402 / 255
Регистрация: 08.02.2010
Сообщений: 7,393
24.06.2011, 06:50     Указатели #7
Цитата Сообщение от Hagrael Посмотреть сообщение
А как записать адрес переменной в простую переменную (я пытался это делать через операцию b=&a, но компилятор ругается, говорит, что операция &a возвращает указатель.
Можно сделать так:
C
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    int x, address_of_x, *pointer_to_x;
    x = 3;
    address_of_x = (int) &x;
    pointer_to_x = (int*) address_of_x;
    printf("memory address of x = %p, x = %d\n", pointer_to_x, *pointer_to_x);
    exit(0);
}
Или даже так:
C
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    int x, address_of_x;
    x = 3;
    address_of_x = (int) &x;
    printf("memory address of x = %p, x = %d\n", (int*) address_of_x, *(int*) address_of_x);
    exit(0);
}
Но наверно понятно, что так делать не надо. Зачем хранить адрес переменной в простой переменной, если для этого существуют указатели?
Hagrael
БТР - мой друг
 Аватар для Hagrael
331 / 273 / 2
Регистрация: 07.01.2010
Сообщений: 1,932
24.06.2011, 11:20  [ТС]     Указатели #8
Цитата Сообщение от sandye51 Посмотреть сообщение
нет. Варианты ...
Спасибо за различные способы, но последний я не до конца понял. Там, что, int используется как класс? И что означает size? Буду признателен, если вы объясните.

Цитата Сообщение от sandye51 Посмотреть сообщение
Ведь это просто ссылка на переменную.
нет. Это ее адрес.
Я, кажется, понял! Операция &a возвращает указатель, однако указатель можно преобразовать в простой тип int! Почему именно указатель, а не простой тип (который можно было бы преобразовать в указатель)? Потому, что гораздо чаще с этой операции нужно получить указатель, чем сам адрес, и потому для быстроты разработчики решили, что операция &a должна возвращать указатель. Все верно?

Цитата Сообщение от sandye51 Посмотреть сообщение
читай про структуру ВАП и управление памятью в ОС
ОК.

Цитата Сообщение от Kastaneda Посмотреть сообщение
int *p=(int*)123456;
А вот это интересно! Тогда, выходит, ты сам задаешь адрес для указателя? И если так, то такой код:
C++
1
2
int* p=(int*) 123;
cout << *p;
выдает ошибку "Память не может быть reed", поскольку это запретный для моей программы узел?

Цитата Сообщение от Nameless One Посмотреть сообщение
address_of_x = (int) &x;
И снова еще один способ преобразовать тип переменной! Да сколько их там вообще?))
C++
1
2
3
b=static_cast<int>(a);
b=int(a);
b=(int) a;
Они все чем-то отличаются или нет?

Цитата Сообщение от Kastaneda Посмотреть сообщение
А как компилятор узнает, сколько байт по этому адресу ему нужно использовать (прочитать/записать)?
Хм. Теперь у меня возник такой вопрос: Как вообще программа узнает, сколько байт имеет та или иная переменная? Вот к примеру код:
C++
1
2
int a;
a=5.5;
Вот откуда программа узнает, что надо отбросить дробную часть? В самой переменной a ведь не хранится ее размер. По-моему, тип уже лежит в коде, и программе не надо узнавать его. То есть когда ты говоришь int a;, компилятор запоминает, что переменная a в данном скопе имеет тип int и в последствии в откомпилированном коде что-то меняется. То есть в коде не сказано
Код
Присвоить переменной a значение 5.5
а написано
Код
Присвоить переменной а значение 5.5 без дробной части
Я правильно понимаю?
pito211
 Аватар для pito211
186 / 173 / 8
Регистрация: 22.03.2010
Сообщений: 612
24.06.2011, 11:46     Указатели #9
Вот откуда программа узнает, что надо отбросить дробную часть?
в данном случае на этапе компиляции:
Assembler
1
2
3
int a;
a = 5.5;
010114B7  mov         dword ptr [a],5
Hagrael
БТР - мой друг
 Аватар для Hagrael
331 / 273 / 2
Регистрация: 07.01.2010
Сообщений: 1,932
24.06.2011, 11:48  [ТС]     Указатели #10
Цитата Сообщение от pito211 Посмотреть сообщение
в данном случае на этапе компиляции
А разве может быть по-другому?
pito211
 Аватар для pito211
186 / 173 / 8
Регистрация: 22.03.2010
Сообщений: 612
24.06.2011, 11:55     Указатели #11
конечно.
Assembler
1
2
3
4
5
6
7
8
9
10
int a;
a = 5.5;
00D714F7  mov         dword ptr [a],5 
double f = 5.5;
00D714FE  fld         qword ptr [__real@4016000000000000 (0D75920h)] 
00D71504  fstp        qword ptr [f] 
a = f;
00D71507  fld         qword ptr [f] 
00D7150A  call        @ILT+220(__ftol2_sse) (0D710E1h) 
00D7150F  mov         dword ptr [a],eax
Добавлено через 2 минуты
Цитата Сообщение от Hagrael Посмотреть сообщение
Операция & возвращает указатель
адресс
Nameless One
Эксперт С++
 Аватар для Nameless One
5753 / 3402 / 255
Регистрация: 08.02.2010
Сообщений: 7,393
24.06.2011, 12:03     Указатели #12
Цитата Сообщение от Hagrael Посмотреть сообщение
Спасибо за различные способы, но последний я не до конца понял. Там, что, int используется как класс? И что означает size? Буду признателен, если вы объясните.
это означает выделение динамическое выделение памяти под массив размером size
Я, кажется, понял! Операция &a возвращает указатель, однако указатель можно преобразовать в простой тип int! Почему именно указатель, а не простой тип (который можно было бы преобразовать в указатель)? Потому, что гораздо чаще с этой операции нужно получить указатель, чем сам адрес, и потому для быстроты разработчики решили, что операция &a должна возвращать указатель. Все верно?
Не совсем. Указатель на переменную не просто хранит беззнаковое целое число (если бы это было не так, то везде можно было бы использовать вместо указателей переменные типа size_t), но он еще и знает о размере типа, на переменную которого он указывает. Это необходимо при разыменовании и арифметике указателей (т.е. инкременте или декременте). Например:
C
1
2
3
4
5
6
7
double d, *pd;
int i, *pi;
char p, *pc;
pi = &i, pd = &d;
++pi; // адрес i + sizeof(int)
++pd; // адрес d + sizeof(double)
++pc; // адрес с + sizeof(char)
Как видно, при инкременте указателей разных типов переход по памяти происходит именно на размер данного типа. В этом легко убедиться, если распечатать значения указателей
А вот это интересно! Тогда, выходит, ты сам задаешь адрес для указателя? И если так, то такой код:
C++
1
2
int* p=(int*) 123;
cout << *p;
выдает ошибку "Память не может быть reed", поскольку это запретный для моей программы узел?
Так делать нельзя. Память в программе должна быть либо выделена статически, либо динамически. Ты же взял произвольный адрес (невыделенной памяти), и попытался его разыменовать (т.е. вывести значение переменной типа int, находящееся по адресу 123), и, естественно, это приводит к ошибке.
И снова еще один способ преобразовать тип переменной! Да сколько их там вообще?))
C++
1
2
3
b=static_cast<int>(a);
b=int(a);
b=(int) a;
Они все чем-то отличаются или нет?
первая строка - явное приведение типов в стиле С++, вторая - инициализация переменной b, третья - явное приведение типов в стиле С
Хм. Теперь у меня возник такой вопрос: Как вообще программа узнает, сколько байт имеет та или иная переменная? Вот к примеру код:
C++
1
2
int a;
a=5.5;
Вот откуда программа узнает, что надо отбросить дробную часть?
Тут происходит неявное приведение типов
В самой переменной a ведь не хранится ее размер.
Тип и размер ВСЕХ переменных известен на уже этапе компиляции
По-моему, тип уже лежит в коде, и программе не надо узнавать его. То есть когда ты говоришь int a;, компилятор запоминает, что переменная a в данном скопе имеет тип int и в последствии в откомпилированном коде что-то меняется.
Поздравляю, ты открыл статическую типизацию
То есть в коде не сказано
Код
Присвоить переменной a значение 5.5
а написано
Код
Присвоить переменной а значение 5.5 без дробной части
Я правильно понимаю?
Нет, в коде сказано: у нас есть значение типа double, а переменная типа int. Несоответствие. Надо выполнить приведение типов (а при этом как раз происходит отбрасывание дробной части)
Hagrael
БТР - мой друг
 Аватар для Hagrael
331 / 273 / 2
Регистрация: 07.01.2010
Сообщений: 1,932
24.06.2011, 12:04  [ТС]     Указатели #13
Цитата Сообщение от pito211 Посмотреть сообщение
Операция & возвращает указатель

адресс
Но она же возвращает переменную с типом type*. А такие переменные называются указателями. Чем является адрес?
Nameless One
Эксперт С++
 Аватар для Nameless One
5753 / 3402 / 255
Регистрация: 08.02.2010
Сообщений: 7,393
24.06.2011, 12:07     Указатели #14
Цитата Сообщение от Hagrael Посмотреть сообщение
Чем является адрес
Адрес он и в африке адрес. Натуральное число, являющееся адресом в памяти, по которому расположена переменная типа type.
pito211
 Аватар для pito211
186 / 173 / 8
Регистрация: 22.03.2010
Сообщений: 612
24.06.2011, 12:14     Указатели #15
Цитата Сообщение от Hagrael Посмотреть сообщение
Но она же возвращает переменную с типом type*
возвращает она адрес, а указатель это переменная содержащая адрес
Hagrael
БТР - мой друг
 Аватар для Hagrael
331 / 273 / 2
Регистрация: 07.01.2010
Сообщений: 1,932
24.06.2011, 13:02  [ТС]     Указатели #16
Большое спасибо, Nameless One, за такое хорошее изложение!

Цитата Сообщение от Nameless One Посмотреть сообщение
Не совсем. Указатель на переменную не просто хранит беззнаковое целое число (если бы это было не так, то везде можно было бы использовать вместо указателей переменные типа size_t), но он еще и знает о размере типа, на переменную которого он указывает. Это необходимо при разыменовании и арифметике указателей (т.е. инкременте или декременте).
То есть тип также хранится в ОЗУ? О_о Если так, то у указателей какой-то специфический тип. Будь он int, double или каким-либо другим, все равно под него выделяется 2 блока памяти: число - размер типа; и число - адрес. Таким образом, выходит, что указатель любого типа имеет одинаковый размер. Это так?

Цитата Сообщение от Nameless One Посмотреть сообщение
Так делать нельзя. Память в программе должна быть либо выделена статически, либо динамически. Ты же взял произвольный адрес (невыделенной памяти), и попытался его разыменовать (т.е. вывести значение переменной типа int, находящееся по адресу 123), и, естественно, это приводит к ошибке.
Так это приводит к ошибке потому, что там не выделена память или потому что та память не входит в допустимую для моей программы? Разве полное отсутствие "включенных" битов не свидетельствует просто о нуле? Если свидетельствует, то ошибки из-за того, что память не выделена, быть не должно.

Цитата Сообщение от Nameless One Посмотреть сообщение
Тип и размер ВСЕХ переменных известен на уже этапе компиляции
Но зачем тогда писать int* p=&a;, достаточно просто написать *p=&a;? Размер переменной a ведь уже известен.
pito211
 Аватар для pito211
186 / 173 / 8
Регистрация: 22.03.2010
Сообщений: 612
24.06.2011, 15:11     Указатели #17
Цитата Сообщение от Hagrael Посмотреть сообщение
все равно под него выделяется 2 блока памяти: число - размер типа; и число - адрес
для инта и дабла всё прорсто - выделяется память для инта и для дабла 4 и 8 байт соответсвенно(к примеру). Но если ты с помощью new выделяешь память для неинтегральных типов, то там действительно кладётся некоторый заголовок, чтобы знать потом как удалять delet[]-ом + этой инфой пользуется dynamic_cast<>() и typeid(), только тип ещё желательно делать полиморфным, то есть какую-нибудь функцию сделать виртуальной или деструктор. Если ты попытаешься применить typeid для неполиморфного типа то в теории на этапе компиляции компилятор должен всё подставить, последний факт лично не проверял, но где то читал что должно быть так

Добавлено через 2 минуты
динамик_каст только что проверил на этапе компиляции обругался из-за того что тип не полиморфный
silent_1991
Эксперт C++
4938 / 3014 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
24.06.2011, 16:15     Указатели #18
Цитата Сообщение от Hagrael Посмотреть сообщение
То есть тип также хранится в ОЗУ? Если так, то у указателей какой-то специфический тип. Будь он int, double или каким-либо другим, все равно под него выделяется 2 блока памяти: число - размер типа; и число - адрес.
Тип хранится в ОЗУ только во время компиляции. В том и суть статической типизации - во время выполнения программы не нужно знать никаких дополнительных данных о типах, они все были известны во время компиляции, поэтому во время компиляции же компилятор расставил все необходимые смещения статически, они теперь вшиты в код бинарника и на этапе выполнения не могут быть изменены.

Цитата Сообщение от Hagrael Посмотреть сообщение
Таким образом, выходит, что указатель любого типа имеет одинаковый размер
Ага, так и есть)) А зачем нам использовать разные размеры? Указатель должен вместить ровно такое число, сколько максимально блоков памяти мы можем адресовать, больше и не надо. Не важно, что хранит указатель, ведь и int, и double, и все остальные типы будут располагаться в одной и той же, адресуемой нами, памяти.

Цитата Сообщение от Hagrael Посмотреть сообщение
Так это приводит к ошибке потому, что там не выделена память или потому что та память не входит в допустимую для моей программы?
А разве это не одно и то же? Память в том месте в любом случае есть. Выделение памяти - это же не какой-то цех по заделыванию дыр в памяти свежатинкой. Выделение просто говорит системе: "отдай-ка мне, пожалуйста, память вот такого размера". Система ищет место, где был бы свободный непрерывный блок определённого размера, и возвращает адрес этого блока программе. Это если упрощённо.

Цитата Сообщение от Hagrael Посмотреть сообщение
Но зачем тогда писать int* p=&a;, достаточно просто написать *p=&a;? Размер переменной a ведь уже известен.
Да, размер a известен. Но когда мы будем работать с указателем, а не с самой переменной, как компилятор узнает, что указатель указывает именно на переменную типа int?
Nameless One
Эксперт С++
 Аватар для Nameless One
5753 / 3402 / 255
Регистрация: 08.02.2010
Сообщений: 7,393
24.06.2011, 16:27     Указатели #19
Цитата Сообщение от Hagrael Посмотреть сообщение
Но зачем тогда писать int* p=&a;, достаточно просто написать *p=&a;? Размер переменной a ведь уже известен.
Потому что С статически типизирован, а механизм вывода типов (как в Haskell'е, например), в нем отсутствует. Поэтому аннотации типов в нем обязательны.
Цитата Сообщение от Hagrael Посмотреть сообщение
Будь он int, double или каким-либо другим, все равно под него выделяется 2 блока памяти: число - размер типа; и число - адрес. Таким образом, выходит, что указатель любого типа имеет одинаковый размер. Это так?
под размер ничего не выделяется, т.к. информация о размере переменной нужна только во время компиляции (т.е. в процессе генерации машинного кода), но не в процессе выполнения.
Цитата Сообщение от Hagrael Посмотреть сообщение
Разве полное отсутствие "включенных" битов не свидетельствует просто о нуле?
Каких битов?
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
24.06.2011, 20:32     Указатели
Еще ссылки по теме:

C++ Написать программу сортировки через указатели на указатели
Указатели и массивы. Указатели и функции C++
Указатели на массивы. Указатели и функции C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Hagrael
БТР - мой друг
 Аватар для Hagrael
331 / 273 / 2
Регистрация: 07.01.2010
Сообщений: 1,932
24.06.2011, 20:32  [ТС]     Указатели #20
Большое всем спасибо за ответы!

Цитата Сообщение от silent_1991 Посмотреть сообщение
Так это приводит к ошибке потому, что там не выделена память или потому что та память не входит в допустимую для моей программы?

А разве это не одно и то же?
Вы имеете в виду, что выделенная память - это память, которая выделена для приложения или память, выделенная под переменные? Или это одно и то же, и термин "блок памяти, выделенный для приложения" всего лишь условный, и на самом деле приложению дается ровно столько байт, сколько нужно для его переменных (что вполне логично)?

Цитата Сообщение от silent_1991 Посмотреть сообщение
Да, размер a известен. Но когда мы будем работать с указателем, а не с самой переменной, как компилятор узнает, что указатель указывает именно на переменную типа int?
Я имел в виду вот что:
C++
1
2
3
4
5
6
7
int a=5; // компилятор знает, что переменная "a" является целочисленной
*p=&a // компилятор смотрит, какой тип имеет переменная "а" и сам
         // устанавливает тип указателя. Правда, в дальнейшем, этот
         // указатель сможет работать только с адресами на целочисленные
        // переменные
double b=10.6;
p=&b // ошибка
Это, конечно, только мое предположение, как бы могла работать программа. Если компилятору известен тип переменной a, то он вполне сможет передать этот тип указателю p. Т. е. как бы компилятор сам допишет int перед записью *p=&a. Конечно, это глупо, гораздо лучше даже для программиста явно указать тип переменных, на которые может указывать указатель, но думаю, что логичнее в таком случае было бы использовать отдельный тип pointer без излишек типа int.
Цитата Сообщение от Nameless One Посмотреть сообщение
Потому что С статически типизирован, а механизм вывода типов (как в Haskell'е, например), в нем отсутствует. Поэтому аннотации типов в нем обязательны.
Хм. Да, все манипуляции с типами проводятся еще компилятором, но разве компилятор не может сам добавить int (как написано выше)?

pito211, простите, но из вашего сообщения я мало что понял, т. к. со многими вещами, которые вы там упомянули, пока не знаком.
Yandex
Объявления
24.06.2011, 20:32     Указатели
Ответ Создать тему
Опции темы

Текущее время: 02:16. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru