использование strtol для строкового литерала, вызывающего ошибку сегментации - PullRequest
0 голосов
/ 02 ноября 2018

У меня есть строка, которую я получаю getline() (точнее, я использую цикл и getline для чтения файла построчно)

Скажем, строка 12|34|

Затем я использую strtok (), чтобы сократить его на substr = strtok(line, "|"); и сохраните их в массив строк с циклом, part[index] = substr;

Таким образом, часть [0] здесь должна быть «12», а часть [0] - «34» Я хотел бы использовать strtol, но я проверил, что он не может быть использован для строкового литерала, затем я пытаюсь выполнить следующий код.

char *temp = strdup(part[1]);
char **ptr;
long ret = strtol(temp, ptr, 10);
printf("%x\n", ret);

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

Ответы [ 6 ]

0 голосов
/ 02 ноября 2018

Абсолютно нет необходимости использовать strtok(), потому что strtol() устанавливает указатель, указываемый вторым параметром, для указания на символ, следующий за проанализированным числом.

Полный пример программы:

#define  _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

int main(void)
{
    char    *line_ptr = NULL;
    size_t   line_max = 0;
    ssize_t  line_len;

    long    *number      = NULL;
    size_t   numbers     = 0;
    size_t   numbers_max = 0;

    char    *curr, *next, *ends;
    long     temp;
    size_t   i;

    while (1) {

        line_len = getline(&line_ptr, &line_max, stdin);
        if (line_len < 1)
            break;

        curr = line_ptr;
        ends = line_ptr + line_len;

        numbers = 0;
        while (1) {

            /* Parse next long. */
            next = curr;
            errno = 0;
            temp = strtol(curr, &next, 0);
            if (errno)
                break;
            if (next == curr)
                break;

            /* Need to grow number array first? */
            if (numbers >= numbers_max) {
                size_t  temp_max = (numbers | 1023) + 1025 - 16;
                long   *temp_ptr;

                temp_ptr = realloc(number, temp_max * sizeof number[0]);
                if (!temp_ptr) {
                    fprintf(stderr, "Out of memory.\n");
                    exit(EXIT_FAILURE);
                }

                numbers_max = temp_max;
                number      = temp_ptr;
            }

            /* Save parsed number. */
            number[numbers++] = temp;

            /* Skip trailing whitespace, */
            curr = next;
            while (curr < ends && (*curr == '\t' || *curr == '\n' || *curr == '\v' ||
                                   *curr == '\f' || *curr == '\r' || *curr == ' '))
                curr++;

            /* Skip separator. */
            if (*curr == '|')
                curr++;
            else
                break; /* No separator, so that was the final number. */
        }

        printf("Parsed %zu longs:", numbers);
        for (i = 0; i < numbers; i++)
            printf(" %ld", number[i]);
        printf("\n");
        fflush(stdout);
    }

    if (ferror(in)) {
        fprintf(stderr, "Error reading standard input.\n");
        exit(EXIT_FAILURE);
    }

    free(line_ptr);
    line_ptr = NULL;
    line_max = 0;

    free(number);
    number = NULL;
    numbers = 0;
    numbers_max = 0;

    return EXIT_SUCCESS;
}

Эта программа не имеет ограничений, кроме доступной памяти. длина строки или количество чисел, которые она хранит в массиве. Политика роста для массива чисел фанки (просто мой стиль); не стесняйтесь заменить его на что угодно. Просто убедитесь, что temp_max по крайней мере numbers + 1. Увеличение его означает, что вы выделяете больше сразу и, следовательно, делаете меньше «медленных» вызовов realloc ().

Внешний цикл while перебирает строки, считанные со стандартного ввода.

Внутренний цикл while анализирует длинные строки из этой строки, разделенные символом |. strtol() игнорирует начальные пробелы. В случае, если между числом и следующим символом канала есть пробел, мы должны явно пропустить это; Вы также можете использовать только while (curr < ends && isspace(*curr)) curr++; для этого.

Если вы хотите собрать все длинные значения в один массив, а не в строку, просто пропустите numbers = 0; перед внутренним циклом while. (И двигайте печать чисел после внешней петли while.)

Фактическое преобразование,

next = curr;
errno = 0;
temp = strtol(curr, &next, 0);
if (errno)
    break; /* errno == ERANGE; number too large in magnitude! */
if (next == curr)
    break; /* end of input, no number */

основывается на том факте, что если преобразуемое число слишком велико по величине, strtol() установит errno = ERANGE и вернет LONG_MIN (если число в строке было отрицательным) или LONG_MAX (если положительное ). Чтобы обнаружить это, мы должны сначала установить errno на ноль. Если строка пуста (или в строке указан случайный нулевой символ, \0), strtol() вернет 0 с next == curr.

0 голосов
/ 02 ноября 2018

Функция strtol(const char *str, char **str_end, int base); будет разыменовываться str_end и будет делать что-то вроде *str_end = one_after_end_of_parsed_long; Поэтому, когда вы передаете указатель типа char**, который не указывает на действительный объект указателя, который может быть изменен с помощью strtol, вы получите неопределенное поведение.

Вы бы лучше написали

char *ptr;  // space for taking on a pointer value
long ret = strtol(temp, &ptr, 10);

или (не предпочтительный вариант):

char **ptr = malloc(sizeof(char*));
long ret = strtol(temp, ptr, 10);
...
free(*ptr);
0 голосов
/ 02 ноября 2018
char **ptr;
long ret = strtol(temp, ptr, 10);

неправильно. ptr не инициализируется и не ссылается на что-либо полезное.

Второй параметр strtol() должен ссылаться на адрес фактического значения char *, в котором хранится адрес первого не преобразованного символа. Согласно 7.22.1.3 функции strtod, strtof и strtold стандарта C :

Указатель на последнюю строку хранится в объекте, на который указывает endptr, при условии, что endptr не является нулевым указателем.

Правильный код будет

char *endptr;
long ret = strtol(temp, &endptr, 10);

или

char *endptr;
char **ptr = &endptr;
long ret = strtol(temp, ptr, 10);

В этом случае после вызова strtol() значение в endptr будет адресом первого символа, который не был преобразован в результирующее значение long.

Если вам все равно, какой первый неконвертированный символ:

char **ptr;
long ret = strtol(temp, NULL, 10);
0 голосов
/ 02 ноября 2018

Здесь вы уже отделили свою строку. Таким образом, каждая строка содержит одно длинное число. Второй аргумент используется, чтобы узнать, где остановилось преобразование в строке. Если вам это не нужно, введите NULL

char *temp = strdup(part[1]);
long ret = strtol(temp, NULL, 10);
printf("%lx\n", ret);

Кроме того printf для длинного номера требуются различные флаги формата. Здесь lx для long hexadecimal.

0 голосов
/ 02 ноября 2018

Вы злоупотребляете strtol. Требуется char**, потому что он намерен установить char*, на который он указывает, на странице man:

Если endptr не равно NULL, strtol() сохраняет адрес первого недопустимого символа в *endptr.

Передавая его неинициализированным char**, вы вызываете неопределенное поведение, когда оно пытается разыменовать его. Измените код на:

char *ptr;  // Place to put the end ptr
long ret = strtol(temp, &ptr, 10);  // Pass address of the location it can set

В качестве альтернативы, если вы никогда не используете ptr, просто сделайте:

long ret = strtol(temp, NULL, 10);  // Don't care about end ptr; strtol won't set on NULL
0 голосов
/ 02 ноября 2018

проблема в том, что ptr не инициализирован. Поэтому, когда strtol пытается выполнить запись по адресу ptr, происходит сбой (или неопределенное поведение).

Вы должны передать действительный адрес указателя для хранения последнего необработанного символа, например:

char *ptr;
long ret = strtol(temp, &ptr, 10);

&ptr является действительным и указывает на место хранения автоматической переменной на ptr

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...