Мне, как новичку, интересно узнать об архитектуре памяти и о том, как значения сохраняются в памяти, поэтому я попробовал этот код. Ничего больше.
Я пытаюсь вручную ввести значения байтов, чтобы создать определенное двойное значение, используя 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 Спасибо за ваш совет. Я новичок и не знаю, как использовать GDB.
Да... gdb — не самый веселый отладчик! В gdb есть множество более простых возможностей, таких как интеграция IDE и автономные пользовательские интерфейсы.
@Клиффорд, хорошо, я попробую.
Вы используете неправильный спецификатор формата. Чтобы гарантировать, что ввод читается как 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
Да, я тоже нашел ответ после того, как задал вопрос. Но ты ответил на вопрос до того, как я отвечу на него. Поэтому я принял ваш ответ как правильный. Большое вам спасибо.
спасибо, это происходит постоянно; в этом прелесть задавать вопросы, используя минимально воспроизводимый пример. Часто мы находим ответы, задавая вопрос подробно, поскольку для постановки самого вопроса требуется нетривиальное количество усилий.
Да, несоответствие возникает, потому что функция scanf считывает значения как целые числа (int), но я интерпретирую эти целочисленные значения как указатели на символы, что приводит к неправильному представлению в памяти значения double.
Когда я использую scanf("%d", b), он ожидает аргумент int *, но получает char *, что приводит к неправильному хранению и приводит к неправильному конечному значению для double. Вместо этого мне нужно убедиться, что scanf правильно присваивает каждый байт a.
Вот как я исправил код для достижения желаемого результата:
- Считайте входные значения в переменную int.
- Присвойте каждому байту правильную позицию в двойной переменной, используя приведение.
Вот исправленная версия кода:
#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;
}
В этой исправленной версии:
- Scanf("%u", &value) считывает входные данные как целое число без знака.
- Значение преобразуется в 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
Безопаснее (и проще, и быстрее) наблюдать за памятью в отладчике, чем пытаться написать код для проверки представления. Как и здесь, вам необходимо правильно получить этот код проверки (для этого вы также можете использовать отладчик ;-)).