#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
singlechar
вместо ожидаемой строки с нулевым завершением.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);
}
например.