Изменение значения int с помощью указателя char

Почему этот код выводит 10 = -246 вместо 10 = 10? Имеет ли значение размер указателя?

#include <stdio.h>

int main() {
    int i = 10;
    int j = -1;

    char *p, *q;

    p = (char*) &i;
    q = (char*) &j;

    *q = *p;

    printf("%d = %d\n", i, j);

    return 0;
}

int состоит из нескольких байтов (char - один байт) ... если вы хотите скопировать значение, вам нужно скопировать все байты, а не только 1

M.M 19.03.2018 09:38
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
1 121
5

Ответы 5

В первую очередь результат здесь зависит от порядка байтов процессора. Похоже, у вас процессор с прямым порядком байтов.

Предполагая, что int является 32-битным дополнением до 2, -1 хранится в 4 байтах как 0xFFFFFFFF. Доступ к char влияет только на один байт. Поскольку у вас маленький порядок байтов, в вашем случае это будет наименее значимый байт. Этот байт будет перезаписан со значением 10, 0x0A. В итоге вы получите 0xFFFFFF0A, который является 2-дополнительным представлением -246.


Обратите внимание, что тип char имеет подпись, определяемую реализацией, и поэтому его всегда следует избегать при выполнении битовых / байтовых манипуляций. Вместо этого используйте uint8_t.

Также обратите внимание, что доступ к одному типу данных через указатель другого типа является рискованным и плохо определенным во всех других случаях, кроме случаев, когда конкретно используются символьные типы (uint8_t - символьный тип), поскольку они являются исключением из «правила строгого псевдонима». ".

К сожалению, uint8_t может быть символьным, а может и не быть. Может быть расширенный целочисленный тип, определенный с размером 8 бит (Справка)

M.M 19.03.2018 10:07

@ M.M Нет, это не имеет смысла в реальном мире. Если uint8_t существует, то и 8-битный char существует. Тогда было бы совершенно глупо с точки зрения реализации компилятора сопоставлять uint8_t с чем-либо, кроме unsigned char. И даже если бы его можно было сопоставить с каким-то расширенным нестандартным типом, такой компилятор стал бы бесполезным и вымер, поскольку никто не хочет использовать бесполезный компилятор.

Lundin 19.03.2018 10:19

Предположим, что процессор 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;

user9515151 19.03.2018 10:32

Прежде чем искать решение, прежде всего, чего вы хотите достичь в этой программе? для (int k = 1; k <= 3; k ++) q [k] = (char) NULL; Эта строка в идеале устанавливает последние 3 байта в NULL, то есть ноль. Вместо этого вы можете инициализировать j var нулем.

ravikumar 19.03.2018 10:41

Упражнение здесь - печать 10 = 10 без присвоения новых чисел непосредственно переменным int, а работа только с указателями char.

user9515151 19.03.2018 10:54

Лучше использовать memset после оператора printf, чтобы обнулить следующие 3 байта. memset (q + 1, 0, 3);

ravikumar 19.03.2018 11:36

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-байтовую структуру.

Другие вопросы по теме