Для (* ptr) [], почему printf (% p, (void *) ptr + 1) работает, но не printf (% p, ptr + 1)?

Я просто не могу понять ошибку в следующей программе. Я назначил адрес массива размером 5 указателю ptr типа (*)[]. Несоответствия типов нет и это нормально, но потом я хочу напечатать значение ptr + 1. Как я и ожидал, он показывает ошибку о неуказанных границах. Но мне просто не хватает того, как тот же ptr+1 работает нормально когда я привел его к типу void*.

Кастинг - это нормально, но прежде, чем дойти до этого, как программа может вычислить ptr+1, если она просто не знает границ? Как он узнает, двигаться ли вперед на 5 элементов, на 8 элементов вперед или на какие-либо элементы вперед? Почему (void*)ptr+1 не показывает ту же ошибку, что и ptr+1?

Чтобы лучше выделить все это, я также использовал указатель ctr, который явно объявлен как имеющий тип (*)[5] вместо (*)[]. Пожалуйста, назовите мне технические причины этого. Спасибо.

#include<stdio.h>

int main(void)
{
 int ar1[5]={1,5,3,8,9};
    int (*ptr)[]=&ar1,(*ctr)[5]=&ar1;
    printf("%p\n",ptr+1);      //ERROR about unspecified bounds
    printf("%p\n",(void*)ptr+1);    //It's ok
    printf("%p\n",ctr+1);       //It's ok
}

PSST !! Последние два правильных printf() не дают одинакового значения. Если я закомментирую неправильную строку printf("%p\n",ptr+1);, это будет результат.

0023FF25

0023FF38

PSST!! Я проверил это снова, в ptr+1 части (void*)ptr+1, 1 просто добавляется к числовому значению ptr. Что происходит?


person Thokchom    schedule 02.05.2013    source источник


Ответы (2)


Пожалуйста, объясните мне технические причины этого.

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

Пример: добавление единицы к указателю int увеличит его на четыре, потому что int имеет длину 4 байта и затем будет указывать на следующий int.

В вашем примере с ptr вы никогда не указывали размер. Невозможно перейти к следующему элементу, потому что размер этого элемента неизвестен. Как только вы указали размер, указав на массив ровно из 5 элементов, он снова заработал.

При приведении ptr к void снова стало легко перейти к следующему элементу. Компилятор просто добавил один байт.

В вашем пробеге:

ptr is 0023FF24

((void*)ptr) + 1 равно 0023FF25 (базовый адрес плюс 1 байт для одного элемента "void")

ctr равно 0023FF24 (то же, что и ptr)

ctr + 1 равно 0023FF38 (базовый адрес плюс 20 (5 * sizeof(int)) байтов для одного элемента int (*)[5])

person nvoigt    schedule 02.05.2013
comment
Итак, в 32-битной системе, если мы добавляем 1 к целочисленному указателю, он перескакивает на 4 позиции, но пустой указатель перескакивает на 1 место / пока при увеличении? - person Thokchom; 02.05.2013
comment
Правильный. Любая арифметика указателя умножается на sizeof(type pointed to), причем void является особенным, поскольку он не имеет размера, но рассматривается как размер 1. Таким образом, int* x; x += 2; увеличивает значение x на 8 (в два раза больше размера int). void* x; x += 2; увеличит значение x на 2 (два раза на 1). - person nvoigt; 02.05.2013
comment
@Thokchom Арифметика указателей на void*s не допускается стандартом. Некоторые компиляторы, такие как gcc, допускают это как расширение. Я не знаю компилятора, который обрабатывает void* отличное от char* в арифметике указателя, если он вообще разрешает арифметику для void*s, но вам не следует полагаться на это. - person Daniel Fischer; 02.05.2013

согласно вашему коду

int (*ptr)[]

ptr - это указатель на массив НЕИЗВЕСТНОГО размера. Поэтому до сих пор компилятор не имел представления о размере, на который указывает ptr. Когда вы пытаетесь сделать приращение, компилятор все еще не знает, на сколько приращать. Приращение указателя будет добавлением sizeof (заостренного элемента). Таким образом, вы получаете ошибку «неопределенная граница».

Теперь что касается ваших оставшихся двух вопросов, для лучшего понимания позвольте мне перефразировать ваши оба вопроса в один - "ПОЧЕМУ (VOID *) ВЕДЕТ КАК (CHAR *)" ?? (как это было :)?) Ваш ответ -

В стандарте C (проект N1256): 6.2.5-27:

A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.

person Dayal rai    schedule 02.05.2013
comment
Не поэтому компилятор обрабатывает указатель арифметику на void* так же, как char*. В большинстве систем int* и char* have the same representation and alignment requirements, but arithmetic on them behaves differently. Arithmetic on void * `являются расширениями, специфичными для gcc; в стандарте C это нарушение ограничения. - person Keith Thompson; 06.08.2013