2015-08-29 5 views
0

В C, это правда, что:Какова связь между представлением памяти и значением переменной в C?

[8-bit] signed char: -127 to 127 
[8-bit] unsigned char: 0 to 255 

Но что же на самом деле происходит в памяти? Является ли подписанный символ представленным в двух дополнениях и неподписанным символом, представленным без какого-либо конкретного представления (т. Е. Последовательность из 11111111)?

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

Спасибо!

+0

Почему бы не прочитать единственный [авторитетный ресурс] (http://port70.net/~nsz/c/c11/n1570.html#6.2.6)? Он должен ответить на все ваши вопросы. И вы можете захотеть повторно узнать о языках _statically_ vs. _dynamically_ types. – Olaf

+0

Это должно быть полезно: http://stackoverflow.com/questions/7681024/negative-numbers-are-stored-as-2s-complement-in-memory-how-does-the-cpu-know-i – aniliitb10

+0

Обычно сначала бит используется для знака. '0b01111111' - 127,' 0b10000000' - -128 – EvgeniyZh

ответ

4

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

void test1() 
{ 
    char p = 0; 
    p += 3; 
} 

void test2() 
{ 
    unsigned char p = 0; 
    p += 3; 
} 

Вы видите здесь инструкции, составленные компилятором из источника, опубликованного выше. Скомпилированный без оптимизации -O0 это созданная сборка clang 3.7. Вы можете игнорировать большинство инструкций, если вы не знакомы с ними. Обратите внимание на movsx и movzx. Эти две инструкции влияют на то, как обрабатывается место памяти.

test1():        # Instructions for test1 
    push rbp 
    mov rbp, rsp 
    mov byte ptr [rbp - 1], 0 
    movsx eax, byte ptr [rbp - 1] <-- Move byte to word with sign-extension 
    add eax, 3 
    mov cl, al 
    mov byte ptr [rbp - 1], cl 
    pop rbp 
    ret 

test2():        # Instructions for test2 
    push rbp 
    mov rbp, rsp 
    mov byte ptr [rbp - 1], 0 
    movzx eax, byte ptr [rbp - 1] <-- Move byte to word with zero-extension 
    add eax, 3 
    mov cl, al 
    mov byte ptr [rbp - 1], cl 
    pop rbp 
    ret 
+2

x86 ассемблер на самом деле не является хорошим для демонстрации людям, которые, скорее всего, не знакомы с ассемблером в целом. Вы должны объяснить отдельные строки. – Olaf

+0

Используются фактические операции movzx и movzx .. это «" movsx "", я знаю, что это опечатка! – aniliitb10

+1

Я скомпилировал его с 'signed char x = 255; unsigned char y = 255; '. Я заметил, что компилятор вставляет в регистр -1: 'movb $ -1, -2 (% rbp)'. Если я скомпилирую его с 'signed char x = 5', он введет $ 5. Почему это? Это потому, что 255 = -1 = 11111111 в дополнении? Благодаря! – Laurent

0

Внутреннее представление чисел не является частью языка C, это особенность архитектуры самой машины. Большинство реализаций используют дополнение 2, потому что он делает сложение и вычитание одной и той же двоичной операции (операции с подписью и без знака идентичны).

FYI Почти все существующие аппаратные средства процессора используют два дополнения, поэтому имеет смысл, что и большинство языков программирования.

+1

И нет, язык не поддерживается. Это, но оставляет его для реализации. Обратите внимание, что правила преобразования для спящих без знака фактически оптимизированы для дополнения 2s. – Olaf

+0

Я отредактирую свой ответ. Спасибо @Olaf – Fawzan

+0

В C, как первоначально задумывалось, значение переменной было эффективно определено базовым хранилищем. Изменение значения изменит хранилище, и изменение хранилища изменит значение. Стандарт C89 ослабил эту связь, а C99 еще больше ослабил ее, до такой степени, что C99 семантически намного слабее, чем язык, популярный в 1990-х годах. – supercat

2

C является строго типизированным языком. Интерпретация памяти полностью определяется контекстом. То есть тип (достаточно хорошо в случае динамической отправки) известен во время компиляции, и компилятор делает все решения заранее. Для обеспечения производительности проверки времени выполнения сводятся к минимальному минимуму (в C никому, если вы не выполняете динамическую диспетчеризацию или RTTI вручную).

В C (и C++) вы можете легко интерпретировать одно и то же местоположение памяти по-разному, все, что вам нужно сделать, это получить указатель на него и передать его другому типу. Очень опасно, если вы не знаете, что делаете.

Смежные вопросы