Как хранятся целые числа в памяти? - PullRequest
4 голосов
/ 05 апреля 2019

Я думал, что целые числа в C сначала сохраняются с самым старшим битом, например, число 5 будет 0...0101. Я думал, что смогу манипулировать определенными битами, заставив C позволить мне притвориться, что конкретный адрес памяти был int и добавить туда биты, как будто они были int.

Я попытался установить 0 битов 0 в памяти, затем попытался добавить 255 к различным адресам памяти, и, похоже, работает так, как если бы младшая цифра сохранялась в памяти до самой старшей цифры, потому что когда я добавляла 1 на мой адрес памяти и изменил бит там я получил большее число вместо меньшего. Если старший значащий бит был сохранен ранее в памяти, добавление 255 к адресу памяти на 1 байт выше не должно влиять на число по первоначальному адресу, поскольку последние 8 бит являются началом следующего int. Мне было интересно, правильно ли я интерпретировал это, и что целые числа были сохранены с младшим битом вначале.

#include "stdio.h"
#include "string.h"
#include "stdlib.h"

int main() {
    int *x = malloc(8); //getting 4 memory addresses
    int *y = malloc(8);
    int *z = malloc(8);
    int *a = malloc(8);

    x[0] = 0; //setting 64 bits past memory addresses to 0s
    x[1] = 0;
    y[0] = 0;
    y[1] = 0;
    z[0] = 0;
    z[1] = 0;
    a[0] = 0;
    a[1] = 0;

    *((int*)((int)x)) = 255; //adding to x's memory address
    *((int*)((int)y + 1)) = 255; //adding 1 byte over from y
    *((int*)((int)z + 2)) = 255; //adding 2 bytes over from z
    *((int*)((int)a + 3)) = 255; //adding 3 bytes over from a

    printf("%d\n", sizeof(int));
    printf("%d,%d\n", x[0], x[1]);
    printf("%d,%d\n", y[0], y[1]);
    printf("%d,%d\n", z[0], z[1]);
    printf("%d,%d\n", a[0], a[1]);

    printf("%d\n", x);
    printf("%d\n", &x[1]);
    return 0;
}

Ожидаемый результат:

4
255,0
0,-16777216
0,16711680
0,65280
12784560
12784564

Фактический объем производства:

4
255,0
65280,0
16711680,0
-16777216,0
12784560
12784564

Ответы [ 2 ]

5 голосов
/ 05 апреля 2019

Я думал, что целые числа в c были сохранены с самым старшим битом, например, число 5 будет 0 ... 0101

Нет, это зависит от вашей платформы& toolchain, не на C.

Схема, которую вы описываете (почти), называется big-endian .

В настоящее время многие обычные ПК little-endian , так что наоборот (младший байт первый).Это может иметь место для вас.

Обратите внимание, что порядковый номер говорит о байтах , а не битах.

Было бы лучше не пытаться манипулировать такими данными.Работайте с языком, используя логические операции, которые не заботятся о порядке байтов.

0 голосов
/ 05 апреля 2019

В вашем коде есть некоторые проблемы:

  • кажется, что существует некоторая путаница между бит и байт .Компьютерная память адресуется в байтах, обычно в современных архитектурах она содержит 8 бит.

  • Вы не должны приводить указатель к int, int может не иметь достаточного диапазона для размещения указателя.значение.преобразуйте указатели в unsigned char * для исправления отдельных байтов, но имейте в виду, что это может не дать ожидаемых результатов из-за правила наложения имен:

    ((unsigned char *)x)[0] = 255; //adding to x's memory address
    ((unsigned char *)y)[1] = 255; //adding 1 byte over from y
    ((unsigned char *)z)[2] = 255; //adding 2 bytes over from z
    ((unsigned char *)a)[3] = 255; //adding 3 bytes over from a
    
  • Аналогично, вы должны использовать %zu для печати size_t или преобразования size_t в int.

  • указатели должны быть отлиты как (void*) и напечатаны с %p.
  • эффект ваших изменений будет более очевидным, если вы напечатаете значения int в шестнадцатеричном формате.

Вот модифицированная версия:

#include <stdio.h>
#include <stdlib.h>

int main() {
    // getting 4 memory addresses, each with enough space for 2 int, initialized to 0
    int *x = calloc(2, sizeof(int));
    int *y = calloc(2, sizeof(int));
    int *z = calloc(2, sizeof(int));
    int *a = calloc(2, sizeof(int));

    ((unsigned char *)x)[0] = 255; //adding to x's memory address
    ((unsigned char *)y)[1] = 255; //adding 1 byte over from y
    ((unsigned char *)z)[2] = 255; //adding 2 bytes over from z
    ((unsigned char *)a)[3] = 255; //adding 3 bytes over from a

    printf("%d\n", (int)sizeof(int));
    printf("%08x,%08x -- %d,%d\n", x[0], x[1], x[0], x[1]);
    printf("%08x,%08x -- %d,%d\n", y[0], y[1], y[0], y[1]);
    printf("%08x,%08x -- %d,%d\n", z[0], z[1], z[0], z[1]);
    printf("%08x,%08x -- %d,%d\n", a[0], a[1], a[0], a[1]);

    printf("%p\n", (void *)x);
    printf("%p\n", (void *)&x[1]);
    return 0;
}

Вывод:

4
000000ff,00000000 -- 255,0
0000ff00,00000000 -- 65280,0
00ff0000,00000000 -- 16711680,0
ff000000,00000000 -- -16777216,0
0x7fd42ec02630
0x7fd42ec02634

Из приведенного выше вывода видно, что:

  • тип int имеет 4 байта
  • указатели используют 8 байтов (мое окружение 64-битное, в отличие от вашего)
  • int хранятся сначала с младшим значащим байтом, таким же, как ваш, который называется архитектура с прямым порядком байтов .

Вы ожидали обратного, архитектура с прямым порядком байтов , что довольно редко встречается на современных настольных и переносных компьютерах, но очень распространено на embedАрхитектура ded и мобильные телефоны.

Оба подхода имеют свои преимущества и недостатки.C поддерживает оба прозрачно, поэтому большинство программистов не знают об этих тонкостях, но понимание этих деталей реализации очень полезно в некоторых ситуациях:

  • системное программирование, низкоуровневое программирование, разработка драйверов устройств,
  • обработка изображений
  • чтение и запись двоичных файлов и потоков,
  • обработка сетевой передачи двоичных данных, особенно на и с различных устройств.
  • взаимодействие с другими языками программирования, написание библиотек и т. д.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...