




В первую очередь результат здесь зависит от порядка байтов процессора. Похоже, у вас процессор с прямым порядком байтов.
Предполагая, что int является 32-битным дополнением до 2, -1 хранится в 4 байтах как 0xFFFFFFFF. Доступ к char влияет только на один байт. Поскольку у вас маленький порядок байтов, в вашем случае это будет наименее значимый байт. Этот байт будет перезаписан со значением 10, 0x0A. В итоге вы получите 0xFFFFFF0A, который является 2-дополнительным представлением -246.
Обратите внимание, что тип char имеет подпись, определяемую реализацией, и поэтому его всегда следует избегать при выполнении битовых / байтовых манипуляций. Вместо этого используйте uint8_t.
Также обратите внимание, что доступ к одному типу данных через указатель другого типа является рискованным и плохо определенным во всех других случаях, кроме случаев, когда конкретно используются символьные типы (uint8_t - символьный тип), поскольку они являются исключением из «правила строгого псевдонима». ".
К сожалению, uint8_t может быть символьным, а может и не быть. Может быть расширенный целочисленный тип, определенный с размером 8 бит (Справка)
@ M.M Нет, это не имеет смысла в реальном мире. Если uint8_t существует, то и 8-битный char существует. Тогда было бы совершенно глупо с точки зрения реализации компилятора сопоставлять uint8_t с чем-либо, кроме unsigned char. И даже если бы его можно было сопоставить с каким-то расширенным нестандартным типом, такой компилятор стал бы бесполезным и вымер, поскольку никто не хочет использовать бесполезный компилятор.
Предположим, что процессор 32-битный.
Первый вопрос: зачем печатать 10 = -246
i = 10; (0x0000 000A)
j = -1; (0xFFFF FFFF) Two's Complement
* q указатель на младшие 8 бит целого числа j, после *q = 10; j становится 0xFFFF FF0A, который является двойным дополнением -246
см. Как компьютеры представляют отрицательные двоичные числа?
Второй вопрос: имеет ли значение размер указателя?
Да, скрытый указатель int на указатель char в этом случае потеряет 24-битные данные.
Здесь p & q - указатель на символ. р = (символ *) & я; Таким образом, p будет указывать на первый байт целого числа var i, размер которого составляет 4 байта. Таким образом, при разыменовании p вы получите 10 (00001010).
д = (символ *) & j; Поскольку j = -1, это наибольшее отрицательное число. Это означает, что j var будет иметь все единицы в 32 битах (11111111 11111111 11111111 11111111)
* д = * р; В этой строке вы копируете самый младший первый байт (00001010) из местоположения i в первый байт местоположения j, потому что оба указателя имеют тип char *. Итак, теперь после копирования значение в местоположении j будет: 11111111 11111111 11111111 00001010, что эквивалентно -246. Вычислите двойное дополнение 11111111 11111111 11111111 00001010. Это даст вам -246.
Я попытался добавить этот цикл, и он решил проблему, но я не знаю, лучший ли это способ сделать это; для (int k = 1; k <= 3; k ++) q [k] = (char) NULL;
Прежде чем искать решение, прежде всего, чего вы хотите достичь в этой программе? для (int k = 1; k <= 3; k ++) q [k] = (char) NULL; Эта строка в идеале устанавливает последние 3 байта в NULL, то есть ноль. Вместо этого вы можете инициализировать j var нулем.
Упражнение здесь - печать 10 = 10 без присвоения новых чисел непосредственно переменным int, а работа только с указателями char.
Лучше использовать memset после оператора printf, чтобы обнулить следующие 3 байта. memset (q + 1, 0, 3);
Does the pointer size matter?
Нет, размер указателя не имеет значения. Имеет значение тип указателя, то есть тип, на который он указывает.
Количество байтов, копируемых при назначении через указатель, зависит от типа указателя. Если тип указателя является указателем на символ, он будет копировать байты sizeof(char). Если тип указателя является указателем int, он скопирует байты sizeof(int).
Why does this code print 10 = -246 instead of 10 = 10?
Это зависит от системы. Поскольку вы получаете этот результат, вы, вероятно, используете систему с прямым порядком байтов, что означает, что данные в памяти сначала сохраняются с LSB (т.е. указатель на переменную указывает на LSB этой переменной).
Итак, что происходит в вашем коде, так это то, что LSB переменной i копируется в LSB переменной j. Поскольку sizeof(int) больше 1, вы не попадете в ситуацию, когда i и j равны. Просто потому, что вы не скопировали все байты i в j.
Предполагая 32-битный int, это может выглядеть так:
Чтобы получить значение i в j, необходимо выполнить приведение, подобное этому:
* (int*)p = * (int*)q
Он преобразует тип вашего указателя, и вы собираетесь «передать» значение i в j.
Или, кроме того, вы можете сделать это без кастинга, используя цикл for: `
for(int k=0; k<sizeof(int);k++){
*(p+k) = *(q+k);
}
` В этом цикле вы собираетесь записывать каждый бит i в каждый байт k по одному байту за раз. Это потому, что int имеет 4-байтовую структуру, а char - 1-байтовую структуру.
intсостоит из нескольких байтов (char- один байт) ... если вы хотите скопировать значение, вам нужно скопировать все байты, а не только 1