Разные результаты с использованием atoi

#c

#c

Вопрос:

Может ли кто-нибудь объяснить, почему эти вызовы не возвращают тот же ожидаемый результат?

 unsigned int GetDigit(const stringamp; s, unsigned int pos)
{
      // Works as intended
      char c = s[pos];
      return atoi(amp;c);

      // doesn't give expected results
      return atoi(amp;s[pos]);
      return atoi(amp;static_cast<char>(s[pos]));
      return atoi(amp;char(s[pos]));
}
  

Замечание: я не ищу лучший способ преобразовать a char в an int .

Комментарии:

1. Результат «работает по назначению» приводит к UB, поскольку вы переходите к atoi single char вместо ожидаемой строки с нулевым завершением.

2. @littleadv: конечно, я имел в виду указатель на один char ; и передача указателя на один символ, безусловно, UB, потому что у вас нет никаких гарантий того, что следует за ним в стеке (на самом деле, это UB без каких-либо сомнений, потому что вы получаете atoi доступ к памяти после последнего элемента «массива»).

3. @Matteo: без каких-либо сомнений, если s[pos] это не байт 0 или иной символ, который приводит atoi к остановке чтения 😉

Ответ №1:

Ни одна из ваших попыток не верна, включая «работает по назначению» (это просто случайно сработало). Для начала atoi() требуется строка с нулевым завершением, которую вы не предоставляете.

Как насчет следующего:

 unsigned int GetDigit(const stringamp; s, unsigned int pos)
{
      return s[pos] - '0';
}
  

Предполагается, что вы знаете, что s[pos] это допустимая десятичная цифра. Если вы этого не сделаете, потребуется некоторая проверка ошибок.

Комментарии:

1. atoi останавливает чтение входной строки на первом символе, который он не может распознать как часть числа. Это может быть нулевой символ. Так что на самом деле звучит так, как будто нулевой символ определенно не нужен, не так ли? Но, тем не менее, вы правы, ни одно из моих решений не является правильным.

2. @RonaldMcBean: завершающий символ — каким бы он ни был — должен быть частью строки, поскольку чтение после конца строки является неопределенным поведением.

Ответ №2:

То, что вы делаете, это используете a std::string , получаете один символ из его внутреннего представления и вводите на него указатель atoi , который ожидает a const char* , который указывает на строку с нулевым завершением. std::string Не гарантируется, что A хранит символы так, чтобы был конечный ноль, просто повезло, что ваша реализация C , похоже, делает это.

Правильным способом было бы запросить std::string версию содержимого с нулевым завершением, используя s.c_str() , а затем вызвать atoi , используя указатель на него.

В вашем коде есть еще одна проблема, вы преобразуете результат atoi в an unsigned int , в то время atoi как возвращает signed int . Что, если ваша строка равна «-123»?

Комментарии:

1. 1: за указание на другую проблему и хорошее объяснение

Ответ №3:

Поскольку int atoi(const char* s) принимает указатель на поле символов, ваши последние три использования возвращают число, соответствующее последовательным цифрам, начинающимся с amp;s[pos] , например, оно может давать 123 для строки типа "123" , начинающейся с позиции 0 . Поскольку данные внутри a std::string не обязательно должны завершаться нулем, ответом может быть что-либо другое в некоторой реализации, т. Е. Неопределенное поведение.

Ваш «рабочий» подход также использует неопределенное поведение. Это отличается от других попыток, поскольку оно копирует значение s[pos] в другое место. Похоже, это работает только до тех пор, пока соседний байт в памяти рядом с символом c случайно оказывается нулевым или нецифровым символом, что не гарантируется. Поэтому следуйте совету, данному @aix.

Чтобы заставить его действительно работать, вы могли бы сделать следующее:

 char c[2] = { s[pos], '' };
return atoi(c);
  

Ответ №4:

если вы хотите получить доступ к данным в виде строки C — используйте s.c_str() , а затем передайте его atoi .

atoi ожидает строку в стиле C, std::string это класс C с другим поведением и характеристиками. Для начала — это не обязательно должно завершаться нулем.

Ответ №5:

atoi принимает указатель на char для своего аргумента. При первой попытке, когда вы используете char c , он принимает указатель только на один символ, следовательно, вы получаете желаемый ответ. Однако в других попытках вы получаете указатель на a char , который оказался началом строки char s, поэтому я предполагаю, что после последующих попыток вы получаете atoi число, преобразованное из символов в позициях pos , pos 1 , pos 2 и до конца s строки.

Ответ №6:

Если вы действительно хотите преобразовать только один символ в строке в позиции (в отличие от подстроки, начинающейся с этой позиции и заканчивающейся в конце строки), вы можете сделать это следующими способами:

 int GetDigit(const stringamp; s, const size_tamp; pos) {
    return atoi(string(1, s[pos]).c_str());
}

int GetDigit2(const stringamp; s, const size_tamp; pos) {
    const char n[2] = {s[pos], ''};
    return atoi(n);
}
  

например.