2011-10-24 6 views
6

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

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

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

Примечание: Я не ищу лучший способ преобразовать char к int.

+0

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

+0

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

+1

@Matteo: без всяких сомнений, если 's [pos]' не является 0 байтом или иначе символом, который заставляет 'atoi' прекращать чтение ;-) –

ответ

10

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

Как о следующем:

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

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

+0

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

+0

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

0

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

atoi ожидает, что строка C-стиля, std::string - это класс C++ с различным поведением и характеристиками. Для начала - это не должно быть NULL прекращено.

0

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

1

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

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

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

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

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

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

int GetDigit2(const string& s, const size_t& pos) { 
    const char n[2] = {s[pos], '\0'}; 
    return atoi(n); 
} 

например.

3

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

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

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

+0

+1: для указания другой проблемы и приятного объяснения –

Смежные вопросы