![]() |
Модераторы: bsa |
![]() ![]() ![]() |
|
Hagrael |
|
||||||||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 143 Регистрация: 26.6.2011 Репутация: нет Всего: нет |
Здравствуйте, уважаемые форумчане. Я не так давно начал учить C++ и засел на некоторых вопросах:
1) Как сдвинуть указатель на несколько байт? (игнорируя арифметику указателей) Реально ли сдвинуть на несколько бит? 2) Почему массив сделали константой? И почему вообще стоит использовать константы? 3) Касательно констант, в следующем коде:
под 10 тоже выделяется отдельное место? А в этом:
4) Если говорить "на языке компьютера", то операция &a возвращает указатель, а не адрес? Ведь эта операция возвращает тип type*, присущий указателям. А адрес - это число (типа int) по моему мнению. 5) Каков порядок инициализации массива в следующем коде:
Строка компилятором заменяется на указатель на массив типа char. Значит, инициализация состоит из следующих этапов: узнаем размер массива по правую часть; освобождаем память под этот размер; по очереди копируем ячейки правого массива в соответственные ячейки левого. Так? 6) Странно работает программа:
Я один (!) раз ввожу "12345", а она мне выдает "123"! Я же 2 раза использовал функцию cin.getline! Не могли бы вы объяснить мне, почему мне приходится вводить информацию один раз, и переменной b ничего не присваивается? Заранее благодарен ![]() Это сообщение отредактировал(а) Hagrael - 26.6.2011, 15:00 |
||||||||
|
|||||||||
hawk3500 |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 246 Регистрация: 6.2.2009 Репутация: нет Всего: 2 |
1)
указатель это по сути дела адрес! И здвинуть его модно прибавив (или отняв) нужное вам количество байт. У 99% ПК байтовое адресное пространство. 2) Не ясен вопрос. поясните. 3) выделяется. В разных местах может выделяться. 4) указатель-это и есть число. Всё верно. 5) порядок будет несколько различен от того глобальная это переменная или нет --------------------
воин dzen'a |
|||
|
||||
volatile |
|
||||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2107 Регистрация: 7.1.2011 Репутация: 16 Всего: 85 |
нет
Размер массива сделали константой. Почему не знаю. В новых версиях это уже не константа. Видимо наследие тяжёлых времен.
Во-первых у вас, это не константа. const int a; <- вот это константа. Выделение памяти - зависит от реализации. По опыту могу сказать, что от формы инициализации не зависит. А зависит есть ли где в программе указатель на эту константу. Если указателя нет, то и память не выделяется, а просто подставляется значение. Если есть указатель, то память будет выделена.
Указатель это и есть адрес. точнее указатель это переменная в которой хранится адрес того, на что он указывает.
Зависит от того в каком месте стоит. Если в функции, и если не статик, то будет примерно так как вы написали. для статик или если это глабальный массив, то выделение будет произведено на этапе компиляции. |
||||||||||||
|
|||||||||||||
Сыроежка |
|
||||
Шустрый ![]() Профиль Группа: Участник Сообщений: 127 Регистрация: 24.6.2011 Репутация: 1 Всего: 1 |
Здесь вам неправильно ответили. Сдвинуть указатель на целове на несколько байт нельзя! То есть этот код юудет неопределенным согласно стандарту языка. Поэтому приведенный в одном комментарии од, показанный ниже, является некорректным.
Более того на некоторых системах он может привести к аварийному завершению работы, если вы по этому указателю захотите что-то записать. Указатель для типа int должен быть выровнен на границу слова. Тем более нельзя указатель сдвинуть, как вы пишите, на несколько бит, так как минимальная единица адресация - это байт. Что касается массива, то это не константа. Просто нет такой отдельной переменной, как массив, чтобы вы могли изменить значение по адресу этой переменной. То есть, фактически, вам менять нечего! Чтобы было понятно, то, например, указатель на элемент массива - это отделная ячейка памяти. Поэтому указатели можно менять, а с именем массива никакой ячейки памяти не связано. В памяти располагается лишь сам массив, который вы и можете менять. Что касается вашего примера с определением целочисленного значения, то очно целочисленные константы в памяти не хранятся. То есть компилятор, например, в вашем коде может сразу же записать по адресу переменной 'a' значение 10, и не хранить этот литерал 10 отдельно в памяти. Но бывают случаи, когда, например, используются выражения в качестве аргументов функции, когда компилятор хранит отдельно целочисленные литералы в своем пуле литералов. Но в общем случае компилятор оычно непосредственно вставляет в код целочисленные литералы без сохранения их в отдельном участке памяти. Что касается примера то в данном случае компилятор также не хранит строковый литерал в памяти, а лишь использует его для инициализации массива. То есть память выделяется только для массива a[], размер которого определяется как размер строкового литерала плюс один байт по нулевой завершающий символ. В вашем примере для массива будет выделено 6 байтов, и в них будет скопирован строковый литерал, включая завершающий ноль. То есть этот код полностью эквивалентен следующему коду char a[ 6 ] = { 'H', 'e', 'l', 'l', 'o', '\0' }; То есть никаких двух массивов, один - для строкового литерала, а другой - для идентификатора 'a', не создается. Создается лишь один массив для идентификатора 'a'.
Что касается этого примера выше, товы ведялете буфер для ввода строки в 4 байта. Функция считывает все то, что вы ввели до нажатия клавиши Enter, а затем копирует лишь 3 байта, добавляя их четвертым байтом нулевого значения. |
||||
|
|||||
volatile |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2107 Регистрация: 7.1.2011 Репутация: 16 Всего: 85 |
Согласен с тем что код не кошерный, и новичкам такое показывать конечно нестоило. Но строго говорить что Вовсе он не должен. Есть куча стркутур, где DWORD'ы не выровнены на границу слова. И на PC это абсолютно нормально. Хотя повторюсь, так делать, конечно-же не стоит. |
|||
|
||||
Сыроежка |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 127 Регистрация: 24.6.2011 Репутация: 1 Всего: 1 |
В вашей структуре может быть целове число не выравнено на границу челого числа, но если вы занесете адрес этого целого числа в указатель, то такое поведение не соответсвует станарту является неопределенным. И то, что у вас где-то это может "проскочить", совершенно не означает, что код корректный. Приведу простой пример. Неокторые делают такое объявление char * p = "12"; А затем менют значение по адресу этого указателя. Например, *p = '3'; При раоте, например, MS-DOS это пройдет, так как память там не защищенная, но тем не менее этот код некорректный согласно стандарту, так как строковые литералы менять запрещается. |
|||
|
||||
volatile |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2107 Регистрация: 7.1.2011 Репутация: 16 Всего: 85 |
Сыроежка, ваш пример, с
*p = '3'; к теме выравнивания int'ов никакого отношения не имеет. Добавлено @ 16:28 Кроме того он и не скомпилится. Но повторю. Жалею что показал это. Так делать нехорошо. Это сообщение отредактировал(а) volatile - 26.6.2011, 16:29 |
|||
|
||||
Сыроежка |
|
||||
Шустрый ![]() Профиль Группа: Участник Сообщений: 127 Регистрация: 24.6.2011 Репутация: 1 Всего: 1 |
Имеет самое непосредственное отношение! Вы просто не поняли! Я хотел показать, что некоторые некорректные конструкция языка могут проскакивать на той или иной платформе, но тем не менее код является некорректным. Точно также и в случае занесения в указатель на тип int значения, которое не кратно размеру слова, то есть размеру int. Если вы попытаетесь по этому адресу записать целое число, то на многих платформах вы получите аварийное завершение. Н адругих платформах это может проскочить. Но то, что где-то это проскочило, это совершенно не делает код корректным! Стандарт по этому поводу прямо говорит, что такое поведение является неопределенным, то есть вы играетет в рулетку на свой страх и риск. А точнее пишите код, который не является допустимым кодом ни С и ни С++. Добавлено через 8 минут и 45 секунд
Зря вы думаете, что такой код не компилируется! Многие компияторы, особенно на С, спокойно компилируются этот код. Кроме того вы не забывайте, что компилятору бывает очень трудно обнаружить такую ошибку, например, есл такой указатель передается в качестве аргумента какой-нибудь другой функции, а уж в этой функции происходт присвоение по адресу указателя. У вас код не окмпилируется скорей всего по той причине, что вы компилируете как код С++, и компилятор видет это непосредственное изменение. Дело в том, что ест разница между С и С++ в том, како тип имеют строковые литералы. На С++ строковый литерал имеет тип массива производного от типа const char, а в С это массив, производный от типа char. Но тем не менее стандарты и С++ и С строго запрещают менять строковые литералы. |
||||
|
|||||
volatile |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2107 Регистрация: 7.1.2011 Репутация: 16 Всего: 85 |
Сыроежка, я уже тыщу раз сказал что пример не хорош, и что я жалею что показал его.
Просто иногда новичку нужно знать (вспоминая себя) и такие вещи. но на вопрос
ответ мой такой.
Спорить дальше не буду ![]() |
||||
|
|||||
Сыроежка |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 127 Регистрация: 24.6.2011 Репутация: 1 Всего: 1 |
Как происходит инициализация массива строковым литералом, я вам показал выше в своем комментарии. Но нужно не путать две вещи: инциализацию массива строковым литералом, и инициализацию указателя на char адресом строкового литераа, то есть когда вы пишите char *p = "Hello"; В этом случае компилятор действительно сохраняет символьный строковый литерал в памяти в виде символьного массива, а указателю p присваивает знчение адреса первого элемента этого массива. Причем следует знать важные отличия характеристик строкового итерала в С и в С++. В С строковый литерал имеет тип массива, производного от типа char (массивы называются в стандартах С и С++ производными типами, то есть их тип определяется типом элемента массива). В то время как в С++ строковый литерал имеет тип, произвоный от типа const char. Есть и еще одно важное различие, которые многие программисты не знают. Так, например, следующий код char s[3] = "XYZ"; является корректным в С. В то время как в С++ такой код является некорректным. |
|||
|
||||
hawk3500 |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 246 Регистрация: 6.2.2009 Репутация: нет Всего: 2 |
ИМХО чтобы не было проблем с выравниванием и проблем с выравниванием и разрадностью многих систем порой всё же стоит выходить за пределы C.
Я бы реализовал это просто. void *Point; __asm { mov eax,Point add eax,03h mov Point,eax } Ну а для всех тех кто неприклонно бориться за чистоту языка: void *Point; Point=(void*)((BYTE *)Point)[ 3]. или: Point=(void *)(DWORD(Point)+3) --------------------
воин dzen'a |
|||
|
||||
Сыроежка |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 127 Регистрация: 24.6.2011 Репутация: 1 Всего: 1 |
Я уже написал выше, что этот код некорректный. Вы можете проделывать различные преобразования, но код так и останется не корректным. И это имеет дело не "к чистоте "языка, как вы думаете, а к совершенно некорректному программированию, которое к языку отношения не имеет. Ваши попытки мне напоминают стремление слово "корова" писать через "а". От того, что вы букву "а" напишите, например, прописными буквами, а не строчными, то есть в виде "кАрова", не делает прапвильным правописание этого слова. То есть ваш комментарий ни к С, ни к С++ отношениря не имеет. А имеет отношение лишь к тому, чего не надо делать на С и С++! Если бы автор вопроса интересовался именно этим, то есть тем, как не надо писать на С и С++, то ваш ответ был бы к месту. Добавлено через 3 минуты и 32 секунды
Кстати сказать, хотел бюы отметить, что, во-перваых, в С вообще нет ключевого слова asm ни в каких модификациях (с префиксом в виде знаков подчеркивания или нет), а в С++ есть лишь ключевое слово asm Так что предложенный вами способ имеет отношение лишь к какому-то оригинальному компилятору. |
|||
|
||||
hawk3500 |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 246 Регистрация: 6.2.2009 Репутация: нет Всего: 2 |
void *Point;
__asm { mov eax,Point add eax,03h mov Point,eax } и где же сдесь ошибка?????? --------------------
воин dzen'a |
|||
|
||||
Сыроежка |
|
||||
Шустрый ![]() Профиль Группа: Участник Сообщений: 127 Регистрация: 24.6.2011 Репутация: 1 Всего: 1 |
А я не гвоорил, что здесь ошибка! Я говорил, что к исходному вопросу это отношения не имеет. Автор, фактически, спрашивал, может ли указатель иметь значение, не кратное размеру int. Ответ прост: не может! То есть такое поведение неопределенное. Что касается вашего примера, то ваша переменная Point объявлена не как указатель на int, а как указатель на void. Так что к исходному вопросу вообще никакого отношения не имеет. Внутреннее представление указателя на void по крайней мере в соответствии со стандартом С соответствует внутреннему представлению указателя на char. Это сделано для того, чтобы можно было использовать такие функции, как, например, memcpy. Очевидно, что указатель на void может адресовать любой байт. Тем не менее это неверно для указателей на int. Добавлено через 50 секунд Я имел в виду, может ли указатель на int иметь значение не кратное размеру int. Добавлено через 13 минут и 39 секунд
То есть если отвечать на этот исходный вопрос, то сделать это, то есть игнорируя арифметику указателей, нельзя. Так как тогда указатель будет содержать некорректное значение. И примером этого является указатель на int. Естественно, что указатель на char ожет адресовать любой байт, но даже он не игнориует арифметику указателей, то есть сдвиг происходи согласно размеру типа ( в данном случае sizeof( char ) ), на данное которого указатель указывает. Более того могут быть и wchar_t типы символов. Конечно, если использовать тип char * , то вопросов никаких нет, так как sizeof( char ) == 1. Но, как я понимаю, автора не этот вопрос мучал, а мучал вопрос, может ли какой-нибудь указатель хранить значение, которое не удовлетворяет правилам арифметики с указателями. В связи с вышесказанным ваш пример со вставкой ассемблера вообще выглядет абсурдной, так как можно просто перевести указатель на int в указатель на char и ему присвоить любое значение! Зачем такие сложности с ассемблером?!!! |
||||
|
|||||
Сыроежка |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 127 Регистрация: 24.6.2011 Репутация: 1 Всего: 1 |
То есть суть исходного вопроса, касающегосу игнорирования арифметики указателей сводится к следующему вопросу: является ли следующий код корректным?
Ответ: не является! Это сообщение отредактировал(а) Сыроежка - 26.6.2011, 19:14 |
|||
|
||||
![]() ![]() ![]() |
Правила форума "C/C++: Для новичков" | |
|
Запрещается! 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa. |
2 Пользователей читают эту тему (2 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Для новичков | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |