Понимание того, как создавать атой; Как сравниваются персонажи?

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

Вот пример решения, которое я нашел в Интернете:

int atoi( char* pStr ) 
{
  int iRetVal = 0; 

  if ( pStr )
  {
    while ( *pStr && *pStr <= '9' && *pStr >= '0' ) 
    {
      iRetVal = (iRetVal * 10) + (*pStr - '0');
      pStr++;
    }
  } 
  return iRetVal; 
} 

Я думаю, что основная причина, по которой мне было трудно понять, как атой делалось в прошлом, - это способ сравнения персонажей. Оператор while говорит: пока символ существует, и символ меньше или равен 9, и он больше -или-равно-0, затем что-нибудь делать. Это заявление говорит мне о двух вещах:

  1. Символы можно логически сравнить с другими символами (но каково возвращаемое значение?).

До того, как я рассмотрел это, полагаю, я знал это подсознательно, но на самом деле никогда не думал об этом, но символ «5» «меньше», чем символ «6», точно так же, как 5 меньше 6, так что вы можете сравнить символы как целые числа, по сути (для этой цели).

  1. Почему-то while (* sPtr) и * SPtr! = 0 разные. Мне это кажется очевидным, но я считаю, что не могу выразить это словами, а это значит, что я знаю, что это правда, но не понимаю, почему.

Изменить: я понятия не имею, что будет делать часть * pStr - '0'.

Любая помощь в осмыслении этих наблюдений была бы очень ... полезной! Спасибо!


person Joshua    schedule 30.01.2013    source источник
comment
На самом деле while(*s) и while(*s != 0) - это одно и то же. В языке C ненулевые целые числа считаются истинными.   -  person nneonneo    schedule 31.01.2013
comment
А, ладно, я не понимал, в чем заключалась цель заявления. * PStr ›= '0' должно было избежать отрицательных чисел, я полагаю?   -  person Joshua    schedule 31.01.2013
comment
Однако while(*s != 0) и while(*s != '0') не одинаковы, потому что 0 - это число 0, а '0' - это символ '0' (который имеет значение ASCII 48).   -  person nneonneo    schedule 31.01.2013
comment
@ Джошуа: Обратите внимание, что обычно '0'!=0. *pStr <= '9' && *pStr >= '0' проверяет, является ли *pStr цифрой   -  person Grizzly    schedule 31.01.2013
comment
+1 За очень хорошо поставленный вопрос.   -  person 0x499602D2    schedule 31.01.2013


Ответы (8)


Символ в C представлен просто как значение ASCII. Поскольку все цифры в ASCII являются последовательными (т.е. 0x30 == '0' и 0x39 == '9' со всеми другими цифрами между ними), вы можете определить, является ли символ цифрой, просто выполнив проверку диапазона, и вы можно получить значение цифры, вычитая '0'.

person nneonneo    schedule 30.01.2013
comment
Использование isdigit здесь не сработает, поскольку isdigit зависит от локали. Он может вернуть истину для цифровых символов, зависящих от локали, но это потребует понимания системы нумерации для перевода в число. - person JoergB; 31.01.2013
comment
Однако я думаю, что C действительно требует, чтобы цифры были последовательными в любом языковом стандарте. - person Lightness Races in Orbit; 31.01.2013
comment
Я выбрал это в качестве ответа из-за объяснения того, как проверить, является ли символ цифрой. Это одна из вещей, которые, как я знаю, очень пригодятся в будущем. - person Joshua; 05.02.2013

пока персонаж существует

Нет, не совсем. В нем говорится: «Пока символ не равен 0 (или '\0'). В основном, символ ASCII '\0' указывает конец строки« C ». Поскольку вы не хотите выходить за конец массива символов (а точная длина не известно), каждый символ проверяется на '\0'.

Персонажи могут быть логически сопоставлены с другими персонажами.

Верно. Символ - это не что иное, как число, ну, по крайней мере, в кодировке ASCII. В ASCII, например, '0' соответствует десятичному значению 48, '1' - 49, 'Z' - 90 (вы можете посмотреть Таблица ASCII здесь). Так что да, вы можете сравнивать символы так же, как сравниваете целые числа.

Почему-то while (*sPtr) и *sPtr != 0 разные.

Совсем не отличается. Десятичный 0 - это специальный символ ASCII (nul), который используется для обозначения конца строки «C», как я упоминал в начале. Вы не можете увидеть или распечатать (nul), но оно есть.

person Community    schedule 30.01.2013
comment
Ну, вы можете вывести нулевой символ. printf("%c",0);) - person nneonneo; 31.01.2013

* PStr - '0' преобразует символ в его числовое значение '1' - '0' = 1 Цикл while проверяет, не находимся ли мы в конце строки и есть ли у нас допустимая цифра.

person Mark PM    schedule 30.01.2013

Обратите внимание, что опубликованная реализация atoi не завершена. Настоящий атой может обрабатывать отрицательные значения.

Почему-то while (* sPtr) и * sPtr! = 0 разные.

Эти два выражения одинаковы. При использовании в качестве условия *sPtr считается истинным, если значение, хранящееся по адресу sPtr, не равно нулю, и *sPtr != 0 истинно, когда значение, хранимое по адресу sPtr, не равно нулю. Разница заключается в том, что когда оно используется где-то еще, тогда второе выражение оценивается как истинное или ложное, а первое - как сохраненное значение.

person Josef Kufner    schedule 30.01.2013

Строки в стиле C завершаются нулем.

Следовательно:

while ( *pStr && *pStr <= '9' && *pStr >= '0' ) 

Это тесты:

  • *pStr, что мы еще не достигли конца строки, и это эквивалентно записи *pStr != 0 (примечание без одинарной кавычки, значения ASCII 0 или _ 5_).
  • *pStr >= '0' && *pStr <= '9' (возможно, более логично), что символ в *pStr находится в диапазоне от '0' (значение ASCII 48) до '9' (значение ASCII 57), то есть цифра.
person Johnsyweb    schedule 30.01.2013

Представление '0' в оперативной памяти 0x30 и представление '9' - 0x39. Это то, что видит компьютер, и когда он сравнивает их с логическими операторами, он использует эти значения. Завершающий нулевой символ представлен как 0x00 (он же ноль). Ключевым моментом здесь является то, что chars такие же, как и любые другие int для машины.

Следовательно, в заявлении while говорится:

Пока символ, который мы исследуем, действителен (он же НЕ ноль и, следовательно, НЕ является нулевым ограничителем), и его значение (как его видит машина) меньше 0x39, а его значение больше 0x30, продолжайте.

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

person Ephemera    schedule 30.01.2013

Этот фрагмент кода использует значения ascii для накопления целого числа его альфа-эквивалента.

Что касается вашей первой пронумерованной пули, кажется довольно тривиальным, что при сравнении чего-либо результат является логическим. Хотя мне кажется, что вы пытались спросить, действительно ли компилятор понимает «символы». Насколько я понимаю, это сравнение выполняется с использованием значений ascii символов. т.е. a ‹b интерпретируется как (97‹ 98). (Обратите внимание, что также легко увидеть, что значения ascii используются при сравнении 'a' и 'A', поскольку 'A' меньше 'a')

Что касается вашего второго маркера, кажется, что цикл while проверяет, действительно ли присвоенное значение не равно NULL (значение ascii равно 0). Оператор and выдает FALSE, как только встречается ложный оператор, поэтому вы не выполняете сравнение с NULL char. Что касается остальной части цикла while, он выполняет сравнение ascii, как я уже упоминал о маркере 1. Он просто проверяет, соответствует ли данный символ значению ascii, связанному с числом. то есть между "0" и "9" (или ascii: между 48 и 57)

НАСЛЕДНИЕ, на мой взгляд, самая интересная часть (* ptr-'0 '). Этот оператор возвращает целое число от 0 до 9 включительно. Если вы посмотрите на таблицу ascii, вы заметите, что числа от 0 до 9 расположены рядом друг с другом. Итак, представьте «3» - «0», что равно 51 - 48 и дает 3! : D Проще говоря, он выполняет вычитание ascii и возвращает соответствующее целочисленное значение. : D

Ура, и я надеюсь, что это немного объясняет

person TopGunCoder    schedule 30.01.2013

Давайте разберемся:

if ( pStr )

Если вы передадите atoi нулевой указатель, pStr будет 0x00 - и это будет false. В противном случае нам есть что разобрать.

while ( *pStr && *pStr <= '9' && *pStr >= '0' )

Хорошо, здесь происходит куча вещей. *pStr означает, что мы проверяем, является ли значение pStr равным 0x00 или нет. Если вы посмотрите на таблицу ASCII, ASCII для 0x00 будет 'null', а в C / C ++ соглашение заключается в том, что строки заканчиваются нулем (в отличие от строк стиля Pascal и Java, которые говорят вам их длину, а затем содержат столько символов) . Итак, когда *pStr принимает значение false, наша строка подошла к концу, и мы должны остановиться.

*pStr <= '9' && *pStr >= '0' работает, потому что значения для символов ASCII '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' 'все смежные -' 0 'это 0x30 и' 9 '- это, например, 0x39. Итак, если pStr указывает на значение вне этого диапазона, то мы не анализируем целое число и должны остановиться.

iRetVal = (iRetVal * 10) + (*pStr - '0');

Поскольку свойства чисел ASCII являются смежными в памяти, случается так, что, если мы знаем, что у нас есть число, *pStr - '0' вычисляет его числовое значение - 0 для '0' (0x30 - 0x30), 1 для '1' (0x31 - 0x30). . 9 вместо «9». Итак, мы сдвигаем наш номер вверх и перемещаемся на новое место.

pStr++;

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

Обратите внимание, что эта функция не сработает, если строка не оканчивается нулем, в ней есть какие-либо не числовые значения (например, '-') или если она каким-либо образом не является ASCII. Это не магия, это просто правда.

person Patashu    schedule 30.01.2013