在字符串文字上使用strtol导致分段错误

时间:2018-11-02 10:28:29

标签: c linux strtok strtol

我有一个getline()得到的字符串(更精确地说,我使用循环和getline逐行读取文件)

让我们说这行是12|34|

然后,我使用strtok()将其削减substr = strtok(line, "|"); 并将它们存储到带有循环part[index] = substr;

的字符串数组中

因此,此处的part [0]应该为“ 12”,part [0]为“ 34” 我想使用strtol,但是我检查了它是否不能在字符串文字上使用,然后尝试执行以下代码。

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

当我读第二行时,它会导致分段错误。通过如何真正使用strtol将字符串转换为整数

6 个答案:

答案 0 :(得分:3)

问题是ptr尚未初始化。因此,当strtol尝试在地址ptr上写入时,它将崩溃(或未定义的行为)。

您必须传递指针的 valid 地址以存储最后一个未处理的字符,例如:

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

&ptr是有效的,它指向ptr的自动变量存储位置

答案 1 :(得分:3)

您滥用c = g._legend.get_children()[0].get_children()[1].get_children()[0] c._children = c._children[1:] 。之所以需要strtol是因为它打算按手册页设置它指向的char**

  

如果char*不是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

答案 2 :(得分:3)

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

是错误的。 ptr未初始化,并且没有引用任何有用的东西。

strtol()的第二个参数必须引用存储第一个未转换字符的地址的实际char *值的地址。根据{{​​3}}:

  

指向最终字符串的指针存储在所指向的对象中   endptr,前提是endptr不是空指针。

正确的代码应为

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

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

在这种情况下,在调用strtol()之后,endptr中的值将是转换为结果{{1 }}值。

如果您不关心第一个未转换的字符是什么

long

答案 3 :(得分:2)

这里您已经分隔了字符串。 因此,每个字符串包含一个长数字。第二个参数用于了解转换在字符串中的停止位置。 如果不需要,请传递TypeError: Cannot read property 'length' of null at http.js:109 at Array.forEach (<anonymous>) at HttpHeaders.lazyInit (http.js:103) at HttpHeaders.push../node_modules/@angular/common/fesm5/http.js.HttpHeaders.init

NULL

此外,长号的char *temp = strdup(part[1]); long ret = strtol(temp, NULL, 10); printf("%lx\n", ret); 需要不同的格式标志。这里printf代表lx

答案 4 :(得分:2)

函数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);

答案 5 :(得分:0)

完全没有必要使用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设置为零。如果字符串为空(或该行中有一个零散的nul字符\0),则strtol()将返回0,并带有next == curr