Почему введенное вручную двойное значение не соответствует ожидаемому значению в C?

Вопрос:

Мне, как новичку, интересно узнать об архитектуре памяти и о том, как значения сохраняются в памяти, поэтому я попробовал этот код. Ничего больше.

Я пытаюсь вручную ввести значения байтов, чтобы создать определенное двойное значение, используя scanf в C. Однако полученное двойное значение не соответствует моим ожиданиям. Вот что я делаю:

Сначала рабочий пример:

Эта программа печатает каждое значение байта из 8 байтов, выделенных для двойного значения.

#include <stdio.h>

int main() {
    double a = 18446744073709551616.0;
    printf("value=%lf\n", a);
    int i;
    char *b = (char*)&a;
    for (i = 0; i < 8; i++) {
       printf("%d byte (%p)value: <%d>\n", i + 1, b, *b);
       b++;
    }
    return 0;
}

Выход:

value=18446744073709551616.000000
1 byte (0x7ffda3f173f8)value: <0>
2 byte (0x7ffda3f173f9)value: <0>
3 byte (0x7ffda3f173fa)value: <0>
4 byte (0x7ffda3f173fb)value: <0>
5 byte (0x7ffda3f173fc)value: <0>
6 byte (0x7ffda3f173fd)value: <0>
7 byte (0x7ffda3f173fe)value: <-16>
8 byte (0x7ffda3f173ff)value: <67>

Это имеет смысл, если вы преобразуете все эти значения в двоичные и читаете их в формате с прямым порядком байтов, что дает мне стандартное представление IEEE-754.

Преобразование 18446744073709551616.000000:

1st byte value (0) which in binary -> (00000000)
2nd byte value (0) which in binary -> (00000000)
3rd byte value (0) which in binary -> (00000000)
4th byte value (0) which in binary -> (00000000)
5th byte value (0) which in binary -> (00000000)
6th byte value (0) which in binary -> (00000000)
7th byte value (-16) which in binary -> (11110000)
8th byte value (67) which in binary -> (01000011)

Теперь проблемный код:

Я попытался ввести это значение вручную с помощью scanf со следующим кодом:

#include <stdio.h>

int main() {
    double a;
    int i;
    char *b = (char*)&a;
    for (i = 0; i < 8; i++) {
       printf("Enter %d byte value: ", i + 1);
       scanf("%d", b);
       b++;
    }
    printf("Final value: %lf\n", a);
    return 0;
}

Вход:

1 byte value: 0
2 byte value: 0
3 byte value: 0
4 byte value: 0
5 byte value: 0
6 byte value: 0
7 byte value: -16
8 byte value: 67

Выход:

Окончательное значение: 0,000000.

Вопрос:

Почему этот код не выдает то же двойное значение, что и первый пример? Как я могу правильно ввести значения байтов, чтобы получить одно и то же двойное значение?

Безопаснее (и проще, и быстрее) наблюдать за памятью в отладчике, чем пытаться написать код для проверки представления. Как и здесь, вам необходимо правильно получить этот код проверки (для этого вы также можете использовать отладчик ;-)).

Clifford 03.07.2024 08:19

@Clifford Спасибо за ваш совет. Я новичок и не знаю, как использовать GDB.

Nalan PandiKumar 03.07.2024 09:32

Да... gdb — не самый веселый отладчик! В gdb есть множество более простых возможностей, таких как интеграция IDE и автономные пользовательские интерфейсы.

Clifford 03.07.2024 10:17

@Клиффорд, хорошо, я попробую.

Nalan PandiKumar 03.07.2024 10:47
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
119
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы используете неправильный спецификатор формата. Чтобы гарантировать, что ввод читается как char, вам нужен %hhd. В частности, ваша строка scanf("%d", b); сканирует целое число и сохраняет его в переменной типа char, что делает поведение неопределенным. Напомним, что int и char имеют разные байты. char — 1 байт, а int обычно 4 (в зависимости от компилятора).

Для полноты код и выходные данные показаны ниже:

#include <stdio.h>

int main() {
    double a;
    int i;
    char *b = (char*)&a;
    for (i = 0; i < 8; i++) {
       printf("Enter %d byte value: ", i + 1);
       scanf("%hhd", b);
       b++;
    }
    printf("Final value: %lf\n", a);
    return 0;
}

Выход:

/tmp/E9Olq2nOoS.o
Enter 1 byte value: 0
Enter 2 byte value: 0
Enter 3 byte value: 0
Enter 4 byte value: 0
Enter 5 byte value: 0
Enter 6 byte value: 0
Enter 7 byte value: -16
Enter 8 byte value: 67
Final value: 18446744073709551616.000000

Да, я тоже нашел ответ после того, как задал вопрос. Но ты ответил на вопрос до того, как я отвечу на него. Поэтому я принял ваш ответ как правильный. Большое вам спасибо.

Nalan PandiKumar 03.07.2024 07:15

спасибо, это происходит постоянно; в этом прелесть задавать вопросы, используя минимально воспроизводимый пример. Часто мы находим ответы, задавая вопрос подробно, поскольку для постановки самого вопроса требуется нетривиальное количество усилий.

lifezbeautiful 03.07.2024 07:33

Да, несоответствие возникает, потому что функция scanf считывает значения как целые числа (int), но я интерпретирую эти целочисленные значения как указатели на символы, что приводит к неправильному представлению в памяти значения double.

Когда я использую scanf("%d", b), он ожидает аргумент int *, но получает char *, что приводит к неправильному хранению и приводит к неправильному конечному значению для double. Вместо этого мне нужно убедиться, что scanf правильно присваивает каждый байт a.

Вот как я исправил код для достижения желаемого результата:

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

Вот исправленная версия кода:

#include <stdio.h>

int main() {
    double a;
    int i;
    int value;
    char *b = (char *)&a;
    
    for (i = 0; i < 8; i++) {
       printf("Enter %d byte value(%p): ", i + 1,&b[i]);
       scanf("%u", &value);  // Read input as an signed int
       b[i] = (char)value;   // Cast and assign to the correct byte
    }

    printf("Final value: %lf\n", a);
    return 0;
}

В этой исправленной версии:

  1. Scanf("%u", &value) считывает входные данные как целое число без знака.
  2. Значение преобразуется в char и присваивается правильному байту двойной переменной a.

Это должно дать правильное окончательное значение двойной переменной на основе введенных вручную байтов.

Выход:

Enter 1 byte value(0x7ffd10067208): 0
Enter 2 byte value(0x7ffd10067209): 0
Enter 3 byte value(0x7ffd1006720a): 0
Enter 4 byte value(0x7ffd1006720b): 0
Enter 5 byte value(0x7ffd1006720c): 0
Enter 6 byte value(0x7ffd1006720d): 0
Enter 7 byte value(0x7ffd1006720e): -16
Enter 8 byte value(0x7ffd1006720f): 67
Final value: 18446744073709551616.000000

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