在 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;
}
我在尝试将诸如“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;
}