Адресное местоположение и значение в адресном местоположении в C - PullRequest
2 голосов
/ 25 сентября 2019
int PileInts[1024];
char *Pile = (char *)PileInts;

Что делают эти две строки кода?Я думаю, что строка char *Pile = (char *)PileInts; создает символ с именем *Pile, который дает значение адреса в PileInts.Я прав?Могу ли я получить более глубокое объяснение?

Ответы [ 2 ]

3 голосов
/ 25 сентября 2019

Строка

int PileInts[1024];

создает объект массива, состоящий из 1024 целых чисел.Доступ к объекту можно получить с помощью имени переменной PileInts

. Строка

char *Pile = (char *)PileInts;

создает указатель на символ и указывает на первый символмассив объектов.Доступ к объекту указателя char осуществляется с помощью имени переменной Pile.

Указатель char Pile может использоваться для доступа к отдельным байтам PileInts.Пример:

#include <stdio.h>

int main(void) {
    int PileInts[1024];
    char *Pile = (char *)PileInts;

    PileInts[0] = 1;
    PileInts[1] = 2;

    // Print the bytes/chars of the first two ints of PileInts
    for (unsigned i= 0; i < (2 * sizeof PileInts[0]); ++i)
    {
        printf("0x%02x\n", *Pile); // Print what Pile points to
        ++Pile;                    // Increment Pile so it points to the next byte/char
    }
    return 0;
}

Возможные выходные данные:

0x01
0x00
0x00
0x00
0x02
0x00
0x00
0x00

Примечание. Выходные данные могут отличаться от системы к системе из-за разного порядка байтов и / или целочисленного размера.

Есливы хотите увидеть значение Pile, то есть адрес, на который он указывает, вы можете изменить код следующим образом:

#include <stdio.h>

int main(void) {
    int PileInts[1024];
    char *Pile = (char *)PileInts;

    PileInts[0] = 1;
    PileInts[1] = 2;

    for (unsigned i= 0; i < (2 * sizeof PileInts[0]); ++i)
    {
        printf("Pile points to addresss %p where the value 0x%02x is stored\n", 
               (void*)Pile, *Pile);
        ++Pile;
    }
    return 0;
}

Возможный вывод:

Pile points to addresss 0x7ffe5860b8e0 where the value 0x01 is stored
Pile points to addresss 0x7ffe5860b8e1 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e2 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e3 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e4 where the value 0x02 is stored
Pile points to addresss 0x7ffe5860b8e5 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e6 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e7 where the value 0x00 is stored
1 голос
/ 25 сентября 2019

строка создает символ с именем *Pile, который задает значение адреса в PileInts

Нет.Он создает указатель на символ char* с именем Pile, который указывает на самый младший байт в первом int в массиве int -

Могу ли я получить более глубокое объяснение?

Код дает указатель, который можно использовать для доступа к отдельным байтам первого int в массиве, а оттуда к отдельным байтам следующих смежных int до концамассив.

Это возможно, потому что когда имя массива используется в выражении, оно «разлагается» на указатель на первый элемент этого массива.Делая PileInts, при использовании в массиве, эквивалентно типу int*.

Преобразование из int* в char* допустимо, но подозрительно C. Некоторые вещи, о которых следует знать:

  • Прежде всего, uint8_t* следует использовать всякий раз, когда кто-то пытается получить доступ к необработанным значениям данных.Тип char очень проблематичен, поскольку имеет подпись, определяемую реализацией - его не следует использовать ни для чего другого, кроме символов и строк. Является ли символ подписанным или неподписанным по умолчанию?

  • Этот указатель будет указывать на самый низкий адрес первого int.Каким байтам это соответствует, зависит от процессора, а именно от endianess процессора.Использование операторов побитового сдвига вместо указателей приведет к удалению этой зависимости процессора, поэтому в зависимости от того, что вы хотите сделать с этим указателем, он может или не может быть подходящим инструментом для этой задачи.

Что касается самого преобразования указателя, то оно хорошо в соответствии с C17 6.3.2.3/7:

Указатель на тип объекта может быть преобразован в указатель на другой тип объекта.Если результирующий указатель неправильно выровнен для ссылочного типа, поведение не определено.В противном случае при обратном преобразовании результат сравнивается равным исходному указателю.

Это означает, что что-то вроде (int*) ((char*)PileInts + 1) будет неопределенной ошибкой в ​​поведении, потому что int* будет смещен.

Тот же параграф в стандарте продолжается:

Когда указатель на объект преобразуется в указатель на тип символа, результат указывает на младший адресуемый байт объекта.Последовательные приращения результата, вплоть до размера объекта, дают указатели на оставшиеся байты объекта.

Это правило, позволяющее нам перебирать любой тип данных с помощью символьного указателя(или предпочтительно эквивалент uint8_t*).То же самое не верно для любого другого типа указателя, мы могли бы, например, не использовать short*, чтобы сделать то же самое, думая, что мы перебираем 16-битные слова.

Существует также другое правило, запрещающее нам использовать любой другой тип указателя, а именно правило, регулирующее внутреннюю систему типов компилятора и то, как разные указатели могут быть псевдонимами друг друга.Неформально известный как строгое правило псевдонимов .Это правило также имеет исключение только для типов символов.То есть мы не можем сделать это:

int PileInts[1024];
char *Pile = (char *)PileInts;
short* sptr = (short*)Pile; // very questionable cast but not a bug yet
printf("%h", *sptr); // bug, data is accessed as wrong type, strict aliasing violation
...