Преобразование с шестнадцатиричным преобразованием в десятичное число

Я изучаю C, и я не могу понять одну из упражнений K & R, список:

Упражнение 2-3. Напишите функцию htoi(s) , которая преобразует строку шестнадцатеричных цифр (включая необязательный 0x или 0X ) в ее эквивалентное целочисленное значение. Допустимые цифры: от 0 до 9 , от a до f и от A до F

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

Может ли кто-нибудь дать мне несколько указателей на то, как лучше всего это понять, я не ищу, чтобы кто-то держал меня за руку, но вместо этого направил меня к правильному пониманию, чтобы я мог написать это в самой изящной форме, насколько это возможно, а не с printf("%x", skill);

Рекурсия не нужна. Вам просто нужно пройти назад по строке (т.е. начать в столбце единиц), суммируя однократное преобразование, умноженное на коэффициент умножения. Это псевдокод и не обрабатывает необязательный префикс 0x (и не проверяет возможность переполнения):

 long total = 0; long multiplier = 1; for (int i = string.length - 1; i >= 0 i--) { digit = ConvertSingleHexDigittoInt(string[i]); total += digit * multiplier; multiplier *= 16; } 

Я оставил легкую реализацию ConvertSingleHexDigittoInt () для вас 🙂

У Митча есть основная идея, но давайте рассмотрим ее немного подробнее.

Шестнадцатеричное число – это только основание 16, что означает, что цифры (справа налево) имеют значения как

цифра × 16 0 (т.е. 1)
цифра × 16 1 (т.е. 16)
цифра × 16 2 (256)

и так далее. Итак, 0xE – 14, например.

То, что вам нужно, это цикл, начинающийся с правого конца строки. Скажем, строка s, length (s) – длина строки. В псевдокоде вы хотите

 value = 0 r = 1 // ask yourself "what values does r take as this proceeds?" for i from length(s)-1 to 0 // Ask yourself "why length(s)-1?" value = value + (digitval(s[i])*r) // get ready for the next digit r = r * 16 

digitval(char c) должна быть функцией, которая переводит checract в «0123456789ABCDEF» в значения между 0 и 15 (включительно). Я оставлю это как упражнение с одним подсказкой: «массивы».

будьте осторожны с одной дополнительной проблемой; поскольку у вас может быть ведущее «0» или «0x», вам необходимо убедиться, что вы обрабатываете эти случаи.

Обработка строки слева направо проще и, возможно, более читабельна для тех, кто удобен в математике. Страtagsя понимает, что, например, 1234 = (((1 x 10) + 2) x 10 + 3) x 10 + 4

Другими словами, когда вы обрабатываете каждую цифру слева направо, умножайте предыдущее итоговое значение на базу, эффективно «перемещая ее влево» на одну позицию, затем добавляйте новую цифру.

 long decFromHexStr(const char *hexStr) { int i; long decResult = 0; // Decimal result for (i=0; i < strlen(hexStr); ++i) { decResult = 16 * decResult + decFromHexChar(hexStr[i]); } return decResult; } 

Опытные программисты, вероятно, использовали бы указатель для перехода через строку вместо того, чтобы рассматривать ее как массив:

 long decFromHexStr(const char *pHex) { long decResult = 0; while (*pHex != '\0') { decResult = 16 * decResult + decFromHexChar(*pHex++); } return decResult; } 

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

Повеселись!

Что означает шестнадцатеричное число? Возьмем 15FA . Это значит

 1 * 16^3 + 5 * 16^2 + 15 * 16^1 + 10 * 16^0 

Обратите внимание, что A представляет десять, одиннадцать один и т. Д. До F, который представляет пятнадцать. Также 16 ^ 0 равно 1.

Итак, все, что нам нужно сделать, это вычислить значение выражения выше! Простейший способ, вероятно, сделать это в следующем порядке:

 10 * 1 15 * 16 5 * 256 //256 = 16 * 16 1 * 4096 //4096 = 16 * 16 * 16 

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

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

 ((1 * 16 + 5) * 16 + 15) * 16 + 10 

Если хотите, попробуйте каждый из этих методов.

Дополнительная информация:

В основном, компьютеры используют базу 2 (также называемую двоичной) для всех своих номеров и вычислений. Даже строка «1A6DC0» закодирована с 1s и 0s, которые в конечном итоге отображаются на экране в виде букв и цифр.

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

Например, когда вы это делаете

 x = (11 + y) * 6; 

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

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

Например, 15FA будет расширяться следующим образом:

 1 -> 0001 5 -> 0101 F -> 1111 A -> 1010 15FA -> 0001 0101 1111 1010 

Обратите внимание, что это обычно невозможно сделать напрямую и обычно включает сдвиги в логическом и / или битном ( | и << ). Забавные вещи.

Я, вероятно, не внес большой вклад, есть хорошие ответы выше. Но я попробую.

Как и другие, передо мной, я оставляю некоторые функции для вас.

 int htoi(const char* x) { unsigned int current_position;/*current position is to be defined*/ int prefixed=0; int dec=0; char* y = x; if (x && x+1 && (*(x+1)=='x' || *(x+1)=='X')){ /*Is 0x or 0X prefix present?*/ prefixed= PREFIXED; } if (prefixed) y+=2; /*Jumps over 0x or 0X*/ while (*y){ /*getPos(const char*) and singleHexToDec(const char*,unsigned int) functions to be implemented*/ current_position=getPos(y); dec+=singleHexToDec(y,current_position); } return dec; } 

попробуйте объяснить моим грубым английским языком 🙁

Мой код (предположим, что все входы исправлены. Избегайте защитных программ)

 #include  enum { SZ = 11 }; unsigned int htoi(const char *s); int main() { char buff[SZ]; //Max 11 char: 0x XX XX XX XX '\0' (2 + 8 + 1) while(fscanf(stdin, "%s", buff) != EOF) printf("%X\n", htoi(buff) ); return 0; } unsigned int htoi(const char *s) { unsigned int i, r = 0; for(i = (s[1] == 'x') ? 2 : 0; s[i] != '\0'; i++) r = ( r << 4 ) + ( (s[i] > '9') ? 0x9 : 0x0 ) + ( s[i] & 0xF ); return r; } 

Хорошо, прежде всего, назначьте r = 0. Затем, когда мы начинаем for-bucle, мы даем значение init для индексации переменной i. Мы должны проверить, имеет ли строка 0x формат или нет. Нам нужно только проверить позицию 1, чтобы узнать, обрабатываем ли мы входную строку с форматом 0x или без него.

Теперь у нас есть указатель, указывающий на первый правильный характер! Для каждого итерации мы смещаем 4 бита влево. Мы получаем 4 нуля. Идеальный разрыв, чтобы добавить новую шестую цифру! Пример:

 Input: 0xBE1234 Is s[1] == 'x' ? true then i = 2; r = 0; iter 1: r = 0x0; r = 0x0; r = 0xB; iter 2: r = 0xB; r = 0xB0; r = 0xBE; iter 3: r = 0xBE; r = 0xBE0; r = 0xBE1; iter 4: r = 0xBE1; r = 0xBE10; r = 0xBE12; iter 5: r = 0xBE12; r = 0xBE120; r = 0xBE123; iter 6: r = 0xBE123; r = 0xBE1230; r = 0xBE1234 

Может быть, это немного усложняет:

  r = ( r << 4 ) + ( (s[i] > '9') ? 0x9 : 0x0 ) + ( s[i] & 0xF ); 

Прежде всего, мы вытесняем 4 бита, такие же, как умножение на 16, но более эффективные. Затем мы посмотрим, есть ли у нас символ ASCII больше, чем «9». Если это правда, мы работаем с A, B, C, D, E, F или a, b, c, d, e, f. Помните, мы предполагаем, что у нас есть правильный ввод. Итак, теперь взгляните на таблицу ASCII:

 A = 0100 0001 - a = 0110 0001 ... F = 0100 0110 - f = 0110 0110 

но мы хотим что-то вроде этого:

 A = 0000 1010 - a = 0000 1010 ... F = 0000 1111 - f = 0000 1111 

Как мы это делаем? После смещения мы очищаем 4 наиболее значимых бит с маской s [i] & 0xF:

 s[2] == 'B' == 0100 0010 s[2] & 0xF == 0000 0010 

и добавьте 9 для адаптации к целочисленному значению (только в том случае, если s [i] в ​​{‘A’ … ‘F’, ‘a’ … ‘f’})

 s[2] & 0xF + 0x9 = 0000 0010 + 0000 1001 = 0000 1011 (0xB) 

Наконец, мы добавляем к смещенному значению r и присваиваем r. Последовательность выполнения для второй итерации (с [3]):

 r == 0xB, s[3] == 'E' == 0100 0101 (start iter 2) (r << 4) == 0xB0, s[3] == 'E' == 0100 0101 (displacement r << 4 ) (r << 4) == 0xB0, (s[3] & 0xF + 0x9) == 0000 1110 == 0xE (clear most significant bits of s[3] and add 0x9) r = (r << 4) + ( s[3] & 0xF + 0x9 ) == 0xBE == 1011 1110 (add all and assign to r) 

Что произойдет, если у нас есть числовой символ, такой как s [4]?

 s[4] == '1' == 0011 0001 s[4] & 0xF == 0000 0001 

Смещение r четырех позиций, добавьте 0 (ничего), добавьте результат логической операции s [i] & 0xF и, наконец, присвойте r.

 r == 0xBE, s[4] == '1' == 0011 0001 (start iter 3) (r << 4) == 0xBE0, s[4] == '1' == 0011 0001 (displacement r << 4 ) (r << 4) == 0xBE0, (s[4] & 0xF + 0x0) == 0000 0001 (clear most significant bits of s[4] and add 0) r = (r << 4) + s[4] & 0xF == 0xBE1 == 1011 1110 0001 (add all and assign) 

Помните, мы сдвигаем 4, поэтому мы не собираем разрядные биты, потому что добавляем менее значимые биты с пробелом в четыре нуля.

PD: Я обещаю улучшить мой английский, чтобы объяснить лучше, извините.

Обычный подход преобразуется слева направо. Аккумулятор устанавливается на ноль в начале и умножается на 16, прежде чем добавлять эквивалентное значение каждой новой цифры в цикл.

Для функции htoi() которая ожидает шестнадцатеричные цифры с необязательным ведущим 0x , начинайте с пропускания этих символов, если они есть. Прямо проверять значения s[0] и s[1] , вероятно, самый ясный подход.

Если вы знаете цифры в ASCII, вы можете использовать выражения типа s[i] - '0' и s[i] - 'A' + 10 для преобразования i-й цифры в ее целое значение.

Вы, вероятно, захотите сложить все это в один случай для здравомыслия.

Изменить: Изменено *s на s[i] для согласованности с наблюдением, что указатели из будущего с точки зрения этого упражнения.

Обратите внимание, что существует несколько других способов преобразования отдельных цифр в значения. Например, вы можете найти их в векторе всех цифр (что-то вроде strchr("0123456789ABCDEF",s[i]) ), постройте единую таблицу поиска, индексированную по коду символа, со значением каждой цифры в каждой позиции ( digitvalue[s[i]] после того, как int digitvalue[256] была соответствующим образом инициализирована), используйте оператор switch (s[i]) с меткой case для каждой возможной цифры, как предлагается в другом ответе, или используйте проверки диапазона и арифметику как Я предлагаю выше. Что-то думать о том, что выбрать, и почему. Обратите внимание, что это может быть не очевидный выбор, и лучший ответ может быть другим, если ASCII не является вашим набором символов.

Вчера я написал такую ​​функцию. Вы можете увидеть мой код ниже.

 /* Converting a hex string to integer, assuming the heading 0x or 0X has already been removed and pch is not NULL */ int hex_str_to_int(const char* pch) { int value = 0; int digit = 0; for (; *pch; ++pch) { if (*pch >= '0' && *pch <= '9') { digit = (*pch - '0'); } else if (*pch >= 'A' && *pch <= 'F') { digit = (*pch - 'A' + 10); } else if (*pch >= 'a' && *pch <= 'f') { digit = (*pch - 'a' + 10); } else { break; } // Check for integer overflow if ((value *= 16) < 0 || (value += digit) < 0) { return INT_MAX; } } return value; } 

Вот код тестирования:

 int main(void) { printf("%d %d\n", hex_str_to_int("0"), 0x0); printf("%d %d\n", hex_str_to_int("A"), 0xA); printf("%d %d\n", hex_str_to_int("10"), 0x10); printf("%d %d\n", hex_str_to_int("A1"), 0xA1); printf("%d %d\n", hex_str_to_int("AB"), 0xAB); printf("%d %d\n", hex_str_to_int("100"), 0x100); printf("%d %d\n", hex_str_to_int("1A2"), 0x1A2); printf("%d %d\n", hex_str_to_int("10A"), 0x10A); printf("%d %d\n", hex_str_to_int("7FFFFFF"), 0x7FFFFFF); printf("%d %d\n", hex_str_to_int("7FFFFFF1"), 0x7FFFFFF1); printf("%d %d\n", hex_str_to_int("7FFFFFF2"), 0x7FFFFFF2); printf("%d %d\n", hex_str_to_int("7FFFFFFE"), 0x7FFFFFFE); printf("%d %d\n", hex_str_to_int("7FFFFFFF"), 0x7FFFFFFF); printf("%d %d\n", hex_str_to_int("80000000"), 0x7FFFFFFF + 1); printf("%d %d\n", hex_str_to_int("80000001"), 0x7FFFFFFF + 2); printf("%d %d\n", hex_str_to_int("10AX"), 0x10A); printf("%d %d\n", hex_str_to_int("203!"), 0x203); return 0; } 

Он выводит следующие значения:

 0 0 10 10 16 16 161 161 171 171 256 256 418 418 266 266 134217727 134217727 2147483633 2147483633 2147483634 2147483634 2147483646 2147483646 2147483647 2147483647 2147483647 -2147483648 2147483647 -2147483647 266 266 515 515