C (void**)a+1 , почему интервал (шаг) равен 8? [дубликат]

avatar
GuoHaodong
9 августа 2021 в 07:19
185
3
0

вот так:

void** a = calloc(4,4);
printf("%x,%x",a,a+1));

результат 1516c0,1516c8

почему шаг 8? Я не хочу использовать (char*) для преобразования типа, что мне делать?

Источник
mediocrevegetable1
9 августа 2021 в 07:20
7

Арифметика указателя. sizeof (void *) в вашей системе равно 8, поэтому a + 1 идет на 8 байт вперед, чтобы перейти к следующему void *. Также правильным спецификатором формата для указателей является %p.

Gerhardh
9 августа 2021 в 07:25
1

Добро пожаловать в СО. Вы забыли важные детали. Какой шаг вы ожидали? 4? 1? Почему? "что я должен делать?" Чего вы хотите достичь?

Gerhardh
9 августа 2021 в 07:26
1

Как упоминалось в mediocrevegetable1, для указателей требуется спецификатор формата %p. И, чтобы быть точным, вы должны указать void*, что означает, что вам нужно преобразовать ваш void** в void* для его печати.

Weather Vane
9 августа 2021 в 07:30
0

Как и при индексации массива, добавление 1 перемещает к следующему элементу, а не к следующему байту. a + 1 совпадает с &a[1].

GuoHaodong
9 августа 2021 в 07:35
0

Я хочу, чтобы шаг стал 1

Weather Vane
9 августа 2021 в 07:37
2

Шаг за шагом 1 что? Для чего? Добавление 1 делает шаг за шагом 1. Зачем вам calloc(4,4), когда размер указателя в вашей системе явно 8?

Tom Karzes
9 августа 2021 в 07:48
1

Используйте %p для печати значений указателя, а не %x. В типичной 64-битной системе указатели имеют размер 8 байтов, а целые числа — 4 байта, поэтому в лучшем случае вы будете печатать только половину указателей, а второй указатель может вообще не печататься.

Yunnosch
9 августа 2021 в 07:57
0

«Я хочу, чтобы шаг стал 1». Менее ужасные решения для этого включают «использование (char *) для преобразования типа», чего вы НЕ хотите. Пожалуйста, решите, какое из этих двух требований важнее. Вы также можете сделать один логический шаг назад и описать, чего вы действительно хотите достичь, т. е. подумайте, смотрим ли мы на meta.stackexchange.com/questions/66377/what-is-the-xy-problem.

U. Windl
9 августа 2021 в 08:27
0

a+1 зависит от типа a, а не от адреса a (ни от количества байтов, зарезервированных по этому адресу).

Lundin
9 августа 2021 в 08:39
1

Однако арифметика указателя @phuclv на void** не работает.

Ответы (3)

avatar
0___________
9 августа 2021 в 08:41
0

Я полагаю, что у вас проблемы с пониманием разницы между физическими адресами и ссылками. Арифметика указателей в C выполняется с использованием объектов, на которые ссылаются указатели. Когда вы печатаете указатель, используя "%p" или "%x" (нехорошо, поскольку это неправильный формат), вы получаете физический (байтовый) адрес.

  1. Вы, вероятно, используете void **, потому что арифметика указателей на void * не работает на большинстве компиляторов, которые не имеют специальных расширений, таких как gcc.

  2. Если вы хотите увеличить address на единицу, просто используйте указатель char * вместо void *

  3. Если вы хотите напечатать разницу в арифметике указателя (не физические байты):

int main(void)
{
    void **a;
    printf("%" PRIiPTR "\n", a - (a+1));
}
avatar
G. Emadi
9 августа 2021 в 08:33
0

Как уже упоминалось, размер void * в вашей системе равен 8, поэтому он идет на 8 байт вперед. В любом случае, вам просто нужно что-то изменить, и вам не нужно использовать char * для этого случая. Кроме того, лучше использовать %p для печати указателей:

void** a = calloc(4, sizeof(void *));
printf("%p,%p",a,a+1));
avatar
Lundin
9 августа 2021 в 08:26
2

Печать указателей с %x определена некорректно, вместо этого следует использовать %p.

a+1 выполняет арифметические действия с указателем для типа void**, то есть вы получаете размер в байтах элемента, на который указывает указатель, a void*. Таким образом, в зависимости от того, насколько велик такой указатель в вашей системе, вы получите соответствующее изменение адреса. По-видимому, ваша система имеет 64-битные указатели (вероятно, 64-битная адресная шина), следовательно, 8 байтов.

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

Если вам нужен универсальный указатель типа, который может перебирать общие фрагменты данных байт за байтом, правильный тип для использования — unsigned char*. Если вы не хотите использовать это по каким-то искусственным причинам, то, к сожалению, ничего не получится.

Raildex
9 августа 2021 в 08:31
0

с каких пор арифметика на void* определена?

Lundin
9 августа 2021 в 08:33
1

@Raildex Начиная с первого стандарта C? Это арифметика приводит к указанному void, который не определен. Таким образом, если бы это было void* a, код не скомпилировался бы. Однако void** — это обычный тип указателя без специальных правил.

GuoHaodong
9 августа 2021 в 08:34
0

да, я хотел бы использовать «универсальный тип» в c, потому что размер некоторого типа меньше 8 байт, например: int равен 4 байтам, я хочу перемещать указатель байт за байтом. Я использую (char*) , он работает довольно хорошо. Нужно ли его менять?

Lundin
9 августа 2021 в 08:39
0

@GuoHaodong Это то, что я написал в ответ, да? За исключением того, что это должно быть unsigned char*, если вы хотите получить доступ к данным через этот указатель. char* имеет подписанность, определяемую реализацией, и поэтому его не следует использовать при работе с необработанными двоичными данными.