Адрес памяти для каждого байта отдельный?
У каждого байта есть свой адрес?
Если создать переменную int (у меня весит 4 байта). Адрес памяти один для 4 байт, или для каждого байта отдельный адрес?
int x = 1;
например переменная x весит 4 байта, я могу как-то обращаться к каждому байту отдельно?
в памяти это выглядит так?
0xFF // address
00000000000000000000000000000001 // 32bit
или так?
0x1
00000000 // 8bit
0x2
00000000
0x3
00000000
0x4
00000001
Ответы (3 шт):
В памяти для 32-битного int это выглядит так:
0x1233 0x1232 0x1231 0x1230
00000000000000000000000000000001
если это little-endian, а если big-endian, то
0x1230 0x1231 0x1232 0x1233
00000000000000000000000000000001
Т.е. это единое целое из 4 байтов, но в принципе вы можете обратиться (адресовать) к каждому байту отдельно. Но единое число типа int занимает 4 байта и имеет адрес (в приведенном случае) 0x1230.
Грубо говоря, вы можете обратиться к каждому ученику в классе (но не отдельно к его, скажем, желудку :)), но для какого-то школьного мероприятия класс — единое целое со старостой класса во главе, по которому можно адресовать весь класс :)
В общем случае наличие переменной отнюдь не означает выделенную память. Переменная может быть размещена компилятором в регистре, она может быть размещена по одному адресу с другой переменной (например, если их области жизни не пересекаются), код может быть преобразован так, что ваша переменная вовсе исчезнет.
Мой любимый пример: вот такой код
unsigned int sum(unsigned int num)
{
unsigned int acc = 0;
for (unsigned int i = 0; i <= num; i++)
acc += i;
return acc;
}
оптимизирующий компилятор преобразует в такой ассемблер:
sum(unsigned int):
mov ecx, edi
lea eax, [rdi - 1]
imul rax, rcx
shr rax
add eax, edi
ret
(который можно примерно перевести назад на C++ как return num * (num - 1) / 2 + num).
Сколько байт весит переменная i исходного кода? Вопрос некорректен.
Ещё один любопытный пример: код
class adder
{
int a, b;
public:
adder(int a, int b) : a(a), b(b) {}
int get() { return a + b; }
};
int add0(int x, int y)
{
return x + y;
}
int add1(int x, int y)
{
adder w(x, y);
return w.get();
}
int add2(int x, int y)
{
adder* pw = new adder(x, y);
int result = pw->get();
delete pw;
return result;
}
int add3(int x, int y)
{
adder* pw = new adder(x, y);
try
{
int result = pw->get();
delete pw;
return result;
}
catch (...)
{
delete pw;
throw;
}
}
int add4(int x, int y)
{
std::unique_ptr<adder> pw { new adder(x, y) };
return pw->get();
}
скомпилровался в обычное
add0(int, int):
lea eax, [rdi + rsi]
ret
add1(int, int):
lea eax, [rdi + rsi]
ret
add2(int, int):
lea eax, [rdi + rsi]
ret
add3(int, int):
lea eax, [rdi + rsi]
ret
add4(int, int):
lea eax, [rdi + rsi]
ret
то есть все пять функций компилируются в аналог return x + y;. Какой адрес объекта adder? Где он расположен, в стеке или в куче? Вопрос некорректен.
Компилятор обязан следовать семантике вашего кода: если код по сути считает сумму чисел, то после компиляции должна получиться сумма чисел. Компилятор не обязан следовать синтаксису вашего кода: если для сложения вы используете дополнительный класс, компилятор вовсе не обязан делать то же самое. Поэтому компилятор имеет право под капотом менять код, убирать и добавлять переменные, менять алгоритм с квадратичного на линейный и наоборот, убирать аллокации, всё что угодно. Понятия исходного кода (переменные, функции, классы, команды) вовсе не обязаны иметь соответствие в компиляте.
Не пойму почему в ответах нет прямого ответа на вопрос: как разбить int на байты?
#include <stdio.h>
int main () {
int i = 1;
unsigned char *b = (unsigned char*) &i;
printf("%X %X %X %X\n", b[0], b[1], b[2], b[3]);
return(0);
}
Один из вариантов.