Модераторы: bsa

Поиск:

Закрытая темаСоздание новой темы Создание опроса
> Микс, Разные простые вопросы 
V
    Опции темы
Hagrael
  Дата 26.6.2011, 14:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 143
Регистрация: 26.6.2011

Репутация: нет
Всего: нет



Здравствуйте, уважаемые форумчане. Я не так давно начал учить C++ и засел на некоторых вопросах:
1) Как сдвинуть указатель на несколько байт? (игнорируя арифметику указателей)
    Реально ли сдвинуть на несколько бит?
2) Почему массив сделали константой? И почему вообще стоит использовать константы?
3) Касательно констант, в следующем коде:
Код
int a;
a=10;

под 10 тоже выделяется отдельное место? А в этом:
Код
int a=10;

4) Если говорить "на языке компьютера", то операция &a возвращает указатель, а не адрес? Ведь эта операция возвращает тип type*, присущий указателям. А адрес - это число (типа int) по моему мнению.
5) Каков порядок инициализации массива в следующем коде:
Код
char a[]="Hello";

Строка компилятором заменяется на указатель на  массив типа char. Значит, инициализация состоит из следующих этапов: узнаем размер массива по правую часть; освобождаем память под этот размер; по очереди копируем ячейки правого массива в соответственные ячейки левого. Так?
6) Странно работает программа:
Код
char a[10], b[10];
cin.getline(a, 4);
cin.getline(b, 4);
cout << a << endl;
cout << b;

Я один (!) раз ввожу "12345", а она мне выдает "123"! Я же 2 раза использовал функцию cin.getline! Не могли бы вы объяснить мне, почему мне приходится вводить информацию один раз, и переменной b ничего не присваивается?

Заранее благодарен  smile 

Это сообщение отредактировал(а) Hagrael - 26.6.2011, 15:00
PM MAIL   Вверх
hawk3500
Дата 26.6.2011, 15:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 246
Регистрация: 6.2.2009

Репутация: нет
Всего: 2



1)
указатель это по сути дела адрес!
И здвинуть его модно прибавив (или отняв) нужное вам количество байт.
У 99% ПК байтовое адресное пространство.
2)
Не ясен вопрос.
поясните.
3)
выделяется.
В разных местах может выделяться.
4)
указатель-это и есть число.
Всё верно.
5)
порядок будет несколько различен от того глобальная это переменная или нет

--------------------
воин dzen'a
PM MAIL   Вверх
volatile
Дата 26.6.2011, 15:32 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 16
Всего: 85



Цитата(Hagrael @  26.6.2011,  14:45 Найти цитируемый пост)
1) Как сдвинуть указатель на несколько байт? (игнорируя арифметику указателей)

Код

int *p = адрес;
// сдвинуть на 3 байта
 p = (int*)((char*) p + 3);


Цитата(Hagrael @  26.6.2011,  14:45 Найти цитируемый пост)
Реально ли сдвинуть на несколько бит?

нет


Цитата(Hagrael @  26.6.2011,  14:45 Найти цитируемый пост)
Почему массив сделали константой? И почему вообще стоит использовать константы?

Размер массива сделали константой. Почему не знаю. В новых версиях это уже не константа. Видимо наследие тяжёлых времен.

Цитата(Hagrael @  26.6.2011,  14:45 Найти цитируемый пост)
3) Касательно констант, в следующем коде:
int a;
a=10;
под 10 тоже выделяется отдельное место? А в этом:
int a=10;

Во-первых у вас, это не константа. 
const int a; <- вот это константа.
Выделение памяти - зависит от реализации.
По опыту могу сказать, что от формы инициализации не зависит. А зависит есть ли где в программе указатель на эту константу.
Если указателя нет, то и память не выделяется, а просто подставляется значение.
Если есть указатель, то память будет выделена.

Цитата(Hagrael @  26.6.2011,  14:45 Найти цитируемый пост)
 Если говорить "на языке компьютера", то операция &a возвращает указатель, а не адрес? 

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

Цитата(Hagrael @  26.6.2011,  14:45 Найти цитируемый пост)
Каков порядок инициализации массива в следующем коде:
char a[]="Hello";

Зависит от того в каком месте стоит.
Если в функции, и если не статик, то будет примерно так как вы написали.
для статик или если это глабальный массив, то выделение будет произведено на этапе компиляции.



PM MAIL   Вверх
Сыроежка
Дата 26.6.2011, 15:53 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 127
Регистрация: 24.6.2011

Репутация: 1
Всего: 1



Здесь вам неправильно ответили. Сдвинуть указатель на целове на несколько байт нельзя! То есть этот код юудет неопределенным согласно стандарту языка. Поэтому приведенный в одном комментарии од, показанный ниже, является некорректным. 
Цитата(volatile @  26.6.2011,  15:32 Найти цитируемый пост)
1:
int *p = адрес;
// сдвинуть на 3 байта
 p = (int*)((char*) p + 3);


Более того на некоторых системах он может привести к аварийному завершению работы, если вы по этому указателю захотите что-то записать. Указатель для типа int должен быть выровнен на границу слова.

Тем более нельзя указатель сдвинуть, как вы пишите, на несколько бит, так как минимальная единица адресация - это байт.

Что касается массива, то это не константа. Просто нет такой отдельной переменной, как массив, чтобы вы могли изменить значение по адресу этой переменной.  То есть, фактически, вам менять нечего!  Чтобы было понятно, то, например, указатель на элемент массива - это отделная ячейка памяти. Поэтому указатели можно менять, а с именем массива никакой ячейки памяти не связано. В памяти располагается лишь сам массив, который вы и можете менять. 

Что касается вашего примера с определением целочисленного значения, то очно целочисленные константы в памяти не хранятся. То есть компилятор, например, в вашем коде

Цитата(Hagrael @  26.6.2011,  14:45 Найти цитируемый пост)
1:

int a;
a=10;


может сразу же записать по адресу переменной 'a' значение 10, и не хранить этот литерал 10 отдельно в памяти. Но бывают случаи, когда, например, используются выражения в качестве аргументов функции, когда компилятор хранит отдельно целочисленные литералы в своем пуле литералов. Но в общем случае компилятор оычно непосредственно вставляет в код целочисленные литералы без сохранения их в отдельном участке памяти.


Что касается примера

Цитата(Hagrael @  26.6.2011,  14:45 Найти цитируемый пост)
1:

char a[]="Hello";


то в данном случае компилятор также не хранит строковый литерал в памяти, а лишь использует его для инициализации массива. То есть память выделяется только для массива a[], размер которого определяется как размер строкового литерала плюс один байт по нулевой завершающий символ. В вашем примере для массива будет выделено 6 байтов, и в них будет скопирован строковый литерал, включая завершающий ноль.

То есть этот код полностью эквивалентен следующему коду

char a[ 6 ] = { 'H', 'e', 'l', 'l', 'o', '\0' };

То есть никаких двух массивов, один - для строкового литерала, а другой - для идентификатора 'a', не создается. Создается лишь один массив для идентификатора 'a'.
Цитата(Hagrael @  26.6.2011,  14:45 Найти цитируемый пост)
1:
:


char a[10], b[10];
cin.getline(a, 4);
cin.getline(b, 4);
cout << a << endl;
cout << b;


Что касается этого примера выше, товы ведялете буфер для ввода строки в 4 байта. Функция считывает все то, что вы ввели до нажатия клавиши Enter, а затем копирует лишь 3 байта, добавляя их четвертым байтом нулевого значения. 
PM MAIL   Вверх
volatile
Дата 26.6.2011, 16:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 16
Всего: 85



Цитата(Сыроежка @  26.6.2011,  15:53 Найти цитируемый пост)
Здесь вам неправильно ответили. Сдвинуть указатель на целове на несколько байт нельзя! То есть этот код юудет неопределенным согласно стандарту языка. Поэтому приведенный в одном комментарии од, показанный ниже, является некорректным. 
Цитата(volatile @  26.6.2011,  15:32 )
1:
int *p = адрес;
// сдвинуть на 3 байта
 p = (int*)((char*) p + 3);
Более того на некоторых системах он может привести к аварийному завершению работы, если вы по этому указателю захотите что-то записать. Указатель для типа int должен быть выровнен на границу слова.

Согласен с тем что код не кошерный, и новичкам такое показывать конечно нестоило.
Но строго говорить что 
Цитата(Сыроежка @  26.6.2011,  15:53 Найти цитируемый пост)
Указатель для типа int должен быть выровнен на границу слова.

Вовсе он не должен.
Есть куча стркутур, где DWORD'ы не выровнены на границу слова.
И на PC это абсолютно нормально.
Хотя повторюсь, так делать, конечно-же не стоит.

PM MAIL   Вверх
Сыроежка
Дата 26.6.2011, 16:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 127
Регистрация: 24.6.2011

Репутация: 1
Всего: 1



Цитата(volatile @  26.6.2011,  16:08 Найти цитируемый пост)
Цитата(Сыроежка @  26.6.2011,  15:53 )
Указатель для типа int должен быть выровнен на границу слова.


Вовсе он не должен.
Есть куча стркутур, где DWORD'ы не выровнены на границу слова.
И на PC это абсолютно нормально.
Хотя повторюсь, так делать, конечно-же не стоит.


В вашей структуре может быть целове число не выравнено на границу челого числа, но если вы занесете адрес этого целого числа в указатель, то такое поведение не соответсвует станарту  является неопределенным. И то, что у вас где-то это может "проскочить", совершенно не означает, что код корректный.
Приведу простой пример. Неокторые делают такое объявление

char * p = "12";

А затем менют значение по адресу этого указателя. Например, *p = '3'; При раоте, например, MS-DOS это пройдет, так как память там не защищенная, но тем не менее этот код некорректный согласно стандарту, так как строковые литералы менять запрещается.
PM MAIL   Вверх
volatile
Дата 26.6.2011, 16:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 16
Всего: 85



Сыроежка, ваш пример, с
*p = '3';
к теме выравнивания int'ов никакого отношения не имеет.

Добавлено @ 16:28
Кроме того он и не скомпилится.

Но повторю.
Жалею что показал это.
Так делать нехорошо.

Это сообщение отредактировал(а) volatile - 26.6.2011, 16:29
PM MAIL   Вверх
Сыроежка
Дата 26.6.2011, 16:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 127
Регистрация: 24.6.2011

Репутация: 1
Всего: 1



Цитата(volatile @  26.6.2011,  16:24 Найти цитируемый пост)
Сыроежка, ваш пример, с
*p = '3';
к теме выравнивания int'ов никакого отношения не имеет.


Имеет самое непосредственное отношение! Вы просто не поняли! Я хотел показать, что некоторые некорректные конструкция языка могут проскакивать на той или иной платформе, но тем не менее код является некорректным. Точно также и в случае занесения в указатель на тип int значения, которое не кратно размеру слова, то есть размеру int. Если вы попытаетесь по этому адресу записать целое число, то на многих платформах вы получите аварийное завершение. Н адругих платформах это может проскочить. Но то, что где-то это проскочило, это совершенно не делает код корректным! Стандарт по этому поводу прямо говорит, что такое поведение является неопределенным, то есть вы играетет в рулетку на свой страх и риск. А точнее пишите код, который не является допустимым кодом ни С и ни С++.

Добавлено через 8 минут и 45 секунд
Цитата(volatile @  26.6.2011,  16:24 Найти цитируемый пост)
Сыроежка, ваш пример, с
*p = '3';
к теме выравнивания int'ов никакого отношения не имеет.

Добавлено @ 16:28
Кроме того он и не скомпилится.


Зря вы думаете, что такой код не компилируется! Многие компияторы, особенно на С, спокойно компилируются этот код. Кроме того вы не забывайте, что компилятору бывает очень трудно обнаружить такую ошибку, например, есл такой указатель передается в качестве аргумента какой-нибудь другой функции, а уж в этой функции происходт присвоение по адресу указателя.

У вас код не окмпилируется скорей всего по той причине, что вы компилируете как код С++, и компилятор видет это непосредственное изменение.  Дело в том, что ест разница между С и С++ в том, како тип имеют строковые литералы. На С++ строковый литерал имеет тип массива производного от типа const char, а в С это массив, производный от типа char. Но тем не менее стандарты и С++ и С строго запрещают менять строковые литералы.
PM MAIL   Вверх
volatile
Дата 26.6.2011, 16:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 16
Всего: 85



Сыроежка, я уже тыщу раз сказал что пример не хорош, и что я жалею что показал его.
Просто иногда новичку нужно знать (вспоминая себя) и такие вещи.
но на вопрос
Цитата(Hagrael @  26.6.2011,  14:45 Найти цитируемый пост)
Как сдвинуть указатель на несколько байт? (игнорируя арифметику указателей)

ответ мой такой.
Цитата(volatile @  26.6.2011,  15:32 Найти цитируемый пост)
int *p = адрес;
// сдвинуть на 3 байта
 p = (int*)((char*) p + 3);


Спорить дальше не буду
 smile 
PM MAIL   Вверх
Сыроежка
Дата 26.6.2011, 16:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 127
Регистрация: 24.6.2011

Репутация: 1
Всего: 1



Цитата(Hagrael @  26.6.2011,  14:45 Найти цитируемый пост)
5) Каков порядок инициализации массива в следующем коде:

код C++
1:

char a[]="Hello";


Как происходит инициализация массива строковым литералом, я вам показал выше в своем комментарии.
Но нужно не путать две вещи: инциализацию массива строковым литералом, и инициализацию указателя на char адресом строкового литераа, то есть когда вы пишите

char *p = "Hello";

В этом случае компилятор действительно сохраняет символьный строковый литерал в памяти в виде символьного массива, а указателю p присваивает знчение адреса первого элемента этого массива.
Причем следует знать важные отличия характеристик строкового итерала в С и в С++. 
В С строковый литерал имеет тип массива, производного от типа char (массивы называются в стандартах С и С++ производными типами, то есть их тип определяется типом элемента массива). В то время как в С++ строковый литерал имеет тип, произвоный от типа const char.

Есть и еще одно важное различие, которые многие программисты не знают. Так, например, следующий код

char s[3] = "XYZ"; 

является корректным в С. В то время как в С++ такой код является некорректным. 
PM MAIL   Вверх
hawk3500
Дата 26.6.2011, 18:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 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
PM MAIL   Вверх
Сыроежка
  Дата 26.6.2011, 18:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 127
Регистрация: 24.6.2011

Репутация: 1
Всего: 1



Цитата(hawk3500 @  26.6.2011,  18:00 Найти цитируемый пост)
ИМХО чтобы не было проблем с выравниванием и проблем с выравниванием и разрадностью многих систем порой всё же стоит выходить за пределы 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) 


Я уже написал выше, что этот код некорректный. Вы можете проделывать различные преобразования, но код так и останется не корректным.
И это имеет дело не "к чистоте "языка, как вы думаете, а к совершенно некорректному программированию, которое к языку отношения не имеет.

Ваши попытки мне напоминают стремление слово "корова" писать через "а". От того, что вы букву "а" напишите, например, прописными буквами, а не строчными, то есть в виде "кАрова", не делает прапвильным правописание этого слова.

То есть ваш комментарий ни к С, ни к С++ отношениря не имеет. А имеет отношение лишь к тому, чего не надо делать на С и С++! Если бы автор вопроса интересовался  именно этим, то есть тем, как не надо писать на С и С++, то ваш ответ был бы к месту.

Добавлено через 3 минуты и 32 секунды
Цитата(hawk3500 @  26.6.2011,  18:00 Найти цитируемый пост)
Я бы реализовал это просто.
void *Point;
__asm
{
  mov eax,Point
  add eax,03h
  mov Point,eax
}


Кстати сказать, хотел бюы отметить, что, во-перваых, в С вообще нет ключевого слова asm  ни в каких модификациях (с префиксом в виде знаков подчеркивания или нет), а в С++ есть лишь ключевое слово asm Так что предложенный вами способ имеет отношение лишь к какому-то оригинальному компилятору. 
PM MAIL   Вверх
hawk3500
Дата 26.6.2011, 18:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 246
Регистрация: 6.2.2009

Репутация: нет
Всего: 2



void *Point;
__asm
{
  mov eax,Point
  add eax,03h
  mov Point,eax
}

и где же сдесь ошибка??????
--------------------
воин dzen'a
PM MAIL   Вверх
Сыроежка
Дата 26.6.2011, 18:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 127
Регистрация: 24.6.2011

Репутация: 1
Всего: 1



Цитата(hawk3500 @  26.6.2011,  18:15 Найти цитируемый пост)
void *Point;
__asm
{
  mov eax,Point
  add eax,03h
  mov Point,eax
}

и где же сдесь ошибка?????? 


А я не гвоорил, что здесь ошибка! Я говорил, что к исходному вопросу это отношения не имеет. Автор, фактически, спрашивал, может ли указатель иметь значение, не кратное размеру int. Ответ прост: не может! То есть такое поведение неопределенное.

Что касается вашего примера, то ваша переменная Point объявлена не как указатель на int, а как указатель на void. Так что к исходному вопросу вообще никакого отношения не имеет. Внутреннее представление указателя на void по крайней мере в соответствии со стандартом С соответствует внутреннему представлению указателя на char. Это сделано для того, чтобы можно было использовать такие функции, как, например, memcpy.
Очевидно, что указатель на void может адресовать любой байт. Тем не менее это неверно для указателей на int.

Добавлено через 50 секунд
Я имел в виду, может ли указатель на int иметь значение не кратное размеру int.

Добавлено через 13 минут и 39 секунд
Цитата(Hagrael @  26.6.2011,  14:45 Найти цитируемый пост)
1) Как сдвинуть указатель на несколько байт? (игнорируя арифметику указателей)


То есть если отвечать на этот исходный вопрос, то сделать это, то есть игнорируя арифметику указателей, нельзя. Так как тогда указатель будет содержать некорректное значение. И примером этого является указатель на int. 
Естественно, что указатель на char ожет адресовать любой байт, но даже он не игнориует арифметику указателей, то есть сдвиг происходи согласно размеру типа ( в данном случае sizeof( char ) ), на данное которого указатель указывает. Более того могут быть и wchar_t типы символов. 
Конечно, если использовать тип char * , то вопросов никаких нет, так как sizeof( char ) == 1. Но, как я понимаю, автора не этот вопрос мучал, а мучал вопрос, может ли какой-нибудь указатель хранить значение, которое не удовлетворяет правилам арифметики с указателями.

В связи с вышесказанным ваш пример со вставкой ассемблера вообще выглядет абсурдной, так как можно просто перевести указатель на int  в указатель на char и ему присвоить любое значение! Зачем такие сложности с ассемблером?!!!
PM MAIL   Вверх
Сыроежка
Дата 26.6.2011, 19:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 127
Регистрация: 24.6.2011

Репутация: 1
Всего: 1



То есть суть исходного вопроса, касающегосу игнорирования арифметики указателей сводится к следующему вопросу: является ли следующий код корректным?

Код

int x = 10;
int *p = &x;

p = ( int * )( ( char * )p + 1 );


Ответ: не является!

Это сообщение отредактировал(а) Сыроежка - 26.6.2011, 19:14
PM MAIL   Вверх
Закрытая темаСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
2 Пользователей читают эту тему (2 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.1060 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.