Как преобразовать параметр программы из argv
в int64_t
? atoi()
подходит только для 32-битных целых чисел.
Как преобразовать строку в int64_t?
Ответы (7)
Попытка соответствия C99.
[править] нанял @R. исправление
// Note: Typical values of SCNd64 include "lld" and "ld".
#include <inttypes.h>
#include <stdio.h>
int64_t S64(const char *s) {
int64_t i;
char c ;
int scanned = sscanf(s, "%" SCNd64 "%c", &i, &c);
if (scanned == 1) return i;
if (scanned > 1) {
// TBD about extra data found
return i;
}
// TBD failed to scan;
return 0;
}
int main(int argc, char *argv[]) {
if (argc > 1) {
int64_t i = S64(argv[1]);
printf("%" SCNd64 "\n", i);
}
return 0;
}
sscanf
, но вам нужен именно SCNd64
, а не PRId64
. Мне также неясно, должен ли sscanf
вести себя хорошо при переполнении.
- person R.. GitHub STOP HELPING ICE; 09.06.2013
scanf()
предназначено для работы как семейство strtol()
функций. В последнем случае ошибки диапазона приводят к тому, что ERANGE
сохраняется в errno
. (проект 7.8.2.3.3 C11). Таким образом, можно было бы включить тест errno
.
- person chux - Reinstate Monica; 09.06.2013
scanf
вел себя так. Спецификация для scanf
даже не устанавливает каких-либо требований к тому, как хранится определяемое значение. Он просто указывает ожидаемый формат ввода и требуемый тип вывода, но ничего не говорит о процедуре преобразования. Это кажется недосмотром, но оставляет много неясностей в отношении обработки ошибок...
- person R.. GitHub STOP HELPING ICE; 11.06.2013
scanf()
должно работать как strtol()
, исходит из C11 Draft 7.21.6.2.12: d
Соответствует необязательному десятичному целому числу со знаком, формат которого совпадает с ожидаемым для последовательности субъектов функции strtol с значение 10 для базового аргумента … . Мой компилятор Eclipse C Indigo Service Release 1 устанавливает errno
в ERANGE через scanf()
. Так что по крайней мере 1 компилятор делает это. Хотя strtol()
устанавливает errno
, я, как и вы, вижу неоднозначность в отношении scanf()
обработки ошибок. Может другие знают?
- person chux - Reinstate Monica; 11.06.2013
strtol
, это явно не указано, и на самом деле не существует варианта strtol
, соответствующего преобразованиям меньше long
, таким как %d
или %hd
.
- person R.. GitHub STOP HELPING ICE; 11.06.2013
%d
конвертировалось как бы по strtol
, возможно, переполнения long
не произошло бы, но результат мог бы не уместиться в int
, и в этом случае вообще не понятно, что должно произойти. (Зажим, как strtol
? Преобразование, определяемое реализацией, как в выражениях C? Ошибка?)
- person R.. GitHub STOP HELPING ICE; 11.06.2013
fscanf
¶10 … Если подавление присваивания не указано *
, результат преобразования помещается в объект, на который указывает первый аргумент, следующий за аргументом формата, который еще не получил результат преобразования. Если этот объект не имеет соответствующего типа или если результат преобразования не может быть представлен в объекте, поведение не определено. Таким образом, scanf()
имеет UB при переполнении — также указано в Приложении J.2 Неопределенное поведение.
- person Jonathan Leffler; 01.09.2017
Есть несколько способов сделать это:
strtoll(str, NULL, 10);
Это соответствует стандарту POSIX C99.
вы также можете использовать strtoimax; который имеет следующий прототип:
strtoimax(const char *str, char **endptr, int base);
Это хорошо, потому что всегда будет работать с локальным intmax_t... Это C99 и вам нужно включить <inttypes.h>
int64_t
совпадает с long long
.
- person Oliver Charlesworth; 08.06.2013
long long
достаточно велико, чтобы хранить любое значение int64_t
, что почти верно, но не совсем. Смотрите мой ответ.
- person R.. GitHub STOP HELPING ICE; 08.06.2013
int64_t
, этот метод подходит. Когда результат выходит за пределы диапазона int64_t
, этот подход требует неопубликованной дополнительной работы для переносимого обнаружения таких проблем.
- person chux - Reinstate Monica; 20.05.2016
strtoll
преобразует его в long long
, который обычно является 64-битным целым числом.
Сделать это на 100% переносимым немного сложно. long long
должно быть не менее 64 бит, но не обязательно должно быть дополнением до двух, поэтому оно может не представлять -0x7fffffffffffffff-1
, и, таким образом, использование strtoll
может привести к нарушению углового регистра. Та же проблема относится и к strtoimax
. Вместо этого вы можете использовать начальный пробел (если вы хотите разрешить начальный пробел) и сначала проверить знак, а затем использовать strtoull
или strtoumax
, любой из которых требуется для поддержки значений до полного положительного диапазона int64_t
. Затем вы можете применить знак:
unsigned long long x = strtoull(s, 0, 0);
if (x > INT64_MAX || ...) goto error;
int64_t y = negative ? -(x-1)-1 : x;
Эта логика написана, чтобы избежать всех случаев переполнения.
int64_t
определено в какой-либо системе с представлением со знаком, отличным от дополнения до двух. Я также думаю, что это одна из основных причин, по которой целые типы точной ширины являются необязательными.
- person ouah; 08.06.2013
int64_t
и uint64_t
являются необязательными в POSIX. Так что я согласен, что int64_t
вряд ли будет существовать в системах, где long
или long long
не подходят.
- person R.. GitHub STOP HELPING ICE; 09.06.2013
-(x-1)-1
или -x
работают так же, как x
— широкий беззнаковый тип.
- person chux - Reinstate Monica; 20.05.2016
-((int64_t)x-1)-1
. Это имеет больше смысла?
- person R.. GitHub STOP HELPING ICE; 20.05.2016
-((int64_t)x-1)-1
имеет больше смысла, но (int64_t)x
по-прежнему означает целочисленное переполнение со знаком. Возможно -1-((int64_t)-(x+1))
?
- person chux - Reinstate Monica; 20.05.2016
x+1
может быть больше, чем INT64_MAX
, или может быть нулевым, и в любом случае -(x+1)
равно x+1
и либо дает неправильный результат, либо преобразование, определяемое реализацией, при приведении к int64_t
. С другой стороны, (int64_t)x
определено корректно, потому что приведенная выше строка только что проверила, что x
находится в диапазоне int64_t
.
- person R.. GitHub STOP HELPING ICE; 21.05.2016
INT64_MIN
...
- person R.. GitHub STOP HELPING ICE; 21.05.2016
x+1
не больше, чем INT64_MAX
, поскольку я, как и вы в своем предыдущем комментарии, имел в виду левую половину :
(когда negative
истинно - (x<0
)) OTOH, возможно, мне не хватает условия, когда установлено negative
. IAC, приятно работать с вами.
- person chux - Reinstate Monica; 21.05.2016
negative
устанавливалось в зависимости от того, прочитали ли вы символ '-'
перед числом. x<0
всегда ложно, поскольку имеет беззнаковый тип.
- person R.. GitHub STOP HELPING ICE; 21.05.2016
strtoll
, а, вероятно, хуже.
- person R.. GitHub STOP HELPING ICE; 21.05.2016
Пользователи, пришедшие из веб-поиска, также должны учитывать std::stoll
.
Это не совсем эффективно отвечает на этот исходный вопрос для const char*
, но у многих пользователей все равно будет std::string
. Если вас не волнует эффективность, вы должны получить неявное преобразование (на основе определяемого пользователем преобразования с использованием конструктора std::string
с одним аргументом) в std::string
, даже если у вас есть const char*
.
Это проще, чем std::strtoll
, для которого всегда требуется 3 аргумента.
Он должен выдать, если ввод не является числом, но см. комментарии.
Это сработало для меня с другим типом int64, и мне нравится чистый стиль C++:
std::istringstream iss(argv[i]);
int64_t i64;
iss >> i64;
Вы можете получить ошибку компиляции: operator‹‹... не определен.
И я не знаю, что произойдет, если argv[i] будет содержать "HALLO".
Как преобразовать строку в int64_t?
Простейший
#include <stdlib.h>
int64_t value = atoll(some_string); // lacks error checking. UB on overflow
Лучше
long long v = strtoll(s, NULL, 0); // No reported errors, well defined on overflow
Надежный: создайте вспомогательную функцию для обнаружения всех проблем.
#include <stdbool.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
// Return error flag
bool my_strtoi64(int64_t *value, const char *s) {
// Maybe add a s==NULL, value==NULL checks.
char *endptr;
errno = 0;
long long v = strtoll(s, &endptr, 0);
// Optional code for future growth of `long long`
#if LLONG_MIN < INT64_MIN || LLONG_MAX > INT64_MAX
if (v < INT64_MIN) {
v = INT64_MIN;
errno = ERANGE;
} else if (v > INT64_MAX) {
v = INT64_MAX;
errno = ERANGE;
#endif
*value = (int64_t) v;
if (s == endptr) { // No conversion, *v is 0
return true;
}
if (errno == ERANGE) { // Out of range
return true;
}
if (errno) { // Additional implementations specific errors
return true;
}
while (isspace(*(unsigned char* )endptr)) { // skip trail white-space
endptr++;
}
if (*endptr) { // Non-numeric trailing text
return true;
}
return false; // no error
}
int64_t
совпадает сlong
, быстрый хакерский способ — просто использоватьatol()
. - person Oliver Charlesworth   schedule 08.06.2013char *
вint64_t
(если вы этого хотите, ответом будет простое приведение), а вместо этого о том, как преобразовать строку, на которую указываетchar *
, которая представляет число в некотором ориентированное на человека текстовое соглашение, такое как десятичная строка, доint64_t
. - person R.. GitHub STOP HELPING ICE   schedule 09.06.2013