Страницы

Поиск по вопросам

пятница, 26 октября 2018 г.

Взятие адреса у массива

Всем известно что имя массива это адрес первого элемента в массиве, но все было бы так просто если бы не взятие адреса у имени массива.
Есть такое объявление массива: char array[] = "String";
Если мы пишем: array или &array[0] - мы получаем адрес первого элемента в массиве первый вариант - неявный, второй - явный.
Я подумал хм, а что если я напишу &array - на первый взгляд ничего странного адрес тот же что и у array или &array[0], но если чучуть закопаться по-глубже, то есть &array+1 - мы увидим что адрес увеличится на длину строки (для меня это странно, никогда этого не видел, кроме как в параметрах, когда аргументом был указатель на массив элементов определенного количества, прям как сейчас)
Интересно есть ли альтернатива такому поведению (&array)? Я пока что додумался до такого способа: (char (*)[sizeof(array)])array - но правильно ли это?
Помимо этого интересно следущее поведение: Если мы пишем *array мы получим элемент массива ровно как и array[0], но если мы напишем *(&array) - мы не получим элемент массива, мы получим тот же самый адрес, который указывает на первый элемент, но с той лишь разницей что его адресаная арифметика упала с целого массива до одного символа.
Можно ли сказать что в данном случае это разименование указателя до уровня указателя на символ (такой своеобразный даунгрейд указателя)?
И что вообще происходит с массивом когда мы пишем *(&array) на ваш взгляд?


Ответ

Если у вас есть указатель вида
T *p;
то 1) для арифметики указателей используется значение sizeof( T ), и 2) разыменование указателя дает lvalue , на который указывает указатель.
Например, если имеется следующий фрагмент кода
int a[] = { 1, 2 }; int *p = &a[0];
то значение указателя после выполнения выражения ++p будет равно
значение указателя перед инкрементом плюс значение sizeof( int )
В этом случае в результате указатель будет указывать на элемент a[1]
Если имеется объявление следующего массива
char array[] = "String";
то оно эквивалентно следующему объявлению массива
char array[7] = "String";
Если ввести typedef объявление
typedef char T[7];
то объявление вышеприведенного массива можно записать также как
T array = "String";
Поэтому чтобы объявить указатель на этот объект, следует записать
T *p = &array;
И, как показано выше, для арифметики указателей для указателя p используется выражение sizeof( T ) , которое равно sizeof( char[7] )
Разыменовывая этот указатель, вы получите lvalue объекта типа char[7]
Это легко проверить, выполнив предложение
printf( "%zu
", sizeof( *p ) );
результатом которого будет значение 7, то есть получена длина массива типа char[7]
Однако когда в качестве объекта, полученного после разыменования указателя, имеется массив, то он в выражения за редким исключением преобразуется к указателю на свой первый элемент.
Из стандарта C (6.3.2.1 Lvalues, arrays, and function designators)
3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.
Поэтому для вышеприведенного примера если выражение *p, не используется в качестве операнда оператора sizeof или оператора &, то происходит неявное преобразование полученного массива в указатель на свой первый элемент. Поэтому, например, выражение **p даст объект типа char со значением 'S'

Комментариев нет:

Отправить комментарий