【问题标题】:Why is this segfaulting? strtok?为什么这是段错误?斯特克?
【发布时间】:2014-10-06 15:16:02
【问题描述】:

所以我很好奇为什么以下代码段不断出现段错误。在我看来是正确的。

int * addCoins(char *val){
    const char *deli = ",";
    char *ptr =NULL;

    char *denomination = strtok_r(val, deli, &ptr);
    char *count = strtok_r(NULL, deli, &ptr);
    int deno = atoi(denomination);
    int cnt = atoi(count);
    int *k;
    k = malloc(sizeof(*k)*2);
    k[0] = deno;
    k[1] =cnt;

    return k;
}

在 main 中调用 addCoins 函数。我认为问题不在于这里,但老实说,我对这个问题有点不知所措。

char* fileNameCoin = argv[2];
FILE *fileCoin;
fileCoin = fopen(fileNameCoin, "r+");
char bufCoin[256];
int i = 0;
//vmNode->next = NULL;
int *j;
while (fgets(bufCoin, sizeof bufCoin, fileCoin) != NULL) {
    j = addCoins(bufCoin);
    int deno = j[0];
    switch(deno){
        case 5:
            Coins[i].denom = j[0];
            Coins[i].count = j[1];
            break;
        case 10:
            Coins[i].denom = j[0];
            Coins[i].count = j[1];
            break;
        case 20:
            Coins[i].denom = j[0];
            Coins[i].count = j[1];
            break;
        case 50:
            Coins[i].denom = j[0];
            Coins[i].count = j[1];
            break;
        case 100:
            Coins[i].denom = j[0];
            Coins[i].count = j[1];
            break;
        case 200:
            Coins[i].denom = j[0];
            Coins[i].count = j[1];
            break;
        case 500:
            Coins[i].denom = j[0];
            Coins[i].count = j[1];
            break;
        case 1000:
            Coins[i].denom = j[0];
            Coins[i].count = j[1];
            break;
        default:
            break;
    }

    i++;
}

以下是文件的定义方式

1000,3
500,4
200,20
100,30
50,5
20,3
10,40
5,20

其中第一个数字是以美分为单位的面额,第二列是所述面额的数字。

这些是 typedef:

/* The different denominations of coins available */
enum denomination
{
    FIVE_CENTS, TEN_CENTS, TWENTY_CENTS, FIFTY_CENTS, ONE_DOLLAR,
    TWO_DOLLARS, FIVE_DOLLARS, TEN_DOLLARS
};

/* Each coin in the coins array will have a denomination (20 cents,
 * 50 cents, etc) and a count - how many of that coin do we have on hand
 */
struct coin
{
    enum denomination denom;
    unsigned count;
};

【问题讨论】:

  • 段错误到底发生在哪里?
  • 发生在 addCoins 函数中,在 strtok_r 行。
  • 你像筛子一样泄漏内存;您应该在循环末尾包含free(j);。你可以折叠你的switch,这样case 5: case 10: case 20: case 50: case 100: case 200: case 500: case 100: Coins[i].denom = j[0]; Coins[i].count = j[1]; break;。我不确定简单地忽略错误数据是个好主意。也报错。并检查来自strtok_r() 的返回值——但使用它而不是strtok() 做得很好。文件打开成功的检查在哪里?
  • 您可能希望使用符号编译并使用调试器。
  • "发生在 addCoins 函数中,在 strtok_r 行。" 有两行! 叹息

标签: c segmentation-fault strtok


【解决方案1】:

老实说,您应该通过typedef 更好地使用structs。示例(使用exename in.txt 调用):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* The different denominations of coins available */
enum denomination {
    FIVE_CENTS, TEN_CENTS, TWENTY_CENTS, FIFTY_CENTS, ONE_DOLLAR,
    TWO_DOLLARS, FIVE_DOLLARS, TEN_DOLLARS
};

typedef struct {
    enum denomination denom;
    unsigned int count;
} coin;

coin * addCoins(char *val) {
    coin *k = malloc(sizeof(coin));
    if( sscanf( val, "%d,%d", &(k->denom), &(k->count)) != 2 ) {
      fprintf(stderr,"Two int values not found on line '%s' in input.\n", val);
      free(k);
      k=NULL;
    }
    return k;
} 

int main(int argc, char *argv[])
{
    coin Coins[100]={0};
    char* fileNameCoin = argv[1];
    FILE *fileCoin = fopen(fileNameCoin, "r+");
    char bufCoin[256];
    int i = 0;
    coin *j;
    if( fileCoin ) {
      while(fgets(bufCoin, sizeof bufCoin, fileCoin) != NULL) {
        j = addCoins(bufCoin);                                                                                       
        if( j ) { // only add if 2 int values found on input line
          Coins[i] = *j;
          free(j);
          printf("c: %d, %d\n", Coins[i].denom, Coins[i].count);
          i++;
        } 
      }
      fclose(fileCoin);
    }
    else {
      fprintf(stderr,"Unable to open file %s for input.\n",fileNameCoin);
    }
}

【讨论】:

    【解决方案2】:

    您已经对代码进行了更彻底的重新实现,这实际上非常好。这是一组应用于您的原始代码的清理;它表明程序中存在的任何问题都不在显示的代码中。

    #define _XOPEN_SOURCE 700
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    enum denomination
    {
        FIVE_CENTS, TEN_CENTS, TWENTY_CENTS, FIFTY_CENTS, ONE_DOLLAR,
        TWO_DOLLARS, FIVE_DOLLARS, TEN_DOLLARS
    };
    
    struct coin
    {
        enum denomination denom;
        unsigned count;
    };
    
    static struct coin Coins[10];
    
    static
    int *addCoins(char *val)
    {
        const char *deli = ",";
        char *ptr = NULL;
    
        char *denomination = strtok_r(val, deli, &ptr);
        char *count = strtok_r(NULL, deli, &ptr);
        printf("D = %s, C = %s", denomination, count);
        int deno = atoi(denomination);
        int cnt = atoi(count);
        printf("D = %d, C = %d\n", deno, cnt);
        int *k = malloc(sizeof(*k) * 2);
        if (k == 0)
        {
            fprintf(stderr, "Failed to allocate memory\n");
            exit(1);
        }
        k[0] = deno;
        k[1] = cnt;
    
        return k;
    }
    
    int main(int argc, char **argv)
    {
        if (argc != 2)
        {
            fprintf(stderr, "Usage: %s file\n", argv[0]);
            return 0;
        }
        char *fileNameCoin = argv[1];
        FILE *fileCoin = fopen(fileNameCoin, "r+");
        if (fileCoin == 0)
        {
            fprintf(stderr, "%s: failed to open file %s\n", argv[0], argv[1]);
            return 0;
        }
        char bufCoin[256];
        int i = 0;
        while (fgets(bufCoin, sizeof bufCoin, fileCoin) != NULL)
        {
            int *j = addCoins(bufCoin);
            int deno = j[0];
            switch (deno)
            {
            case 5:
            case 10:
            case 20:
            case 50:
            case 100:
            case 200:
            case 500:
            case 1000:
                Coins[i].denom = j[0];
                Coins[i].count = j[1];
                printf("(%d,%d)\n", Coins[i].denom, Coins[i].count);
                i++;
                break;
            default:
                fprintf(stderr, "Unrecognized coin denomination (%d,%d)\n", j[0], j[1]);
                break;
            }
            free(j);
        }
        return 0;
    }
    

    编译:

    gcc -g -O3 -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
        -Wold-style-definition -Wold-style-declaration -Werror coins.c -o coins
    

    #define _XOPEN_SOURCE 700 是声明 strtok_r() 所必需的。

    示例运行:

    D = 1000, C = 3
    D = 1000, C = 3
    (1000,3)
    D = 500, C = 4
    D = 500, C = 4
    (500,4)
    D = 200, C = 20
    D = 200, C = 20
    (200,20)
    D = 100, C = 30
    D = 100, C = 30
    (100,30)
    D = 50, C = 5
    D = 50, C = 5
    (50,5)
    D = 20, C = 3
    D = 20, C = 3
    (20,3)
    D = 10, C = 40
    D = 10, C = 40
    (10,40)
    D = 5, C = 20
    D = 5, C = 20
    (5,20)
    

    【讨论】:

    • 是的,事实证明我在寻找错误的错误位置。它位于更下方的地方,我对列表的容器有一个狡猾的暗示。
    • 调试器和打印语句都可以有效地找到崩溃发生的地方。当您找到了崩溃的正确位置时,您就可以开始寻找原因了。再现性是首先,然后是本地化,然后找到原因,然后解决问题,然后重新测试。
    • 是的,我通常是这样处理的。问题是 addcoin 函数中发生了崩溃,但它是由其他原因引起的。至于调试器,我得先熟悉一下gdb。因为我还没有找到一个合理的调试器,它有一个漂亮的 ui for mint。有什么建议吗?
    • 我使用 GDB;我怀疑这是否称得上是“一个不错的用户界面”。我不使用 IDE;我还没有找到一个比它更有帮助的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多