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

Как сдвинуть указатель на double на 4 байта? - C++

Восстановить пароль Регистрация
 
Рейтинг: Рейтинг темы: голосов - 15, средняя оценка - 4.67
alsav22
5282 / 4801 / 442
Регистрация: 04.06.2011
Сообщений: 13,587
17.08.2012, 00:38     Как сдвинуть указатель на double на 4 байта? #1
Есть конструкция:
C++
1
2
3
double **p = new double*[2];
for (int i = 0; i < 2; i++)
      p[i] = new double[3];
Если так:
C++
1
++p;
, то указатель p сдвигается на 4 байта (размер указателя на double). Если так:
C++
1
++(p[0]);
, то указатель p[0], сдвигается на 8 байтов (размер переменной типа double). Есть ли возможность сдвинуть указатель p[0] на 4 байта?
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
OhMyGodSoLong
~ Эврика! ~
 Аватар для OhMyGodSoLong
1234 / 983 / 42
Регистрация: 24.07.2012
Сообщений: 2,002
17.08.2012, 00:47     Как сдвинуть указатель на double на 4 байта? #2
(double*)((char*)(*p) + 4)

И никому это не показывайте.
alex_x_x
бжни
 Аватар для alex_x_x
2441 / 1646 / 84
Регистрация: 14.05.2009
Сообщений: 7,163
17.08.2012, 01:01     Как сдвинуть указатель на double на 4 байта? #3
C
1
2
3
4
5
6
7
8
9
10
typedef union u {
  char bytes [sizeof(double)];
  double value;
} u;
 
int main () {
  u u1;
  u1.value = 234.23;
  char* p = &u1.bytes[4]; 
}
alsav22
5282 / 4801 / 442
Регистрация: 04.06.2011
Сообщений: 13,587
17.08.2012, 01:04  [ТС]     Как сдвинуть указатель на double на 4 байта? #4
~OhMyGodSoLong~, благодарю! А через инкремент и int можно что-нибудь подобное придумать?
DaskOFF
 Аватар для DaskOFF
112 / 112 / 9
Регистрация: 02.05.2012
Сообщений: 521
Записей в блоге: 1
17.08.2012, 01:07     Как сдвинуть указатель на double на 4 байта? #5
Цитата Сообщение от alsav22 Посмотреть сообщение
~OhMyGodSoLong~, благодарю! А через инкремент и int можно что-нибудь подобное придумать?
написать класс и в нем перегрузить оператор инкремента
alsav22
5282 / 4801 / 442
Регистрация: 04.06.2011
Сообщений: 13,587
17.08.2012, 01:09  [ТС]     Как сдвинуть указатель на double на 4 байта? #6
alex_x_x, не подходит. Нужно именно к заданной конструкции приделать.

Добавлено через 1 минуту
Цитата Сообщение от DaskOFF Посмотреть сообщение
написать класс и в нем перегрузить оператор инкремента
Не подходит. Нужно по примеру ~OhMyGodSoLong~ сделать.
OhMyGodSoLong
~ Эврика! ~
 Аватар для OhMyGodSoLong
1234 / 983 / 42
Регистрация: 24.07.2012
Сообщений: 2,002
17.08.2012, 01:14     Как сдвинуть указатель на double на 4 байта? #7
Эм, ну можно сделать (int*)(*p)++; тогда *p, он же p[0], сдвинется на размер одного int.

Проблема в том, что у вас int четыре байта, а у кого-то другого он может быть равен двум. Или восьми. Потому-то и говорю, что с этим шаманством надо поаккуратнее.

(Прокатило, таки lvalue.)
alsav22
5282 / 4801 / 442
Регистрация: 04.06.2011
Сообщений: 13,587
17.08.2012, 01:47  [ТС]     Как сдвинуть указатель на double на 4 байта? #8
Дают вот такие примеры к заданию:
Код
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
double ** ConstructArray()
{
double ** p=NULL;
int N, M; // количество строк, элементов в строке
// вводим корректное количество строк
do
{
printf("\nProgram makes an array of N rows.\n");
printf("Please, enter N (1<=N<=10): ");
scanf("%d", &N);
}while(N<1 || N>10);
// выделяем память
p=(double **)malloc( sizeof(double *)*N + sizeof(int));
 
// запоминаем в «минус первом» элемент вектора указателя на строки
((int *)p)[0]=N;
// сдвигаем указатель
((int *)p)++;
// Организуем построчный ввод
for(int i=0; i<N; i++)
{/
/ вводим корректное количество элементов в строке
do
{
printf("\nEnter M - number of elements in %d-th row (1<=M<=10): ", i);
scanf("%d", &M);
}while(M<1 || M>10);
// выделяем память
p[i]=(double *)malloc( M*sizeof(double) + sizeof(int));
// записываем в -1 элемент
((int *)(p[i]))[0]=M;
// сдвигаем указатель
((int *)(p[i]))++;                          // ВОТ это никак не делается!!!
for(int j=0; j<M; j++) // вводим элементы
{
double m;
do
{
printf("\nEnter A[%d][%d] (1<=A[%d][%d]<=10): ", i, j, i, j);
scanf("%lf", &m);
}while(m<1 || m>10);
p[i][j]=m;
}
}return p; // вернуть указатель на сконструированный массив
}
И как это сделать (33 строка)?

Добавлено через 1 минуту
Цитата Сообщение от ~OhMyGodSoLong~ Посмотреть сообщение
Эм, ну можно сделать (double*)((int*)(*p)++), тогда *p, он же p[0], сдвинется на размер одного int, а потом преобразуется обратно в double*. Не, не прокатит по идее. ++ требует lvalue, а приведение возвращает rvalue.

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

Добавлено через 8 минут
Там идея такая (упрощая), что создаётся динамический массив размером на несколько double и один int. В начало массива записывается int, потом указатель сдвигается на величину int и записываются значения double.

Добавлено через 8 минут
Вот так проходит: (double*)((int*)(*p) + 1); Через инкремент - никак.
OhMyGodSoLong
~ Эврика! ~
 Аватар для OhMyGodSoLong
1234 / 983 / 42
Регистрация: 24.07.2012
Сообщений: 2,002
17.08.2012, 01:49     Как сдвинуть указатель на double на 4 байта? #9

Не по теме:

Дично я считаю, что такие "оптимизации" до добра не доводят. Там больше времени потратится на эти преобразования для вытягивания длин и т. п.; я уж молчу про то, что этот массив надо потом как-то удалять (не забыв при этом всё посдвигать обратно).

Но как упражнение на понимание указателей — вполне окей.

В общем это. Небольшой ликбез. Есть lvalue, есть rvalue. lvalue — это нечто, что объективно существует в памяти и сохраняется между вычислениями различных выражений. Например, это любая переменная. rvalue — это нечто, что сущестует только на время вычисления выражения (но оно тоже хранится в памяти, временно).

В выражении x = x + 5; оба икса это lvalue, но само подвыражение (x + 5) — это rvalue. Его значение временно хранится в памяти, пока оно не будет записано в x. Но это не переменная. Инкремент увеличивает значение переменной. Он не может увеличить значение rvalue-величины, так как её формально нет в памяти, она умрёт как только закончится вычисление выражения.

Приведение типов тоже даёт rvalue, поэтому инкремент не может его увеличить.

В общем, к чему это я. Вместо
((int *)(p[i]))++;
напишите
p[i] = (int *)(p[i]) + 1;
Должно помочь.
alex_x_x
бжни
 Аватар для alex_x_x
2441 / 1646 / 84
Регистрация: 14.05.2009
Сообщений: 7,163
17.08.2012, 01:51     Как сдвинуть указатель на double на 4 байта? #10
C
1
2
3
4
5
char* array = malloc (30), *ptr = array;
int v_int = 10;
double v_double = 20.3;
memcpy (ptr, &v_int, sizeof(v_int)); ptr += sizeof(v_int);
memcpy (ptr, &v_double, sizeof(v_double)); ptr += sizeof(v_double);
alsav22
5282 / 4801 / 442
Регистрация: 04.06.2011
Сообщений: 13,587
17.08.2012, 16:39  [ТС]     Как сдвинуть указатель на double на 4 байта? #11
Цитата Сообщение от ~OhMyGodSoLong~ Посмотреть сообщение
я уж молчу про то, что этот массив надо потом как-то удалять (не забыв при этом всё посдвигать обратно).
Удаление там присутствует. Сложность только в правильном сдвиге была.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
void DestructArray(double **p)
{
if(!p) return;// проверка корректности
int N; // количество строк
N=((int *)p)[-1];
for(int i=0; i<N; i++)
{
    p[i] = (double*)(((int *)*(p + i)) - 1); // сдвигаем назад указатели на массивы double.
    free(p[i]); // освобождаем память под массивами double.
}
(int *)p--;  // сдвигаем назад указатель на массив указателей.
free(p); // освобождаем память под массивом указателей.
}
Добавлено через 12 часов 11 минут
~OhMyGodSoLong~, если можно, ещё про это поясните. Компилятор не ругается на такое:
C++
1
2
(char*)p++;
(int*)(p[0])++;
, но и нужного сдвига не происходит. Приведение, как-бы не срабатывает при инкременте (по начальному типу указателя происходит).
OhMyGodSoLong
~ Эврика! ~
 Аватар для OhMyGodSoLong
1234 / 983 / 42
Регистрация: 24.07.2012
Сообщений: 2,002
17.08.2012, 17:09     Как сдвинуть указатель на double на 4 байта? #12
Дело в приоритетах. Приоритет у инкремента и приведения типов одинаковый, но есть ещё понятие ассоциативности. Есть левая ассоциативность и правая. От неё зависит, в каком порядке применяются операторы с одинаковым приоритетом.

Например, сложение левоассоциативно, так что выражение
a + b + c + d + e
трактуется как
((((a + b) + c) + d) + e)
то есть вычисляется слева направо.

А вот присваивание правоассоциативно, так что выражение
a = b = c = d = e = 2
трактуется как
a = (b = (c = (d = (e = 2))))
то есть присваивания идут справа налево (выражение присваивания возвращает присвоенное значение).

К правоассоциативным операторам относятся присваивания (и просто =, и всякие +=) и унарные операторы (среди них, в частности, и инкремент/декремент, а также приведение типов). Все остальные левоассоциативны.

По этой причине
(char*)p++;
трактуется как
(char*)(p++);
То есть происходит инкремент p (на размер адресуемой величины, а не char), возвращается старое значение p и именно оно уже приводится к типу char*.

Если же попробовать написать "правильные скобки":
((char*)p)++;
то оно вообще не скомпилируется из-за рассказанных причин про lvalue: теперь приведение отрабатывает первым, но приведение типов возвращает rvalue-величину, которую инкремент не может увеличить.
alsav22
5282 / 4801 / 442
Регистрация: 04.06.2011
Сообщений: 13,587
17.08.2012, 18:06  [ТС]     Как сдвинуть указатель на double на 4 байта? #13
Цитата Сообщение от ~OhMyGodSoLong~ Посмотреть сообщение
То есть происходит инкремент p (на размер адресуемой величины, а не char), возвращается старое значение p и именно оно уже приводится к типу char*.
возвращается старое значение p
Имеется ввиду, значение p до инкремента? Или, указатель типа double**, со значением увеличенным через ++, приводится к типу char* ?
OhMyGodSoLong
~ Эврика! ~
 Аватар для OhMyGodSoLong
1234 / 983 / 42
Регистрация: 24.07.2012
Сообщений: 2,002
17.08.2012, 19:00     Как сдвинуть указатель на double на 4 байта? #14
Вот что происходит в этой строке: (char*)p++

1. Запоминается текущее значение переменной p типа double**.
2. p увеличивается на размер адресуемой величины (то есть double*, на 4/8 байт в зависимости от разрядности компьютера).
3. Постинкремент возвращает старое (теперь) значение переменной p. Это то значение типа double**, которое было сохранено на первом шаге.
4. Это значение приводится к типу char* и становится значением всего выражения «(char*)p++».
alsav22
5282 / 4801 / 442
Регистрация: 04.06.2011
Сообщений: 13,587
17.08.2012, 19:14  [ТС]     Как сдвинуть указатель на double на 4 байта? #15
Спасибо, понял. А если так: (char*)++p; ? Пожалуйста, если не трудно, то по пунктам, как в предыдущем примере объясните.
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
17.08.2012, 19:45     Как сдвинуть указатель на double на 4 байта?
Еще ссылки по теме:

C++ Как получить ссылку на указатель или указатель на указатель в массиве?
Почему указатель на указатель объявляется как float **A; C++
C++ Как правильно удалять выделенную память под указатель на указатель?

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

Или воспользуйтесь поиском по форуму:
OhMyGodSoLong
~ Эврика! ~
 Аватар для OhMyGodSoLong
1234 / 983 / 42
Регистрация: 24.07.2012
Сообщений: 2,002
17.08.2012, 19:45     Как сдвинуть указатель на double на 4 байта? #16
Правила приоритетов остаются теми же самыми.

1. p увеличивается на размер адресуемой величины (то есть double*, на 4/8 байт в зависимости от разрядности компьютера).
2. Преинкремент возвращает новое? увеличенное значение переменной p (типа double**).
4. Это значение приводится к типу char* и становится значением всего выражения «(char*)++p».
Yandex
Объявления
17.08.2012, 19:45     Как сдвинуть указатель на double на 4 байта?
Ответ Создать тему
Опции темы

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