Чтение символов на битовом уровне

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

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

e.g.

01010101 = false,true,false,true,false,true,false,true

Я бы опубликовал идею того, как я пытался сделать это сам, но я понятия не имею, я все еще экспериментирую с C, и это мой первый опыт программирования на таком низком уровне.

Спасибо


person Jamie Keeling    schedule 02.02.2010    source источник
comment
Вы хотите это на C или на C#? Тег .net3-5, который вы поместили, будет означать C#, но вы также поместите тег C, который несовместим.   -  person Marcel Gosselin    schedule 02.02.2010
comment
Это было веселое упражнение!   -  person Aiden Bell    schedule 02.02.2010


Ответы (3)


Этот код C89:

/* we need this to use exit */
#include <stdlib.h>
/* we need this to use CHAR_BIT */
#include <limits.h>
/* we need this to use fgetc and printf */
#include <stdio.h>

int main() {
    /* Declare everything we need */
    int input, index;
    unsigned int mask;
    char inputchar;

    /* an array to store integers telling us the values of the individual bits.
       There are (almost) always 8 bits in a char, but it doesn't hurt to get into
       good habits early, and in C, the sizes of the basic types are different
       on different platforms. CHAR_BIT tells us the number of bits in a byte. 
     */
    int bits[CHAR_BIT];

    /* the simplest way to read a single character is fgetc, but note that
       the user will probably have to press "return", since input is generally 
       buffered */
    input = fgetc(stdin);
    printf("%d\n", input);

    /* Check for errors. In C, we must always check for errors */
    if (input == EOF) {
        printf("No character read\n");
        exit(1);
    }

    /* convert the value read from type int to type char. Not strictly needed,
       we can examine the bits of an int or a char, but here's how it's done. 
     */
    inputchar = input;

    /* the most common way to examine individual bits in a value is to use a 
       "mask" - in this case we have just 1 bit set, the most significant bit
       of a char. */
    mask = 1 << (CHAR_BIT - 1);

    /* this is a loop, index takes each value from 0 to CHAR_BIT-1 in turn,
       and we will read the bits from most significant to least significant. */
    for (index = 0; index < CHAR_BIT; ++index) {
        /* the bitwise-and operator & is how we use the mask.
           "inputchar & mask" will be 0 if the bit corresponding to the mask
           is 0, and non-zero if the bit is 1. ?: is the ternary conditional
           operator, and in C when you use an integer value in a boolean context,
           non-zero values are true. So we're converting any non-zero value to 1. 
         */
        bits[index] = (inputchar & mask) ? 1 : 0;

        /* output what we've done */
        printf("index %d, value %u\n", index, inputchar & mask);

        /* we need a new mask for the next bit */
        mask = mask >> 1;
    }

    /* output each bit as 0 or 1 */
    for (index = 0; index < CHAR_BIT; ++index) {
        printf("%d", bits[index]);
    }
    printf("\n");

    /* output each bit as "true" or "false" */
    for (index = 0; index < CHAR_BIT; ++index) {
        printf(bits[index] ? "true" : "false");
        /* fiddly part - we want a comma between each bit, but not at the end */
        if (index != CHAR_BIT - 1) printf(",");
    }
    printf("\n");
    return 0;
}

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

person Steve Jessop    schedule 02.02.2010
comment
+1 за подробный пример, который заставил меня удалить свой собственный. Любой другой (если не упрямый или глупый) должен сделать то же самое;) - person Aiden Bell; 02.02.2010
comment
Очень тщательно, к сожалению, при вставке в пустой исходный файл я получаю 45 ошибок, в основном синтаксические ошибки и необъявленные идентификаторы. - person Jamie Keeling; 02.02.2010
comment
Отлично, замечательно работает. Спасибо за такое замечательное объяснение вашего решения. - person Jamie Keeling; 02.02.2010

Для настройки битов часто безопаснее использовать типы без знака, потому что сдвиги отрицательных значений со знаком имеют эффект, зависящий от реализации. Обычный char может быть как подписанным, так и неподписанным (традиционно он не подписан на платформах MacIntosh, но подписан на ПК). Следовательно, сначала приведите своего персонажа к типу unsigned char.

Тогда вашими друзьями будут побитовые логические операторы (&, |, ^ и ~) и операторы сдвига (<< и >>). Например, если ваш персонаж находится в переменной x, то для получения 5-го бита вы просто используете: ((x >> 5) & 1). Операторы сдвига перемещают значение вправо, отбрасывая пять младших битов и перемещая интересующий вас бит в «самое нижнее положение» (он же «крайний правый»). Побитовое И с 1 просто устанавливает все остальные биты в 0, поэтому результирующее значение равно 0 или 1, что является вашим битом. Обратите внимание, что я нумерую биты от левого значащего (самого правого) до самого значащего (крайнего левого) и начинаю с нуля, а не с единицы.

Если вы предполагаете, что ваши символы 8-битные, вы можете написать свой код как:

unsigned char x = (unsigned char)your_character;
int i;

for (i = 7; i >= 0; i --) {
    if (i != 7)
        printf(",");
    printf("%s", ((x >> i) & 1) ? "true" : "false");
}

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

Обратите внимание, что в соответствии со стандартом C unsigned char имеет по крайней мере восемь бит, но может иметь и больше (в настоящее время только несколько встроенных DSP имеют символы, которые не являются 8-битными). Для дополнительной безопасности добавьте это в начало кода (как объявление верхнего уровня):

#include <limits.h>
#if CHAR_BIT != 8
#error I need 8-bit bytes!
#endif

Это предотвратит успешную компиляцию, если целевая система окажется одним из этих специальных встроенных DSP. Как примечание к примечанию, термин «байт» в стандарте C означает «элементарную единицу памяти, которая соответствует unsigned char», так что, на языке C, байт может иметь более восьми битов (байт не является всегда октет). Это традиционный источник путаницы.

person Thomas Pornin    schedule 02.02.2010

Это, вероятно, не самый безопасный способ - никаких проверок работоспособности/размера/типа - но он все равно должен работать.

unsigned char myBools[8];
char myChar;  

// get your character - this is not safe and you should
// use a better method to obtain input...
// cin >> myChar; <- C++
scanf("%c", &myChar);

// binary AND against each bit in the char and then
// cast the result. anything > 0 should resolve to 'true'
// and == 0 to 'false', but you could add a '> 1' check to be sure.
for(int i = 0; i < 8; ++i)
{
   myBools[i] = ( (myChar & (1 << i) > 0) ? 1 : 0 );
}

Это даст вам массив беззнаковых символов — либо 0, либо 1 (истина или ложь) — для символа.

person Antony Woods    schedule 02.02.2010
comment
Возможно, следует заключать скобки вокруг (1 ‹‹ i)? - person Sam Post; 02.02.2010
comment
Отредактировано, но можете ли вы проверить это, потому что мой C не так силен, как мой C++. - person Antony Woods; 02.02.2010
comment
Я думаю, что OP хочет любого персонажа. - person Antony Woods; 02.02.2010
comment
Сканф должен быть scanf("%c", &myChar); - person interjay; 02.02.2010
comment
Я думал, что в c нет типа bool? - person Jamie Keeling; 02.02.2010
comment
@ Джейми Килинг - Зависит от того, что GNUC использует #ifndef GNUC #include‹bool.h› #endif - person Aiden Bell; 02.02.2010
comment
@acron: Спасибо за попытку, лично я привык к C #, поэтому я так же хорошо осведомлен, как и вы, ха-ха. @Aiden Bell: bool.h не существует. - person Jamie Keeling; 02.02.2010
comment
Отредактировано для использования беззнакового символа вместо логического. 0 — ложь, 1 — правда. - person Antony Woods; 02.02.2010
comment
@ Джейми, тогда ты не на платформе, которая поддерживает это за пределами стандарта. Вы можете ввести свой собственный;) - person Aiden Bell; 02.02.2010