Что на самом деле происходит, когда указатель на целое число приводится к указателю на символ? - PullRequest
6 голосов
/ 20 сентября 2010
int i=40;
char *p;
p=(char *)&i;//What actually happens here?
printf("%d",*p);

Каким будет выход?Пожалуйста, помогите!

Ответы [ 5 ]

7 голосов
/ 20 сентября 2010
p=(char *)&i;//What actually happens here?

Он принимает адрес i и преобразует его в указатель char.Таким образом, значение *p теперь является первым байтом i.Что это значение, зависит от платформы.

5 голосов
/ 20 сентября 2010

Давайте начнем с рассмотрения того, как содержимое i и p будет размещено в памяти (при условии порядка старшего числа):

Item               Address               0x00  0x01  0x02  0x03
----               -------               ----------------------
   i               0x08000000            0x00  0x00  0x00  0x28
   p               0x08000004            0x??  0x??  0x??  0x??

Поскольку p объявляется какавтоматическая переменная, она ни к чему не инициализирована и содержит случайную битовую комбинацию, представленную 0x??.

В строке

p = (char *)&i;

выражение &i вычисляется по адресу i или 0x08000000, а его тип равен pointer to int или int *.Приведение преобразует тип из int * в char *, а результат присваивается p.

Вот как все выглядит в памяти после назначения:

Item               Address               0x00  0x01  0x02  0x03
----               -------               ----------------------
   i               0x08000000            0x00  0x00  0x00  0x28
   p               0x08000004            0x08  0x00  0x00  0x00

Таким образом, значение p теперь является адресом из i.В строке

printf("%d", *p);

тип выражения *p равен char, а его значением является то, что хранится в адресе 0x08000000, который в данном конкретном случае равен 0. Поскольку printf являетсяФункция variadic, значение *p повышается от типа char до типа int.

Так что для этого конкретного случая выходной сигнал равен "0".Если бы порядок был прямым порядком байтов, карта выглядела бы как

Item               Address               0x03  0x02  0x01  0x00
----               -------               ----------------------
   i               0x08000000            0x00  0x00  0x00  0x28
   p               0x08000004            0x08  0x00  0x00  0x00

, а результат был бы "40".

Обратите внимание, что весь этот пример предполагает, что целочисленные и символьные указатели имеют одинаковый размер и расположение;это не гарантируется, чтобы быть верным везде (см. Online C Standard (черновик n1256) , раздел 6.2.5, параграф 27), поэтому вы не можете полагаться на эту работу везде, как вы ожидаете (предполагая, чтоПравильно думать, что int и char не являются совместимыми типами, как определено стандартом, но я могу ошибаться в этом).Типа забивать вообще не безопасно.

2 голосов
/ 20 сентября 2010

здесь вы

int i = 40; // выделяете память для целого числа i и присваиваете ему значение 40

char *p = (char*)&i; 

так что здесь вы определяете переменную-указатель и присваиваете ей адресиз i после приведения его к char*

предположим, что i выделено на 1021 address, поэтому p будет иметь этот адрес с пределом в 1 байт, поэтому он должен содержать first 8 bit from the representation of 40;

поскольку 40 был покрыт первыми 8-битными 2-байтовыми символами, он будет содержать символьный эквивалент 40, но когда вы печатаете его с помощью %d, это shopuld print 40;

1 голос
/ 20 сентября 2010

Это зависит. В Windows вывод будет 40, но это просто из-за большого количества совпадений:

Прежде всего, printf не (не может) проверять тип своих аргументов, поэтому, поскольку он видит% d в строке формата, он предполагает, что данный аргумент является целым числом. Хотя * p является только символом, результат повышается до целого (как и для каждого аргумента, который не указан в прототипе функции).

Во-вторых, p будет указывать на память, занятую переменной i, но, поскольку это указатель на символ, он будет брать только один байт из памяти i. Так как Windows / Intel использует первое соглашение по наименьшему значению байтов, 40 будет храниться как байтовый шаблон "40 0 0 0", поэтому, поскольку * p берет первый байт (символ), результат будет 40. Если бы я значение 256 или больше, результат будет неверным.

0 голосов
/ 02 ноября 2018

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

$ cat a.c
#include <stdio.h>

int main(){
    int a;
    char *x;
    x = (char *) &a;
    a=512;
    x[0]=1;
    x[1]=2;
    printf("%d\n",a);
    return 0;
}

Скомпилируйте и запустите:

$ gcc a.c && ./a.out
513

Почему это 513?Мы можем использовать gdb, чтобы увидеть основную причину.

$ gcc a.c -g && gdb ./a.out
(gdb) list
1       #include <stdio.h>
2
3        int main(){
4            int a;
5            char *x;
6            x = (char *) &a;
7            a=512;
8            x[0]=1;
9            x[1]=2;
10           printf("%d\n",a);

установить точку останова в строке 8 переменного тока и запустить

(gdb) b a.c:8
Breakpoint 1 at 0x40113d: file a.c, line 8.
(gdb) run

после остановки программы в точке останова, вывести переменную aадрес памяти.

(gdb) p &a
$2 = (int *) 0x7fffffffd9d4
(gdb) p x
$3 = 0x7fffffffd9d4 ""

адрес памяти переменной a равен 0x7fffffffd9d4, а значение переменной x такое же.

перед тем, как показать содержимое памяти, давайте разберемся, как 512 в шестнадцатеричном формате это:

00 00 02 00

, а x86 имеет младший порядок байтов, поэтому в памяти должно быть:

[higher address] 00 02 00 00 [lower address]

Давайте покажем реальную память, такую ​​же, как мы думали.

(gdb) x/4xb 0x7fffffffd9d4
0x7fffffffd9d4: 0x00    0x02    0x00    0x00

затем, покажите адрес памяти x [0] и x [1] и преобразуйте содержимое памяти в реальное значение, не должно быть трудно понять, почему распечатывается 513.

(gdb) p &x[0]
$4 = 0x7fffffffd9d4 ""
(gdb) p &x[1]
$5 = 0x7fffffffd9d5 "\002"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...