在 C 中对二维数组使用 Strtok() 的分段错误

Segmentation Fault using Strtok() for 2d array in C

我在尝试将诸如“1,2,3;4,5,6;7,8,9”之类的字符串拆分为二维数组时不断出现分段错误为什么此代码会不断发生有人可以为我解释一下吗?

int main(void){
    char string[100];
    char *token;
    char *end;
    int num;
    int row_counter;
    int column_counter;
    int counter_position;
    char *rows[100];
    int square_ints[40][40];

    row_counter = 0;
    column_counter = 0;
    string[] = "1,2,3;4,5,6;7,8,9";
    token = strtok(string, ";");
    rows[row_counter] = token;
    row_counter++;
    while (token != NULL){
        token = strtok(NULL, ";");
        rows[row_counter] = token;
        row_counter++;
    }

    counter_position = row_counter;

    for (row_counter = 0; row_counter < counter_position; row_counter++) {
        token = strtok(rows[row_counter], ",");
        num = strtol(token, &end, 10);
        square_ints[row_counter][column_counter] = num;
        printf("%d\n", square_ints[row_counter][column_counter]);
        column_counter++;
        while (token != NULL) {
            token = strtok(NULL, ",");
            num = strtol(token, &end, 10);
            square_ints[row_counter][column_counter] = num;
            printf("%d\n", square_ints[row_counter][column_counter]);
            column_counter++;
        }
    }
}

应该打印:1 2 3 4 5 6 7 8 9 而我却得到 1 2 3 Segmentation Fault

扩展 MikeCAT 提到的内容:

作为 for 循环的开始,您可以:

token = strtok(rows[row_counter], ",");
num = strtol(token, &end, 10);

稍后你做:

token = strtok(NULL, ",");
num = strtol(token, &end, 10);

问题是,在这两种情况下,strtok都会returnNULL,所以token的值为NULL。当您将 token 传递给 strtol 时,它会尝试 use/dereference 并产生段错误。

因此,要修复,在每次 strtok 调用后,您需要添加:

if (token == NULL)
    break;

这是更正后的代码。我添加了一些调试 printf,所以你可以看到问题。

我使用 cpp 条件来区分旧代码和新代码:

#if 0
// old code
#else
// new code
#endif

#if 1
// new code
#endif

总之,就在这里:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef DEBUG
#define dbgprt(_fmt...) \
    do { \
        printf(_fmt); \
    } while (0)
#else
#define dbgprt(_fmt...) \
    do { \
    } while (0)
#endif

int
main(void)
{
#if 0
    char string[100];
#else
    char string[] = "1,2,3;4,5,6;7,8,9";
#endif
    char *token;
    char *end;
    int num;
    int row_counter;
    int column_counter;
    int counter_position;
    char *rows[100];
    int square_ints[40][40];

    row_counter = 0;
    column_counter = 0;
#if 0
    string[] = "1,2,3;4,5,6;7,8,9";
#endif
    token = strtok(string, ";");
    rows[row_counter] = token;
    row_counter++;
    while (token != NULL) {
        token = strtok(NULL, ";");
        rows[row_counter] = token;
        row_counter++;
    }

    counter_position = row_counter;

    for (row_counter = 0; row_counter < counter_position; row_counter++) {
        dbgprt("DEBUG: rows[%d]='%s'\n",row_counter,rows[row_counter]);
        token = strtok(rows[row_counter], ",");
        dbgprt("DEBUG: token='%s'\n",token);
#if 1
        if (token == NULL)
            break;
#endif
        num = strtol(token, &end, 10);
        square_ints[row_counter][column_counter] = num;
        printf("%d\n", square_ints[row_counter][column_counter]);
        column_counter++;
        while (token != NULL) {
            token = strtok(NULL, ",");
            dbgprt("DEBUG2: token='%s'\n",token);
#if 1
            if (token == NULL)
                break;
#endif
            num = strtol(token, &end, 10);
            square_ints[row_counter][column_counter] = num;
            printf("%d\n", square_ints[row_counter][column_counter]);
            column_counter++;
        }
    }

    return 0;
}

请注意,虽然上面的代码有效,但它是在给定循环之前和循环内部复制代码。如果我们添加一个额外的指针变量(例如bp),我们可以稍微简化代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main(void)
{
    char string[] = "1,2,3;4,5,6;7,8,9";
    char *token;
    char *end;
    int num;
    int row_counter;
    int column_counter;
    int counter_position;
    char *bp;
    char *rows[100];
    int square_ints[40][40];

    row_counter = 0;
    column_counter = 0;

    bp = string;
    while (1) {
        token = strtok(bp,";");
        bp = NULL;
        if (token == NULL)
            break;
        rows[row_counter] = token;
        row_counter++;
    }

    counter_position = row_counter;

    for (row_counter = 0; row_counter < counter_position; row_counter++) {
        bp = rows[row_counter];
        if (bp == NULL)
            break;

        while (1) {
            token = strtok(bp, ",");
            bp = NULL;
            if (token == NULL)
                break;

            num = strtol(token, &end, 10);
            square_ints[row_counter][column_counter] = num;
            printf("%d\n", square_ints[row_counter][column_counter]);
            column_counter++;
        }
    }

    return 0;
}

还有一个类似的函数 strsep 被一些人认为是“正确的 strtok”。这是使用该功能的版本:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main(void)
{
    char string[] = "1,2,3;4,5,6;7,8,9";
    char *token;
    char *end;
    int num;
    int row_counter;
    int column_counter;
    int counter_position;
    char *bp;
    char *rows[100];
    int square_ints[40][40];

    row_counter = 0;
    column_counter = 0;

    bp = string;
    while (1) {
        token = strsep(&bp,";");
        if (token == NULL)
            break;
        rows[row_counter] = token;
        row_counter++;
    }

    counter_position = row_counter;

    for (row_counter = 0; row_counter < counter_position; row_counter++) {
        bp = rows[row_counter];
        if (bp == NULL)
            break;

        while (1) {
            token = strsep(&bp,",");
            if (token == NULL)
                break;

            num = strtol(token, &end, 10);
            square_ints[row_counter][column_counter] = num;
            printf("%d\n", square_ints[row_counter][column_counter]);
            column_counter++;
        }
    }

    return 0;
}