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

Поиск:

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


hell is here
**


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

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



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


Шустрый
*


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

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



Цитата(alexvs11 @  26.6.2011,  19:31 Найти цитируемый пост)
Сыроежка, приведите выдержку из стандарта про выравнивание, раз вы его тут упоминали 


Резонное предложение. Приведу, но только завтра или послезавтра, так как сейчас общаюсь из нтернет-кафе, и под рукой никаких документов нет.
PM MAIL   Вверх
volatile
Дата 26.6.2011, 19:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(Сыроежка @ 26.6.2011,  19:13)
является ли следующий код корректным?
Код

int x = 10;
int *p = &x;
p = ( int * )( ( char * )p + 1 );

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


а такой код будет корректным ? smile
Код

for ( int i=0; i<sizeof(int); ++i )
   p = ( int * )( ( char * )p + 1 );

Еесли Нет, приведите пример машины, операционной системы, да хоть чего-нибудь, где этот код работать не будет.
(именно этот код с for)

Если Да, то как может в целом корректный код состоять из нескольких некорректных операций?

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


Бывалый
*


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

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



Сыроежка вот вы говорили:

Цитата

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

int x = 10;
int *p = &x;
p = ( int * )( ( char * )p + 1 );


Изначально вопрос стоял так:
Цитата

1) Как сдвинуть указатель на несколько байт? (игнорируя арифметику указателей)
    Реально ли сдвинуть на несколько бит?


Игнорировать арифметику указателей-означает прежде всего не привязываться к базовым типам.
Если у меня есть указатель на тот или иной участок памяти и задача сдвинуть на несколько байт,
то я просто прибавлю к к имеющемуся значению в указателе значение равное сумме того что есть + 3.
А вот какой тип данных вы будете использовать мне всё равно.
С точки зрения программы адресное пространство масива линейно и применение понятия выравнивания не корректно,
разве что быть может это учитываеться в каких нибудь древних системах или при низкоуровневом программировании...но мы о нём речи не ведём.
Пришлите скрин отладчика(ОС значения не имеет) под которым мя явно увидим что это приведёт к падению системы.....


Это сообщение отредактировал(а) hawk3500 - 26.6.2011, 23:31
--------------------
воин dzen'a
PM MAIL   Вверх
volatile
Дата 27.6.2011, 00:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



hawk3500, на некоторых платформах (микроконтроллеры напр.) действительно нельзя обращаться по указателям не выровненным на слово. К PC (Personal Computer), да и вообще к любым машинам на X86 это не отностися, не важно какая система (win*; dos*; *nix).

Сделаю ударение нельзя именно обращаться.
Но здесь никто и не обращается
Мы только прибавляем число к указателю.  smile 

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


Эксперт
****


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

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



удалил. не к чему это.

Это сообщение отредактировал(а) volatile - 27.6.2011, 01:54
PM MAIL   Вверх
borisbn
Дата 27.6.2011, 06:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



>на некоторых платформах (микроконтроллеры напр .) действительно нельзя обращаться по указателям не выровненным на
слово

Мало того, на некоторых компиляторах размер char равен размеру short и равен размеру int - AnalogDevice под какой-то свой процессор (161-й по-моему) 


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
Hagrael
Дата 27.6.2011, 08:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Спасибо всем, кто мне ответил!

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

Код
int x=10;
int* p=&x;
p+=1/4;

Данный код некорректен из-за того, что оператор + может связывать переменную типа type* (любой указатель) только с переменной типа int.
Код
int x = 10;
int *p = &x;
p = ( int * )( ( char * )p + 1 );

А почему некорректен этот код, мне непонятно. Однако, думаю, что прежде следует понять, почему не работает код, приведенный ниже:
Код
int x=10;
int* p=&x;
char *p_char=(char*) p;
cout << p_char;

Консоль абсолютно чиста, если не считать статуса завершения программы. Из-за чего?

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

Массив на самом деле ведь указывает на свою первую ячейку. Однако это не просто указатель, а константа-указатель. Следующий код приводит к ошибке " non-lvalue in assignment ":
Код
int arr[10];
arr++;

А следующий - к ошибке " incompatible types in assignment of `int' to `int[10]' ":
Код
int arr[10];
arr+=1;

Однако я заметил, что обычные константы нельзя копировать в др. переменные без префикса constant, однако массив можно. Т. е. следующий код будет корректным:
Код
int arr[10];
int* pointer=arr;

А следующий - нет, т. к. нет префикса const перед второй строкой:
Код
int arr[10];
int* pointer=arr;

Так что же это такое - массив? Это не просто const type*  smile 

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

int a;
a=10;

под 10 тоже выделяется отдельное место? А в этом:

int a=10;

Вот что я понял. К переменным всех типов присваиваются их литералы, которые заранее никуда в ОЗУ не копируются. Единственное - исключение может составить указатель, указывающий на строку. Тогда строковый литерал изначально превращается в массив типа char, и когда дело доходит до инициализации нашего указателя, то в него возвращается заранее созданный массив.

А под локальные переменные неиспользуемой функции выделяется место? Или оно выделяется под них только при запуске функции? А где хранятся все те литералы, которые посылаются в аргументы? В памяти той функции, из которой вызывается другая функция, принимающая литералы в качестве аргументов? И еще вопрос: .exe-файл на протяжении всей программы хранится в ОЗУ. Но зачем? Разве из него сразу же не вытянули всю информацию?

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

Мне все говорят, что указатель - это переменная, хранящая адрес. Но ведь компьютер не видит этой разницы, так?

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

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

Если ты ввел в первый раз менее 4-х символов, то компьютер тебя просит ввести еще одну строку, после чего выводится то, что ты ввел в первый раз и то, что ты ввел во второй раз. Однако если ты введешь 4 символа или более, то тебе не предоставится второй возможности ввести строку, вместо этого на экран выведутся 3 первых буквы из того, что ты ввел в этот единственный раз и на пустота, находящаяся строчкой ниже. Можете объяснить это поведение?
Кстати, касательно функции cin.getline: она странно работает - возвращает 3 символа, когда я вторым аргументом послал число 4! Что это такое?

И новый вопрос:
7) Что такое "конец слова" и "выравнивание под конец слова"?

Заранее благодарю.

Это сообщение отредактировал(а) Hagrael - 27.6.2011, 10:21
PM MAIL   Вверх
bsa
Дата 27.6.2011, 10:46 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

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



Цитата(Hagrael @  27.6.2011,  08:57 Найти цитируемый пост)
Код
int x=10;
int* p=&x;
p+=1/4;
Данный код некорректен из-за того, что оператор + может связывать переменную типа type* (любой указатель) только с переменной типа int.
Данный код вполне корректен. Так как p переместится на 0 позиций. Слово "сдвиг" я бы вообще не рекомендовал применять к указателям, так как под сдвигом понимается битовая операция (фактически, сдвиг на 1 влево тоже самое что и умножение на 2, а вправо - целочисленное деление на 2), которая для указателей вообще недопустима (если не понятно почему, почитай про указатели в Ответах на часто задаваемые вопросы).
Цитата(Hagrael @  27.6.2011,  08:57 Найти цитируемый пост)
Код
int x = 10;
int *p = &x;
p = ( int * )( ( char * )p + 1 );

А почему некорректен этот код, мне непонятно.
По мне, он корректен - я не нашел в стандарте указаний на то, что доступ к объектам обязан быть выравнен. С другой стороны, если я такой код увижу, то я буду считать его ошибочным (если, конечно, в комментариях к нему меня не убедят в обратном). Потому что очень мало причин, по которым может это понадобиться. Обычно, все решается несколько иначе.


Цитата(Hagrael @  27.6.2011,  08:57 Найти цитируемый пост)
Код
int x=10;
int* p=&x;
char *p_char=(char*) p;
cout << p_char;

Консоль абсолютно чиста, если не считать статуса завершения программы. Из-за чего?
Начнем с того, что 10 - это код символа '\n' - конец строки. Он на консоли отображается как перемещение курсора в начало следующей строки. Но, данный код может отобразиться тогда и только тогда, когда у тебя система Little Endian (x86, например). А вот на Big Endian системах (ARM, например) на экран будет выведен символ '\0' (так как x будет содержать байты 0, 0, 0, 10 вместо 10, 0, 0, 0).

Цитата(Hagrael @  27.6.2011,  08:57 Найти цитируемый пост)
Массив на самом деле ведь указывает на свою первую ячейку. Однако это не просто указатель, а константа-указатель. 

Здесь и далее у тебя уже начинается путаница.
"константный указатель" и "указатель на константу" в большинстве случаев значит одно и тоже (хотя, должно значить разное): const type * pointer
"указатель-константа" - это уже несколько другое:  type * const pointer
Разница в следующем: в первом случае ты имеешь право менять УКАЗАТЕЛЬ (например, чтобы он указывал в другое место), но не имеешь права менять элемент, на который он указывает, а во втором ты имеешь право менять элемент, но не сам указатель. Разницу видишь?
Массив имеет свой собственный тип - массив (type[n]). Его можно воспринимать, как "указатель-константа". Но в отличие от последнего, sizeof() возвращает не размер указателя, а занимаемое место (в char'ах) массивом.
Цитата(Hagrael @  27.6.2011,  08:57 Найти цитируемый пост)
А под локальные переменные неиспользуемой функции выделяется место? Или оно выделяется под них только при запуске функции?
выделяется только при входе в функцию.
Цитата(Hagrael @  27.6.2011,  08:57 Найти цитируемый пост)
А где хранятся все те литералы, которые посылаются в аргументы? В памяти той функции, из которой вызывается другая функция, принимающая литералы в качестве аргументов?
в коде программы (когда надо вызвать твою функцию, процессор получает команды: запихать 10 в стек, запихать 50 в стек, вызвать подпрограмму по адресу такому-то, ..., откатить стек на <размер аргументов>). 
Цитата(Hagrael @  27.6.2011,  08:57 Найти цитируемый пост)
И еще вопрос: .exe-файл на протяжении всей программы хранится в ОЗУ. Но зачем? Разве из него сразу же не вытянули всю информацию?
Вообще-то, основную часть экзэшника (речь не про отладочную сборку, когда 99,99% его объема уходит под отладочную информацию) занимает программа. А она должна быть в памяти, чтобы процессор мог ее выполнять.

Цитата(Hagrael @  27.6.2011,  08:57 Найти цитируемый пост)
Мне все говорят, что указатель - это переменная, хранящая адрес. Но ведь компьютер не видит этой разницы, так?

Еще раз рекомендую почитать ответы на часто задаваемые вопросы (тема прикреплена).
Цитата(Hagrael @  27.6.2011,  08:57 Найти цитируемый пост)
Если ты ввел в первый раз менее 4-х символов, то компьютер тебя просит ввести еще одну строку, после чего выводится то, что ты ввел в первый раз и то, что ты ввел во второй раз. Однако если ты введешь 4 символа или более, то тебе не предоставится второй возможности ввести строку, вместо этого на экран выведутся 3 первых буквы из того, что ты ввел в этот единственный раз и на пустота, находящаяся строчкой ниже. Можете объяснить это поведение?
http://cplusplus.com/reference/iostream/istream/getline/ почитай, когда включается failbit. Кстати, после возникновения ошибки и до ее снятия читать не получится.

Цитата(Hagrael @  27.6.2011,  08:57 Найти цитируемый пост)
7) Что такое "конец слова" и "выравнивание под конец слова"?
под "словом" понимается так называемое "машинное слово". Т.е. это данные такого размера, работа с которыми для процессора наименее трудоемка. Наиболее популярны 32 и 64 бита (4 и 8 байт, соответственно). У современных процессоров все узлы оптимизированы на работу с такими словами. Но платой за это является снижение производительности при невыравненном доступе: представь, процессор за 1 такт читает 4 байта с адресов, кратных 4. Чтобы прочитать данные, которые лежат только кратно 2, ему придется прочитать 2 раза по 4 байта, а затем лишние байты выкинуть. А теперь представь, что нужно сделать, чтобы записать: прочитать 4 байта, заменить 2 на новые данные, записать, прочитать еще 4 байта, заменить 2 байта на новые, записать.... Минимум 4 такта вместо 1. Именно поэтому все современные компиляторы умеют делать выравнивание по границе машинного слова.

PM   Вверх
Hagrael
Дата 27.6.2011, 12:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Спасибо за ответ.

Цитата(bsa @  27.6.2011,  10:46 Найти цитируемый пост)
Данный код вполне корректен. Так как p переместится на 0 позиций.

На самом деле код был таким:
Код
int x=10;
int* p=&x;
p+=0.25;

Я написал 1/4 для ясности, забыв о том, что если делить один int на другой, то выполнится целочисленное деление smile Думаю, теперь понятно, о чем я говорил:
Цитата(Hagrael @  27.6.2011,  08:57 Найти цитируемый пост)
Данный код некорректен из-за того, что оператор + может связывать переменную типа type* (любой указатель) только с переменной типа int.


Цитата(bsa @  27.6.2011,  10:46 Найти цитируемый пост)
По мне, он корректен - я не нашел в стандарте указаний на то, что доступ к объектам обязан быть выравнен. С другой стороны, если я такой код увижу, то я буду считать его ошибочным (если, конечно, в комментариях к нему меня не убедят в обратном). Потому что очень мало причин, по которым может это понадобиться. Обычно, все решается несколько иначе.

Т. е. технически этот код правилен, однако вы бы подумали, что его автор не знает, что делает, да?

Цитата(bsa @  27.6.2011,  10:46 Найти цитируемый пост)
Начнем с того, что 10 - это код символа '\n' - конец строки. Он на консоли отображается как перемещение курсора в начало следующей строки.

И правда, я просто не заметил переноса строки.

Цитата(bsa @  27.6.2011,  10:46 Найти цитируемый пост)
Но, данный код может отобразиться тогда и только тогда, когда у тебя система Little Endian (x86, например). А вот на Big Endian системах (ARM, например) на экран будет выведен символ '\0' (так как x будет содержать байты 0, 0, 0, 10 вместо 10, 0, 0, 0).

Немного об Endian-ах в Википедии и не до конца понял smile Big Endian и Little Endian - это различные порядки байтов. Но по идее это же одно и то же число, и после операций результат на различных системах должен быть одинаковым относительно каждой системы. Или нет?

Цитата(bsa @  27.6.2011,  10:46 Найти цитируемый пост)
Здесь и далее у тебя уже начинается путаница.
"константный указатель" и "указатель на константу" в большинстве случаев значит одно и тоже (хотя, должно значить разное): const type * pointer
"указатель-константа" - это уже несколько другое:  type * const pointer
Разница в следующем: в первом случае ты имеешь право менять УКАЗАТЕЛЬ (например, чтобы он указывал в другое место), но не имеешь права менять элемент, на который он указывает, а во втором ты имеешь право менять элемент, но не сам указатель. Разницу видишь?

Спасибо, понял.

Цитата(bsa @  27.6.2011,  10:46 Найти цитируемый пост)
Массив имеет свой собственный тип - массив (type[n]). Его можно воспринимать, как "указатель-константа". Но в отличие от последнего, sizeof() возвращает не размер указателя, а занимаемое место (в char'ах) массивом.

Ах вот оно что, значит, массив - это не просто указатель на свою первую ячейку. Он не является константой (т. к. при операции int* p=int_arr; в левой стороне не нужно слово const), однако его нельзя менять. Так?

Цитата(bsa @  27.6.2011,  10:46 Найти цитируемый пост)
Вообще-то, основную часть экзэшника (речь не про отладочную сборку, когда 99,99% его объема уходит под отладочную информацию) занимает программа. А она должна быть в памяти, чтобы процессор мог ее выполнять.

Это понятно, но разве при запуске программы глобальные переменные все функции с их инструкциями не вытаскиваются?

Цитата(bsa @  27.6.2011,  10:46 Найти цитируемый пост)
Чтобы прочитать данные, которые лежат только кратно 2, ему придется прочитать 2 раза по 4 байта, а затем лишние байты выкинуть. А теперь представь, что нужно сделать, чтобы записать: прочитать 4 байта, заменить 2 на новые данные, записать, прочитать еще 4 байта, заменить 2 байта на новые, записать.... Минимум 4 такта вместо 1.

Значит, в ОЗУ все стремиться к подобному порядку:
4 байта | 8 байт | 8 байт | ...
Но зачем 2 раза читать по 4 байта, а затем лишние байты выкинуть? Вы говорите о таких размерах, как 5-7 байт? Тогда, выходит, это устроено так:
    1. Нашел адрес;
    2. Считал 8 байт (2 раза по 4 байта);
    3. Выкинул 2 байта (т. к. нужно было только 6 байт)
А по 8 процессор читать не может, да?

Цитата(bsa @  27.6.2011, 10:46 Найти цитируемый пост)
http://cplusplus.com/reference/iostream/istream/getline/

Я английский знаю не ахти, но я так понял, дело в том, что failbit - это бит, перед которым получение информации прекращается. Я правильно говорю?

Это сообщение отредактировал(а) Hagrael - 27.6.2011, 12:42
PM MAIL   Вверх
bsa
Дата 27.6.2011, 23:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

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



Цитата(Hagrael @  27.6.2011,  12:35 Найти цитируемый пост)
Т. е. технически этот код правилен, однако вы бы подумали, что его автор не знает, что делает, да?

да
Цитата(Hagrael @  27.6.2011,  12:35 Найти цитируемый пост)
Немного об Endian-ах в Википедии и не до конца понял  Big Endian и Little Endian - это различные порядки байтов. Но по идее это же одно и то же число, и после операций результат на различных системах должен быть одинаковым относительно каждой системы. Или нет?
endianess отвечает лишь за порядок байт в ОЗУ. АЛУ работает независимо.
Цитата(Hagrael @  27.6.2011,  12:35 Найти цитируемый пост)
Ах вот оно что, значит, массив - это не просто указатель на свою первую ячейку. Он не является константой (т. к. при операции int* p=int_arr; в левой стороне не нужно слово const), однако его нельзя менять. Так?
Я же объяснил, чем "указатель на константу" отличается от "указателя-константы". const int *p - указатель на константу. int * const p - указатель-константа. Так вот, массив можно воспринимать как указатель-константу (хотя, на самом деле, делать так не стоит).
Цитата(Hagrael @  27.6.2011,  12:35 Найти цитируемый пост)
Это понятно, но разве при запуске программы глобальные переменные все функции с их инструкциями не вытаскиваются?
А что по твоему содержит экзэшник? Он содержит именно программный код, информацию о распределении памяти и необходимых подключаемых библиотеках, а также данные для инициализации глобальных переменных. Правда, возможно, он еще может содержать всякий "мусор" (например, самораспаковывающиеся архивы).

Цитата(Hagrael @  27.6.2011,  12:35 Найти цитируемый пост)
А по 8 процессор читать не может, да?
может. все зависит от архитектуры процессора и контроллера памяти.
Цитата(Hagrael @  27.6.2011,  12:35 Найти цитируемый пост)
Я английский знаю не ахти, но я так понял, дело в том, что failbit - это бит, перед которым получение информации прекращается. Я правильно говорю?
Учи английский, иначе в программировании делать тебе нечего.
failbit устанавливается при ряде ошибок чтения. В частности, getline устанавливает его, когда размер буфера меньше, чем данных в потоке. Для снятия этого бита нужно использовать метод clear().
PM   Вверх
volatile
Дата 28.6.2011, 00:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(bsa @ 27.6.2011,  10:46)
Цитата(Hagrael @  27.6.2011,  08:57 Найти цитируемый пост)
Код
int x = 10;
int *p = &x;
p = ( int * )( ( char * )p + 1 );

А почему некорректен этот код, мне непонятно.
По мне, он корректен - я не нашел в стандарте указаний на то, что доступ к объектам обязан быть выравнен. С другой стороны, если я такой код увижу, то я буду считать его ошибочным (если, конечно, в комментариях к нему меня не убедят в обратном). Потому что очень мало причин, по которым может это понадобиться. Обычно, все решается несколько иначе.


согласен на 200%. Удивительно как точно сформулирована мысль.  smile
именно это я и хотел сказать вчера.
PM MAIL   Вверх
Hagrael
Дата 28.6.2011, 10:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Простите, что так долго не понимаю...

Цитата(bsa @  27.6.2011,  23:32 Найти цитируемый пост)
Массив можно воспринимать как указатель-константу (хотя, на самом деле, делать так не стоит).

Вот именно, как я понимаю, на самом деле, это не указатель-константа! Взять даже ошибки, которые высвечиваются в логе после этого кода:
Код
int a=5;
int* const p=&a;
p++; // 1

int arr[10];
arr++; // 2

Вот, собственно, ошибки:
Код
1. increment of read-only variable `p'
2. non-lvalue in assignment

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

Цитата(bsa @  27.6.2011,  23:32 Найти цитируемый пост)
А что по твоему содержит экзэшник? Он содержит именно программный код, информацию о распределении памяти и необходимых подключаемых библиотеках, а также данные для инициализации глобальных переменных. Правда, возможно, он еще может содержать всякий "мусор" (например, самораспаковывающиеся архивы).

А вы можете сказать примерно, из чего состоит .exe и каким образом с его помощью работает программа (или сказать, что почитать по этому поводу). Я так понимаю, что .exe-файл содержит в себе сборник инструкций, выполняющихся одна за другой. Вот, к примеру, у меня такой код:
Код
#include <iostream>

void func();

int main() {
    cout << "Hello";
    func();
    return 0;
}

void func() {cout << "Good Bye";}

После компиляции, насколько я понимаю, в .exe-файле будут следующие инструкции:
Код
1) в месте 0xB04566 будет находиться функция типа void, аргументов она не берет;
2) в месте 0xB04700 будет находиться главная функция, делает она вот что:
    1) пишет "Hello"
    2) вызывает функцию по адресу 0xB04566 без аргументов;
    3) возвращает 0;
4) функция по адресу 0xB04566 должна делать следующее: {}
5) запускай главную функцию!!!

И, выходит, что компьютер при запуске файла проверит его расширение, увидит, что это .exe, загрузит в память и начнет следовать инструкциям.

А адрес переменным задается строгий, или же при создании переменной, для нее берется свободное место, которое по адресу ближе всех др. свободных мест к нулю?

А библиотечные функции пишутся на C++ или Asm? Логичнее было бы писать их на Asm, и я бы не спросил об этом, если бы не увидел директивы #include, включающей файл с расширением .h.

И еще один вопрос: Если у каждой программы своя виртуальная память, то почему выдает ошибку такая конструкция:
Код
int* p=(int*) 123;
*p=6;

я же изменяю свою переменную, а не переменную чужой программы.

Это сообщение отредактировал(а) Hagrael - 28.6.2011, 10:38
PM MAIL   Вверх
triclosan
Дата 28.6.2011, 12:27 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(Hagrael @  28.6.2011,  10:20 Найти цитируемый пост)
А адрес переменным задается строгий, или же при создании переменной, для нее берется свободное место, которое по адресу ближе всех др. свободных мест к нулю?

Прикладной программе (не по функциональности, а по типу и правам запуска) ОС указывает какой участок памяти она может занять (строго говоря это может быть где угодно в пользовательской памяти) далее мы уже имеем дело со смещениями т.е. ваши 0xB04566 и 0xB04700 могут меняться от запуска к запуску, но переход от main к func будет - "сдвинуться на 0xB04700-0xB04566".

Цитата(Hagrael @  28.6.2011,  10:20 Найти цитируемый пост)
А библиотечные функции пишутся на C++ или Asm? Логичнее было бы писать их на Asm, и я бы не спросил об этом, если бы не увидел директивы #include, включающей файл с расширением .h.

Большинство функции на Си как раз писаны на Asm, при этом файл .h все равно присутствует - он содержит прототипы функций, но это все вообще-то зависит от реализации. 
Си++ые же функции (чистых функций в std::* не так уж и много) - главным образом на Си++, причем большинство - шаблонные, поэтому реализация в виде исходного кода.

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


Шустрый
*


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

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



Цитата(hawk3500 @  26.6.2011,  23:31 Найти цитируемый пост)
Игнорировать арифметику указателей-означает прежде всего не привязываться к базовым типам.
Если у меня есть указатель на тот или иной участок памяти и задача сдвинуть на несколько байт,
то я просто прибавлю к к имеющемуся значению в указателе значение равное сумме того что есть + 3.


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

Теперь приведу выдержку из стандарта по языку С:
"6.5.3.2 Address and indirection operators

#4 If an invalid value has been assigned to the pointer the behavior of the using * operator is undefined"

В комментарии к этому параграфу дается разъяснение, что означает "invalid value"

"Among the invalid values for dereferencing a pointer by the using * operator are a null pointer, an address inoppropriatedly aligned for the type of object pointed to, ..."

Но и это еще не все. В параграфе 6,3.2.3 №7 сказано: "A pointer of an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the pointed to type, the behavior is undefined"

очему возникли такие требования? Дело в том, что на многих млатформах типы данных должны быть соответственно выравнены. Если они не выравнены машинная операция с этими данными приводит к аварийному завершению.

Добавлено через 5 минут и 52 секунды
Цитата(Hagrael @  28.6.2011,  10:20 Найти цитируемый пост)
Вот именно, как я понимаю, на самом деле, это не указатель-константа! Взять даже ошибки, которые высвечиваются в логе после этого кода:

код C++

int a=5;
int* const p=&a;
p++; // 1

int arr[10];
arr++; // 2





Вот, собственно, ошибки:

Без подсветки

1. increment of read-only variable `p'
2. non-lvalue in assignment



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



Я вам уже объяснил! Вы очевидно с первого раза не поняли! Что вы собираетесь менять?!!! Назовите мне ту ячейку памяти, которую вы собираетесь изменить! С именем массива не связана никакая ячейка памяти! То есть нет такой ячейки, где хранится адрес массива, и которую вы могли бы изменить! В памяти хранится сам массив, то есть его элементы. А имя массива - это, можно так сказать, виртуальный адрес массива, то есть нигде этот адрес не хранится! Если бы он где-то хранился, то тогда бы вы его могли изменить. Но в этом случае это уже был бы не сам массив, а указатель на массив, то есть отдельная ячейка памяти, которую вы могли бы менять!

Добавлено через 9 минут и 19 секунд
Цитата(Hagrael @  28.6.2011,  10:20 Найти цитируемый пост)
И еще один вопрос: Если у каждой программы своя виртуальная память, то почему выдает ошибку такая конструкция:

код C++
1:
2:

int* p=(int*) 123;
*p=6;


я же изменяю свою переменную, а не переменную чужой программы.


123 - это не переменная! Это константа препроцессора, которая в результате компиляции заносится непосредственно в машинную команду, которая инициализирует некоторую переменную.

В приведенном ваше примере компилятор никуда не помещает 123, поэтому у этой константы нет адреса!

Я вам уже все об этом в своем первом сообщении сказал. Кстати сказать это было единственное грамотное сообщение в ответ на ваш исходный вопрос, а вы его, непонятно почему, проигнорировали!

Добавлено через 11 минут и 52 секунды
Цитата(bsa @  27.6.2011,  10:46 Найти цитируемый пост)
Цитата(Hagrael @  27.6.2011,  08:57 )
код C++
1:
2:
3:

int x = 10;
int *p = &x;
p = ( int * )( ( char * )p + 1 );





А почему некорректен этот код, мне непонятно.

По мне, он корректен - я не нашел в стандарте указаний на то, что доступ к объектам обязан быть выравнен. С другой стороны, если я такой код увижу, то я буду считать его ошибочным (если, конечно, в комментариях к нему меня не убедят в обратном). Потому что очень мало причин, по которым может это понадобиться. Обычно, все решается несколько иначе.


Цитата(volatile @  28.6.2011,  00:25 Найти цитируемый пост)
Цитата(bsa @ 27.6.2011,  10:46)
Цитата(Hagrael @  27.6.2011,  08:57 )
код C++
1:
2:
3:

int x = 10;
int *p = &x;
p = ( int * )( ( char * )p + 1 );





А почему некорректен этот код, мне непонятно.

По мне, он корректен - я не нашел в стандарте указаний на то, что доступ к объектам обязан быть выравнен. С другой стороны, если я такой код увижу, то я буду считать его ошибочным (если, конечно, в комментариях к нему меня не убедят в обратном). Потому что очень мало причин, по которым может это понадобиться. Обычно, все решается несколько иначе.




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



Я уже привел цитаты из стандарта С в своем сообщении выше, так что можете ознакомиться, почему этот код некорректный.
PM MAIL   Вверх
Закрытая темаСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

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

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

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

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


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

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


 




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


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

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