Форум программистов, компьютерный форум 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) Как программа узнает, какие места ОЗУ ей можно занимать (не заняты др. программой), а какие - нет.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
silent_1991
Эксперт C++
4938 / 3014 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
24.06.2011, 20:45     Указатели #21
Hagrael, ваши предположения не верны, ничего никуда автоматически не подставляется. И, ещё раз, звёздочка при объявлении относится к типу, а не к переменной, так что ваше *p = &a; в принципе не верно, даже если забыть про то, что тип надо указывать. Выражение int *p = &a; нужно читать как (int *)p = &a; (скобки для понятности). Т.е. вот это int * и есть ваш тип pointer. А если писать p = &a;, как компилятор поймёт, что мы хотим работать с p как с указателем, а не как просто с целым числом? Конечно, он мог бы предположить что-то, но его предположение могло бы оказаться неверным. Указывая же тип явно, мы сообщаем, что переменная p является указателем на тип int (int *), и компилятор всё обработает верно.

Цитата Сообщение от Hagrael Посмотреть сообщение
Вы имеете в виду, что выделенная память - это память, которая выделена для приложения или память, выделенная под переменные?
Тут ещё смотря про какое выделение речь. Под автоматические и статические переменные (переменные, выделенные на стеке) выделяется столько памяти, сколько нужно. Под динамические вы сами решаете, сколько памяти выделять. А вот такое (int *p = 12345; ) обращение к случайному блоку памяти могло попасть на какие-то критические для программы участи, и если их, например, затереть - программа покрашится. Короче говоря, свободно манипулировать можно только той памятью, для которой заведомо известно, что она предоставлена именно для таких манипуляций.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Hagrael
БТР - мой друг
 Аватар для Hagrael
331 / 273 / 2
Регистрация: 07.01.2010
Сообщений: 1,932
24.06.2011, 20:56  [ТС]     Указатели #22
Все, с типом указателей разобрался, спасибо.

Цитата Сообщение от silent_1991 Посмотреть сообщение
Тут ещё смотря про какое выделение речь. Под автоматические и статические переменные (переменные, выделенные на стеке) выделяется столько памяти, сколько нужно. Под динамические вы сами решаете, сколько памяти выделять. А вот такое (int *p = 12345; ) обращение к случайному блоку памяти могло попасть на какие-то критические для программы участи, и если их, например, затереть - программа покрашится. Короче говоря, свободно манипулировать можно только той памятью, для которой заведомо известно, что она предоставлена именно для таких манипуляций.
А выделенная под приложение память - это память, необходимая приложению, или же есть некий резерв?
silent_1991
Эксперт C++
4938 / 3014 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
24.06.2011, 20:58     Указатели #23
Hagrael, я не понимаю, что вы под этим подразумеваете? Сегмент кода? Сегмент данных? Всё вместе?
Hagrael
БТР - мой друг
 Аватар для Hagrael
331 / 273 / 2
Регистрация: 07.01.2010
Сообщений: 1,932
24.06.2011, 21:04  [ТС]     Указатели #24
silent_1991, я подразумеваю память под, если так можно сказать, процесс. К примеру вот такая у меня программа:
C++
1
2
3
4
5
6
7
8
#include <iostream>
using namespace std;
 
int main() {
    int a=4, b=10;
    // нужный этап
    return 0;
}
Можно спрогнозировать, сколько памяти будет выделено для программы на помеченном этапе выполнения программы? Если подумать логично, то да, можно, и место будет равняться 8 байтам. Но может быть, ОС выделяет некоторый резерв для программы?
silent_1991
Эксперт C++
4938 / 3014 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
24.06.2011, 21:09     Указатели #25
Hagrael, если уж речь зашла о процессе, то будет выделено ровно столько, сколько может вместить машинный код, в который был скомпилирован исходник, поскольку при выполнении программный код должен быть полностью загружен в оперативную память. На стеке же под две автоматические переменные типа int будет выделено 8 байт.

Добавлено через 1 минуту
А зачем нужен резерв, вы можете пояснить? Уже на этапе компиляции известно, под сколько переменных и каких типов необходимо выделить память. Динамических объявлений в C++ нету. Поэтому всё выделение памяти заключается в передвигании вершины стека на нужное количество байт.
Chelioss
179 / 179 / 4
Регистрация: 08.01.2011
Сообщений: 1,131
24.06.2011, 21:10     Указатели #26
Цитата Сообщение от silent_1991 Посмотреть сообщение
Динамических объявлений в C++ нету.
new? или вы имели ввиду, что нет в его коде?
Hagrael
БТР - мой друг
 Аватар для Hagrael
331 / 273 / 2
Регистрация: 07.01.2010
Сообщений: 1,932
24.06.2011, 21:12  [ТС]     Указатели #27
Спасибо за объяснение, но могу ли я полагать, что память будет составлять в итоге размер_файла + размер_переменных байт?
silent_1991
Эксперт C++
4938 / 3014 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
24.06.2011, 21:18     Указатели #28
Chelioss, я имел ввиду, что переменные нельзя объявлять на этапе выполнения.

Hagrael, если вы в дальнейшем не будете динамически выделять память, то да, можете.
ValeryLaptev
Эксперт C++
1004 / 783 / 46
Регистрация: 30.04.2011
Сообщений: 1,595
25.06.2011, 04:36     Указатели #29
Цитата Сообщение от Hagrael Посмотреть сообщение
silent_1991, я подразумеваю память под, если так можно сказать, процесс. К примеру вот такая у меня программа:
C++
1
2
3
4
5
6
7
8
#include <iostream>
using namespace std;
 
int main() {
    int a=4, b=10;
    // нужный этап
    return 0;
}
Можно спрогнозировать, сколько памяти будет выделено для программы на помеченном этапе выполнения программы? Если подумать логично, то да, можно, и место будет равняться 8 байтам. Но может быть, ОС выделяет некоторый резерв для программы?
Все зависит от операционной системы. Если речь идет о Windows, то КАЖДОМУ процессу выделяется 4 гига виртуальной памяти. Вернее, 2 гига виртуалки забирает себе винда, а 2 - оставляет прикладному процессу.
А уж отображением виртуалки в реальную память занимается подсистема управления страничной памятью операционной системы. Меньше одной страницы не бывает, а это - 4 килобайта.
Так что не заморачивайтесь экономией памяти. Будете экономить, когда столкнетесь с физической памятью непосредственно...
Bers
Заблокирован
25.06.2011, 05:10     Указатели #30
Ну, наверное, смысл не в том, что бы экономить память, а в том что бы эффективно её использовать.

Дабы избежать всевозможных потерь из-за перезаписи кусков данных с места на место, проблем с переносимостью из-за выравнивания данных, всевозможных фрагментаций кучи, кеш-промахов и тп.
taras atavin
Ушёл с форума.
 Аватар для taras atavin
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.06.2011, 07:11     Указатели #31
Цитата Сообщение от Hagrael Посмотреть сообщение
я пытался это делать через операцию b=&a, но компилятор ругается, говорит, что операция &a возвращает указатель.
Указатель есть целое специального вида и системно зависимой разрядности. Например, в windows xp указатель 32-х битный. Приведи (можно неявно) указатель к обычному целому и будет тебе счастье.

Добавлено через 32 минуты
Цитата Сообщение от Hagrael Посмотреть сообщение
Почему имеет значение тип указателя? Ведь это просто ссылка на переменную.
Во-первых не бывает "просто ссылки". Ссылка - это вполне определённая сущность со своими синтаксисом и семантикой. А во-вторых указатель - не ссылка. Указатель хранит адрес того данного, или подпрограммы, куда он указывает, причём, этот адрес хранится в виде наименьшего из номеров байт, принадлежащих данному, или подпрограмме, на которую указывает указатель. Ссылка же это совсем другое. Написал ты
C++
1
a=b;
. Откуда надо взять данное и куда поместить? Процессор ведь не знает переменных. Поэтому на саом деле здесь копирование ячеек памяти, доступ к которым осуществляется по их по адресам. При трансляции компил отвёл переменной b один адрес, переменной а - другой. На этапе трансляции это тоже укзатели. Но вот прога полнотью скомпилена и записана на диск, и больше там нет никаких указателей. Отвёл компил память под переменные, когда увидел в тексте
C++
1
float a, b, *c;// Тип может быть любым другим
, создав указаетели во внутренней таблице, потом увидел
C++
1
b=a;
и пишет в код значения указателей, но не обращение к самим указателям. Далее он видеит
C++
1
*c=a;
и поступает с b точно также, а адрес c будет загружен из данного на этапе исполнения программы: указатель есть данное, хранящее адрес, адрес c будет загружен на этапе исполенния программы из указателя, а его адрес на будет на этапе трансляции записан в код. То есть дресация с косвенная (взять адрес из данного по адресу, потом осуществить по нему доступ к данному), а адресация a - прямая (осуществить доступ к данному по готовому адресу). На этапе же трансляции указателю соответсвует уже указатель на указатель, а не на статическую переменную. Так вот ссылка есть копия указателя времени трансляции, либо разыменованный указатель времени исполнения. То есть нет ни какой гарантии стандарта, что укзатель будет существовать и использоваться для косвенной адресации на этапе исполнения программы, хотя такой вариант и возможен. Если же написать
C++
1
2
3
4
float a;
float &b=a;
float *c;
c=&b;
, то в указатль времени исполнения c будет скопирован адрес a, в отличие
C++
1
2
3
4
float a;
float *b=&a;
float **c;
c=&b;
, где в указатель времени исполнения c будет скопирован адрес b, то есть адрес указателя. и sizeof для указателя вернёт разрядность в байтах указателя, а для ссылки sizeof вернёт разрядность в байтах данного, на которое она ссылается.

Добавлено через 19 минут
Цитата Сообщение от Hagrael Посмотреть сообщение
3) Как программа узнает, какие места ОЗУ ей можно занимать (не заняты др. программой), а какие - нет.
А ни какие места и не заняты другой программой, кроме тех, которые заняты операционной системой, её же адреса принадлежат стандартному для данной системы диапазону. Фокус в виртуальных адресах. Любое место ОЗУ имеет два адреса: физичесткий и виртуальный. Физический - это тот, с которым работает аппаратное обеспечение компьютера. Виртуальный же адрес известен только программам, причём, максисмум двум и одна из них всегда операционная система. Но по виртуальному адресу можно вычислить физический. Вот представь себе Землю. Как на Земле можно адересовать участок суши? Конечно же географическими координатами. А как ещё? Стоит на том участке суши дом, на доме табличка с названием улицы и номером дома. Я в письме написал "ул. Кирова, д. № 74". Дом наёдёшь? Даже если город знаешь, но раньше там ни разу не был, спрашивать нельзя, а таблички заляпаны грязью, то дом ты не найдёшь. Потому что не знаешь, какому ральному полоэжению в пространстве соотвествует этот адрес. Таблички протрёшь? Эйси. А если я ещё скопировал карту города, произвольно перименовал на ней улицы и пернумеровал дома и адрес указаел по этой карте? Виртуальный адрес - это адрес по такой отсебяьей карте. Прчиём, их может быть много в одном и том же городе. В одном случае это на самом деле ул. Орджоникидце, д. № 73, в другом ул. 25 лет октября, д. № 12, в третьем действительно ул. Кирова, д. № 74. То есть один и тот же виртуальный адрес может соотвествовать разным местам. В ОЗУ тоже один вирутальынй адрес может соответствовать разным местам, а для каждой программы существует своё соответствие виртуальынх адресов физическим. С физическими же адресами программы не работают и их даже не знают. А динамическую память ещё и выдаёт операционная система, а она то уж точно знает, какие адреса чем заняты.
ValeryLaptev
Эксперт C++
1004 / 783 / 46
Регистрация: 30.04.2011
Сообщений: 1,595
25.06.2011, 08:33     Указатели #32
Цитата Сообщение от Bers Посмотреть сообщение
Ну, наверное, смысл не в том, что бы экономить память, а в том что бы эффективно её использовать.

Дабы избежать всевозможных потерь из-за перезаписи кусков данных с места на место, проблем с переносимостью из-за выравнивания данных, всевозможных фрагментаций кучи, кеш-промахов и тп.
Перечисленные вами проблемы начинают сказываться только при ОЧЕНЬ больших объемах используемой виртуалки и при очень маленькой реальной памяти. Если реальной памяти 2-4 гига, то практически на все вами перечисленное можно не обращать внимания.
Bers
Заблокирован
25.06.2011, 09:07     Указатели #33
Цитата Сообщение от ValeryLaptev Посмотреть сообщение
Перечисленные вами проблемы начинают сказываться только при ОЧЕНЬ больших объемах используемой виртуалки и при очень маленькой реальной памяти. Если реальной памяти 2-4 гига, то практически на все вами перечисленное можно не обращать внимания.
Хм. А причём тут размер памяти? Речь же о потерях драгоценных тиков процессора из-за всякой напрочь не нужной рутины, которой благополучно можно было бы избежать, если более грамотно подойти к вопросу о том, как именно будут лежать данные в памяти.

Это такой прозрачный намёк в сторону пула памяти)
Сэкономим на new/delete и конструкторах/диструкторах. Заодно обезопасим код от всяких мерзких исключений, возможных утечек памяти и бла бла бла.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
25.06.2011, 10:58     Указатели #34
Цитата Сообщение от Hagrael Посмотреть сообщение
3) Как программа узнает, какие места ОЗУ ей можно занимать (не заняты др. программой), а какие - нет
В современных многозадачных операционных системах каждая работающая программа, условно говоря, ничего не знает о других работающих программах и работает так, как будто она работает одна в однозадачной операционной системе. Это очень корявое объяснение на пальцах. Сказал к тому, что на данном этапе тебе не стОит этом вопросом заморачиваться - за тебя всё сделает операционная система

Добавлено через 5 минут

Не по теме:

А вообще, тему надо советовать к прочтению новичками - уж очень много вопросов и ответов в ней сконцентрировано

taras atavin
Ушёл с форума.
 Аватар для taras atavin
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.06.2011, 11:54     Указатели #35
Цитата Сообщение от Evg Посмотреть сообщение
В современных многозадачных операционных системах каждая работающая программа, условно говоря, ничего не знает о других работающих программах и работает так, как будто она работает одна в однозадачной операционной системе. Это очень корявое объяснение на пальцах. Сказал к тому, что на данном этапе тебе не стОит этом вопросом заморачиваться - за тебя всё сделает операционная система
Это ты так кратко переформулировал мой пост?

Добавлено через 5 минут
Цитата Сообщение от Hagrael Посмотреть сообщение
Хм. Теперь у меня возник такой вопрос: Как вообще программа узнает, сколько байт имеет та или иная переменная? Вот к примеру код: Вот откуда программа узнает, что надо отбросить дробную часть? В самой переменной a ведь не хранится ее размер. По-моему, тип уже лежит в коде, и программе не надо узнавать его. То есть когда ты говоришь int a;, компилятор запоминает, что переменная a в данном скопе имеет тип int и в последствии в откомпилированном коде что-то меняется. То есть в коде не сказано
Ну так вот именно. Для статических переменных компилятор сразу запомнил их тип, к нему привязан рахзмер. А для указателей? Размер самого указателя в одной системе всегда один. А со сколькими байтами по хранимому в указателе адресу связан указатель под каждым именем?
C++
1
2
char *a;
float *b;
Со сколькими байтами связан a, а со сколькими b? Это определяется типом указтеля.

Добавлено через 1 минуту
Цитата Сообщение от Hagrael Посмотреть сообщение
А разве может быть по-другому?
Для другого исходника будет именно по другому. Например,
C++
1
2
char a;
a=((char)((int)5.5));
Добавлено через 6 минут
Цитата Сообщение от Hagrael Посмотреть сообщение
Но она же возвращает переменную с типом type*. А такие переменные называются указателями. Чем является адрес?
Нет. Она возвращает значение типа type *. Тип type * - указательный тип. А значение указателя - это адрес.

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

Добавлено через 59 секунд
Цитата Сообщение от Hagrael Посмотреть сообщение
Если так, то у указателей какой-то специфический тип. Будь он int, double или каким-либо другим, все равно под него выделяется 2 блока памяти: число - размер типа; и число - адрес.
Размер подразумевается на этапе разработки, а не хранится. А адрес хранится.

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

Добавлено через 1 минуту
Цитата Сообщение от Hagrael Посмотреть сообщение
о зачем тогда писать int* p=&a;, достаточно просто написать *p=&a;
C++
1
2
int p;//Как здесь определить, тип?
p=&a;
.

Добавлено через 4 минуты
Цитата Сообщение от pito211 Посмотреть сообщение
Но если ты с помощью new выделяешь память для неинтегральных типов, то там действительно кладётся некоторый заголовок, чтобы знать потом как удалять delet[]-ом
Каким бы ни был тип, он известен на этапе компиляции. При delete будет вызов своего деструктора и всего делов, а деструктор должне быть у любого класса. Если даже ты его не сделал, то компил сделает его неявно. Дополнительная инфа нужна только при указателям базовых классов при наследовании, да и то в она скорее всего в самом данном, а не в указателе.

Добавлено через 1 минуту
Цитата Сообщение от silent_1991 Посмотреть сообщение
Указатель должен вместить ровно такое число, сколько максимально блоков памяти мы можем адресовать, больше и не надо.
Причём, однобайтных.

Добавлено через 4 минуты
Цитата Сообщение от Hagrael Посмотреть сообщение
Вы имеете в виду, что выделенная память - это память, которая выделена для приложения или память, выделенная под переменные? Или это одно и то же, и термин "блок памяти, выделенный для приложения" всего лишь условный, и на самом деле приложению дается ровно столько байт, сколько нужно для его переменных (что вполне логично)?
Нет. Для кода память тоже выделяется, но этим система занимается полностью самостоятельно, явно же память выделяется только для динамичесих переменных. То есть во-первых только для перемнных, а во-вторых только для динамических. Оба признака сразу.

Добавлено через 4 минуты
Цитата Сообщение от Hagrael Посмотреть сообщение
Это, конечно, только мое предположение, как бы могла работать программа. Если компилятору известен тип переменной a, то он вполне сможет передать этот тип указателю p. Т. е. как бы компилятор сам допишет int перед записью *p=&a. Конечно, это глупо, гораздо лучше даже для программиста явно указать тип переменных, на которые может указывать указатель, но думаю, что логичнее в таком случае было бы использовать отдельный тип pointer без излишек типа int.
Нет. Указатель обозначается звёздочкой, нетипизированны тоже. Не хочешь указаывать тип данных? Эйси, указатель на void (пишется
C++
1
void *p;
) и никаких гвоздёв, а если специальное имя для указательного типа будет только мешать.

Добавлено через 3 минуты
Цитата Сообщение от Hagrael Посмотреть сообщение
Хм. Да, все манипуляции с типами проводятся еще компилятором, но разве компилятор не может сам добавить int (как написано выше)?
Может, именно это и называется выводом типов. Но лучше иметь приведение типов, то есть когда тебе надо переменнйо одного типа специально присвоить значение другого, а совмещение и вывода, и приведения грозит дополнительными сложностями в изучении такого языка.

Добавлено через 2 минуты
Цитата Сообщение от silent_1991 Посмотреть сообщение
Hagrael, ваши предположения не верны, ничего никуда автоматически не подставляется. И, ещё раз, звёздочка при объявлении относится к типу, а не к переменной, так что ваше *p = &a; в принципе не верно, даже если забыть про то, что тип надо указывать. Выражение int *p = &a; нужно читать как (int *)p = &a; (скобки для понятности). Т.е. вот это int * и есть ваш тип pointer. А если писать p = &a;, как компилятор поймёт, что мы хотим работать с p как с указателем, а не как просто с целым числом? Конечно, он мог бы предположить что-то, но его предположение могло бы оказаться неверным. Указывая же тип явно, мы сообщаем, что переменная p является указателем на тип int (int *), и компилятор всё обработает верно.
Надёжность вывода, кстати, тоже фактор. Хотя и её можно обеспечить. Но сколько это займёт времени и насколько можно будет быть уверенным в том, что оно получилось надёжно?

Добавлено через 3 минуты
Цитата Сообщение от Hagrael Посмотреть сообщение
А выделенная под приложение память - это память, необходимая приложению, или же есть некий резерв?
А это тебе решать. Память, выделенаня для кода (самой программы), для статических переменных и для автоматических (автоматические в стеке валяются) выделяется точно в том количестве, сколько нужно. А динамическую ты сам можешьь резервировать. Например, нажен тебе массив на 10 элментов, а ты с запасом выделяешь 16 (ближайшую не меньшую стпень двойки), надо тебе 100, ты выделяешь 128 и делаешь это на случай, если потом вдруг окажется, что надо больше.

Добавлено через 1 минуту
Цитата Сообщение от Hagrael Посмотреть сообщение
Можно спрогнозировать, сколько памяти будет выделено для программы на помеченном этапе выполнения программы? Если подумать логично, то да, можно, и место будет равняться 8 байтам. Но может быть, ОС выделяет некоторый резерв для программы?
Из этого текста, как раз ясно только, что 8-ми байт не хватит. А сколько нужно не ясно. 8 байт на данные видно. А сколько на код?

Добавлено через 1 минуту
Цитата Сообщение от Chelioss Посмотреть сообщение
new? или вы имели ввиду, что нет в его коде?
new - динамичекое выделение памяти, а не динамическая декларация переменной.
talis
 Аватар для talis
789 / 541 / 37
Регистрация: 11.05.2010
Сообщений: 1,298
Записей в блоге: 1
25.06.2011, 11:58     Указатели #36
Цитата Сообщение от Hagrael Посмотреть сообщение
Если так, то у указателей какой-то специфический тип. Будь он int, double или каким-либо другим, все равно под него выделяется 2 блока памяти: число - размер типа; и число - адрес. Таким образом, выходит, что указатель любого типа имеет одинаковый размер. Это так?
Так-то оно так, да не совсем по этой причине. Есть ведь такая штука, как указатель на массив - не забывайте об этом. Впрочем, когда вы его увидите, вы его не отличите от указателя на простую переменную:

C
1
2
char * ptr; // указатель на массив char или на одну переменную типа char?
int * array; // указатель на массив int или на одну переменную типа int?
Ответить можно и так, и так. Один и тот же указатель может хранить адрес любого по размеру куска памяти. Потому, что единственное, что он хранит - это адрес начала этого куска. А сколько там в нём байт - это уже совершенно не его дело. Тип перед указателем пишется для того, чтобы компилятор знал, каков размер одного элемента этого массива (даже если в нём всего один элемент - читай, просто переменная). Смотрите:

C
1
2
short array[8];
short * ptr = &array;
В памяти short array[8] выглядит так:

0A 1B C4 C8 14 00 08 42 38 31 AA 89 C1 F7 F8 F0 01 FF BC 04 0A 1B C4 C8 14 00 08 42

То есть мы знаем, что у нас есть массив, который имеет в себе 8 элементов, каждый размером sizeof(short), то есть у меня это 2 байта. И у нас есть адрес начала этого массива, скажем, 2048 (напишу в десятичной для наглядности). То есть адрес 5-го элемнта массива будет 2048 + (5 * sizeof(short) ) = 2048 + (5 * 2) = 2048 + 10 = 2058. И начиная с адреса 2058 нам надо считать 2 байта, чтобы прочитать этот элемент массива.

Зная это, компилятор может заранее сделать код для доступа к i-му элементу массива ( array[i] ). Вот формула: 2048 + ( i * 2 ). Если адрес на этапе компиляции не известен, но мы имеем указатель на начало этого массива, то можем сделать так: ptr + ( i * 2 ). Допустим, ptr содержит 3072, а i имеет значение 7. Дальше по аналогии: 3072 + 7 * 2 = 3072 + 14 = 3086. И начиная с адреса 3086 читаем 2 байта и получаем значение элемента ptr[i].

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

А как вы думаете, почему можно легко сделать

C
1
printf( "%i", ptr[-1] );
? компилятор тупо сделает так: ptr + ( i * size ). Если ptr = 1024, i = -1, size = 4, то 1024 + (-4) = 1020 - арес начала -1 элемента массива. По этой же логке нумерация элементов массива начинается с нуля, то есть адрес начала первого элемента равен адресу начала массива: 1024 + (0 * 4) = 1024 + 0 = 1024. Вот и всё.
taras atavin
Ушёл с форума.
 Аватар для taras atavin
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.06.2011, 11:59     Указатели #37
Цитата Сообщение от ValeryLaptev Посмотреть сообщение
Перечисленные вами проблемы начинают сказываться только при ОЧЕНЬ больших объемах используемой виртуалки и при очень маленькой реальной памяти. Если реальной памяти 2-4 гига, то практически на все вами перечисленное можно не обращать внимания.
А кэш? А дальность переходов? А если умудришся не уложится даже в 16 гигов? Передача большого объекта в функцию по указателю, или по ссылке отменяет и лишнии такты при копировании, и перерасъход памяти на копию. Только без фанатизма: если 1000 раз нужен промежуточный результат, на вычисление которого уходит миллион тактов, то эффективнее один раз вычислить и запомнить.
Hagrael
БТР - мой друг
 Аватар для Hagrael
331 / 273 / 2
Регистрация: 07.01.2010
Сообщений: 1,932
25.06.2011, 15:24  [ТС]     Указатели #38
Итак, почему надо указывать тип для указателя? Ведь компилятор может узнать тип переменной, на которую указатель указывает! Ответ: Да, компилятор может сделать приведение типов, но куда лучше, чтобы программист сам понимал, на переменную какого типа будет указывать указатель. Поэтому разработчики языка решили, что пусть лучше будет обязательным объявление типов у указателей. Так ведь, да?

Цитата Сообщение от talis Посмотреть сообщение
Если адрес на этапе компиляции не известен, но мы имеем указатель на начало этого массива, то можем сделать так: ptr + ( i * 2 ).
Стоп. А как может быть адрес переменной не известен на этапе компиляции?

Я понимаю, что про память я рано заговорил, но все-таки. Физическая память - память, работа с которой ведется исключительно на аппаратном уровне, программы и не знают, что это такое. Она имеет адрес - номер (вот только в байтах или в битах?). А виртуальная память - память, с которой работают программы. По поводу второй мне не очень понятно, что она из себя представляет. Вот физическая память - это биты в ОЗУ, а виртуальная - это что?

Каждая программа думает, что она одна. Обеспечивает эту сладкую иллюзию для эгоистки-программы операционная система. Но мне не до конца понятно вот что: в коде откомпилированного файла не хранится точный адрес переменной, по которому ее надо записать. Ведь если так будет, то по этому адресу может находиться другая переменная, и возникнет конфликт. Как я понимаю, программа спрашивает адрес у ОС. Вот как я это вижу:
C++
1
int a=6;
Код
Дорогая ОС, выдели, пожалуйста, мне под переменную 4 байтика и пусть там будет комбинация зажженных битов 110. Да-да, и не забудь вернуть мне ее адрес!
Действительно все происходит так? Кст., в указателе, как я понимаю, хранится виртуальный адрес, да?

Цитата Сообщение от taras atavin Посмотреть сообщение
При трансляции компил отвёл переменной b один адрес, переменной а - другой. На этапе трансляции это тоже укзатели.
Вот вот этого я что-то не понял. На этапе компиляции переменные являются указателями? О_о

Цитата Сообщение от taras atavin Посмотреть сообщение
Надёжность вывода, кстати, тоже фактор
Извините, а вы можете объяснить, что это?
talis
 Аватар для talis
789 / 541 / 37
Регистрация: 11.05.2010
Сообщений: 1,298
Записей в блоге: 1
25.06.2011, 15:38     Указатели #39
Цитата Сообщение от Hagrael Посмотреть сообщение
Физическая память - память, работа с которой ведется исключительно на аппаратном уровне, программы и не знают, что это такое. Она имеет адрес - номер (вот только в байтах или в битах?). А виртуальная память - память, с которой работают программы. По поводу второй мне не очень понятно, что она из себя представляет. Вот физическая память - это биты в ОЗУ, а виртуальная - это что?
Память адресуется по байтам, каждый из которых состоит из 8 бит. На счёт виртуальной памяти - вот схема.

Цитата Сообщение от Hagrael Посмотреть сообщение
Стоп. А как может быть адрес переменной не известен на этапе компиляции?
Если вы динамически выдеяете массив (new, malloc), то размер всего массива может быть заранее неизвестен. Известен только размер элемента массива.

Цитата Сообщение от Hagrael Посмотреть сообщение
Но мне не до конца понятно вот что: в коде откомпилированного файла не хранится точный адрес переменной, по которому ее надо записать. Ведь если так будет, то по этому адресу может находиться другая переменная, и возникнет конфликт.
Компилятор-то имеет список всех переменных. Он генерирует код, который разместит их в разных местах. Да и операционка тоже не без мозгов, двум программам одну страницу памяти не отдаст.

Цитата Сообщение от Hagrael Посмотреть сообщение
Кст., в указателе, как я понимаю, хранится виртуальный адрес, да?
Да, с точки зрения программы её память непрерывна, и адреса виртуального пространства далеко не всегда соответствуют адресам реального. Более того, долго не используемая память может сбрасываться в дисковый своп (swap, терминами винды - файл подкачки). При попытке обращения к области (виртуальной) памяти, которая реально была сброшена в своп, происходит загрузка её из свопа в оперативку. Если оперативка забита, то места для загрузки этой памяти может не быть, и тогда в своп перед этим будет сброшена страница, к которой дольше всех не было обращения. Но программа этого не видит, для неё виртуальная память как была единой так и осталась. Посмотрите схему, которую я привёл выше.

Цитата Сообщение от Hagrael Посмотреть сообщение
На этапе компиляции переменные являются указателями?
Нет, но они ведь размещаются в разных местах в памяти, иначе бы они затирали друг друга. По-этому и адрес у них разный.
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
25.06.2011, 15:59     Указатели
Еще ссылки по теме:

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

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

Или воспользуйтесь поиском по форуму:
Hagrael
БТР - мой друг
 Аватар для Hagrael
331 / 273 / 2
Регистрация: 07.01.2010
Сообщений: 1,932
25.06.2011, 15:59  [ТС]     Указатели #40
За схему большое спасибо, значит, что виртуальная память так же идет на винчестер.

Цитата Сообщение от talis Посмотреть сообщение
Если вы динамически выдеяете массив (new, malloc), то размер всего массива может быть заранее неизвестен. Известен только размер элемента массива.
Размер может быть не известен, но адрес-то должен... Или нет? Кстати, родился вопрос: А может ли ОС менять расположение переменных в физ. памяти?

И вот ряд утверждений, поправьте меня, пожалуйста, если я ошибусь:
1) Виртуальная память - 16-ричное число, которое и содержится в указателе;
2) У нескольких программ могут быть одинаковые виртуальные адреса;
3) Ячейки массива в физ. памяти далеко не всегда расположены последовательно, как в виртуальной.

Добавлено через 10 минут
А где хранятся связки Вирт. адрес => Физ. адрес? В .tmp файле?
И может ли ОС выделить одной программе более 4 ГБ памяти? И зачем ОС такие колосальные объемы - 2 ГБ? И точно ли в Windows под начало выполнения программы выделяется 4 ГБ памяти? Может ли быть выделено меньше? А то у меня столько процессов, не верится, что на каждый уходит по 4 ГБ.
Yandex
Объявления
25.06.2011, 15:59     Указатели
Ответ Создать тему
Опции темы

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