Как память хранилища подписанных и неподписанных символов - PullRequest
3 голосов
/ 25 апреля 2020

Только начал изучать C, и я немного запутался.

У меня есть несколько вопросов:

  1. Если у меня есть следующий код:
signed  char x = 56;
// ‭In the RAM, I will see 00111000‬ yes/no?

signed char z = -56;
// In the RAM, I will see 11001000 yes/no?

unsigned char y = 200;
// ‭In the RAM, I will see 11001000‬ yes/no?
У меня есть следующий код:
if (z<0){
   printf("0 is bigger then z ");
}

После компиляции, как инструкции по сборке узнают, что z равно -56, а не 200? (Есть специальные инструкции ASM для подписанного и неподписанного? ).

Как я уже упоминал в вопросе № 1, значение z и y равно 11001000, и нет никаких признаков того, что он знает, подписан он или нет.

Приносим свои извинения, если я не сделал этого Не могу найти правильный способ задать вопрос, надеюсь, вы меня понимаете. Спасибо

Ответы [ 5 ]

5 голосов
/ 25 апреля 2020

Числа хранятся в двоичном виде. Отрицательные числа обычно хранятся в виде дополнения до двух, но язык C допускает разные представления. Так вот этот:

signed char z = -56;
// In the RAM, I will see 11001000 yes/no?

обычно да, но может быть не на некоторых exoti c платформах.

Второй вопрос слишком специфичен для реализации c. Например, сравнение с нулем на x86 может быть выполнено как самосравнение, и это повлияет на регистр flags, поскольку флаг знака сравнения без знака (SF) игнорируется.

4 голосов
/ 25 апреля 2020

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

void foobar();

void foo(unsigned char a)
{
    if (a < 10)
        foobar();
}

void bar(char a)
{
    if (a < 10)
        foobar();
}

переведет в этот код MIPS с G CC 5.4, используя флаг -O3.

foo:
        andi    $4,$4,0x00ff
        sltu    $4,$4,10
        bne     $4,$0,$L4
        nop

        j       $31
        nop

$L4:
        j       foobar
        nop

bar:
        sll     $4,$4,24
        sra     $4,$4,24
        slt     $4,$4,10
        bne     $4,$0,$L7
        nop

        j       $31
        nop

$L7:
        j       foobar
        nop

Это интересная часть функции foo (которая использует тип unsigned char)

foo:
        andi    $4,$4,0x00ff
        sltu    $4,$4,10

Как вы можете видеть, используется команда sltu, которая является невидимой версия slt. (Вам не обязательно знать, что он делает)

Хотя, если мы посмотрим на функцию bar релевантная часть

bar:
        sll     $4,$4,24
        sra     $4,$4,24
        slt     $4,$4,10

Вы увидите, что используется slt, который будет обрабатывать его операнд регистра как подписанный. Пара sll и sra делает расширение знака, так как здесь операнды a были подписаны, так что это необходимо, в то время как в случае без знака это не так.

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

2 голосов
/ 25 апреля 2020

Компилятор будет генерировать разные инструкции в зависимости от того, является ли это типом без знака или со знаком. И это то, что говорит процессору, как его лечить. Так что да, есть отдельные инструкции для подписанных и неподписанных. Для процессоров Intel существуют также отдельные инструкции в зависимости от ширины (char, short, int)

1 голос
/ 25 апреля 2020

есть специальные инструкции ASM для подписанных и неподписанных?

Да, аппаратные средства обычно имеют инструкции машинного кода (или последовательности команд), которые могут

  • знак расширяет байт до размера слова
  • ноль расширяет байт до размера слова
  • сравнивает подписанные величины для различных отношений <, <=,>,> =
  • сравнивает без знака величины для различных отношений <, <=,>,> =

откуда в инструкциях по сборке известно, что z равно -56, а не 200?

In языки высокого уровня, мы связываем тип с переменной. С этого момента компилятор знает способ интерпретации кода, который использует переменную по умолчанию. (Мы можем переопределить или изменить эту интерпретацию по умолчанию, используя приведение при использовании переменной.)

В машинном коде есть только байты, либо в памяти, либо в регистрах ЦП. Таким образом, дело не в том, как оно хранится (для подписанного или неподписанного), а в том, какие инструкции используются для доступа к хранилищу. Компилятор будет использовать правильный набор инструкций машинного кода каждый раз, когда к переменной обращаются.

Хотя мы храним много вещей в памяти, процессор не имеет понятия объявления переменных. Процессор видит только инструкции машинного кода и интерпретирует все типы данных глазами инструкций, которые ему предписано выполнять.

Как программист на ассемблере, ваша задача - применять правильные инструкции (здесь подписано против без знака) к одной и той же переменной каждый раз, когда она используется. Использование байта в качестве переменной со знаком, а затем в качестве переменной без знака является ошибкой logi c, которую легко сделать на языке ассемблера.

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

0 голосов
/ 25 апреля 2020

Компьютеры не знают и не заботятся о таких вещах. Без подписи и подписи имеет отношение только к программисту. Значение 0xFF может одновременно быть -1, 255 или адресом или частью адреса. Часть числа с плавающей точкой и так далее. Компьютер не заботится. КАК программист передает их интерпретацию битов через программу. Понимание того, что сложение и вычитание также не заботятся о знаковых и беззнаковых, потому что это тот же лог c, но другие инструкции, такие как умножения, где результат больше, чем входы, или деление, где результат меньше, чем входы, тогда есть без знака. и подписанные версии инструкций, или ваш процессор может иметь только одну, и вы должны синтезировать другую (или ни одну, и вы должны синтезировать обе).

int fun0 ( void )
{
    return(5);
}
unsigned int fun1 ( void )
{
    return(5);
}
00000000 <fun0>:
   0:   e3a00005    mov r0, #5
   4:   e12fff1e    bx  lr

00000008 <fun1>:
   8:   e3a00005    mov r0, #5
   c:   e12fff1e    bx  lr

без специальных битов и номенклатуры ... биты - биты.

Компилятор, приводимый в действие индикацией «подпись против беззнака» в инструкциях привода языка высокого уровня и значения данных, которые вызывают Если вывести флаги, которые указывают больше, меньше и равны через одиночные флаги или комбинации, то условная ветвь может быть взята на основе флага. Часто, но не всегда, компилятор генерирует противоположное, если z <0, тогда сделайте что-то, что скажет компилятор, если z> = 0, затем перепрыгните через что-то.

...