我不知道为什么我的代码中出现分段错误

时间:2019-03-01 19:31:06

标签: c segmentation-fault

在代码中,我尝试读取一个文件,然后将其与strtok分开,以获取目录的ID(由8个字符组成)以及要传递给目录的文件类型(A,B,C。 pdf)。然后,我使用功能系统执行相应的命令。我知道在txt文件中,它首先出现的是目录的ID,然后是文件的类型。我的编译没有任何问题,但是当我执行程序时,出现了分段错误,我也不知道为什么。

#include <stdio.h>
#include <stdlib.h>
#define _GNU_SOURCE
#include <unistd.h>
#include <ctype.h>
#include <stdbool.h>
#include <string.h>


int main(int argc, char* argv[]){

    char ch, str[999], id[8], command[25];
    FILE *fp;
    int i = 0;
    char *pch;

    fp = fopen("p1.text", "r");

    if (fp == NULL)
    {
        perror("Error while opening the file.\n");
        exit(EXIT_FAILURE);
    }

    while((ch = fgetc(fp)) != EOF){

        str[i] = ch;    
        i++;

    }
    pch = strtok(str, " ,.-\n");

    while(pch != NULL){
        if(isalpha(pch)){
            sprintf(command, "cp %s.pdf %s", pch, id);
            system(command);
            strcpy(command, "");
        }
        if(strlen(pch) == 8){
            strcpy(id, pch);
        }
        pch = strtok(NULL, " ,.-\n");
    }

    fclose(fp);

    return 0;
}

2 个答案:

答案 0 :(得分:1)

id变量不足以容纳它需要存储的值。

C中的字符串以null终止。因此,一个8字符串需要9字节的存储空间。 id只有8个元素,因此,当您将其复制到数组的末尾时,将其写入。在数组边界之外进行写操作会调用undefined behavior,在这种情况下,这会导致代码崩溃。

id的长度设为8而不是8:

int id[9];

您还没有正确存储fgetc的结果。您将ch声明为char,但是fgetc返回int。要将EOF与普通字符值区分开来是必需的。因此,将ch的类型更改为int

此外,通过一次读取整个文件,然后在循环中使用内部状态机来调用strtok来弄清楚您要使用的元素,这使事情变得比需要复杂的多阅读。

您可以使用fgets一次读一行,然后调用strtok一次以获得id,再调用一次pch来简化此操作:

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

int main()
{
    char line[100], command[100];
    FILE *fp;
    char *pch, *id;

    fp = fopen("p1.text", "r");

    if (fp == NULL)
    {
        perror("Error while opening the file.\n");
        exit(EXIT_FAILURE);
    }

    while (fgets(line, sizeof(line), fp)) {
        id = strtok(line, " \n");
        if (!id) continue;

        pch = strtok(NULL, " \n");
        if (!pch) continue;

        snprintf(command, sizeof(command), "cp %s.pdf %s", pch, id);
        //printf("command=%s\n", command);
        system(command);
    }

    fclose(fp);

    return 0;
}

答案 1 :(得分:0)

我将向您展示一个调试过程,您应该学习该过程,以便自己解决这些问题。我采取的步骤如下所示。请注意,我并不是在尝试解决您的代码中的所有可能问题。我只是隔离并解决了最初的问题。

您应该熟悉gdbvaligrind之类的调试工具。有时,在调试器下运行可执行文件会使问题“消失”。在这种情况下,诸如valgrind之类的内存访问检查工具非常有用。

我用valgrind运行了您的代码,它给了我以下输出:

在运行valgrind之前,应使用调试符号构建可执行文件。假设您使用的是gcc,请按照以下步骤操作。

jnorton@ubuntu:~/source$ gcc -ggdb fault.c 
jnorton@ubuntu:~/source$ ./a.out 
pch = this
Segmentation fault (core dumped)
jnorton@ubuntu:~/source$ clear

jnorton@ubuntu:~/source$ gcc -ggdb fault.c 
jnorton@ubuntu:~/source$ valgrind --leak-check=yes ./a.out
==6745== Memcheck, a memory error detector
==6745== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6745== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==6745== Command: ./a.out
==6745== 
pch = this
==6745== Invalid read of size 2
==6745==    at 0x108B25: main (fault.c:35)
==6745==  Address 0x2fda2c0 is not stack'd, malloc'd or (recently) free'd
==6745== 
==6745== 
==6745== Process terminating with default action of signal 11 (SIGSEGV)
==6745==  Access not within mapped region at address 0x2FDA2C0
==6745==    at 0x108B25: main (fault.c:35)
==6745==  If you believe this happened as a result of a stack
==6745==  overflow in your program's main thread (unlikely but
==6745==  possible), you can try to increase the size of the
==6745==  main thread stack using the --main-stacksize= flag.
==6745==  The main thread stack size used in this run was 8388608.
==6745== 
==6745== HEAP SUMMARY:
==6745==     in use at exit: 552 bytes in 1 blocks
==6745==   total heap usage: 3 allocs, 2 frees, 5,672 bytes allocated
==6745== 
==6745== LEAK SUMMARY:
==6745==    definitely lost: 0 bytes in 0 blocks
==6745==    indirectly lost: 0 bytes in 0 blocks
==6745==      possibly lost: 0 bytes in 0 blocks
==6745==    still reachable: 552 bytes in 1 blocks
==6745==         suppressed: 0 bytes in 0 blocks
==6745== Reachable blocks (those to which a pointer was found) are not shown.
==6745== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==6745== 
==6745== For counts of detected and suppressed errors, rerun with: -v
==6745== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
jnorton@ubuntu:~/source$ 

valgrind通知已报告:

==6745==    at 0x108B25: main (fault.c:35)

我添加了一些调试printfs,但第35行是:

if(isalpha(pch)){

此处发生segfault是因为isalpha()接受一个int参数,而不是指向字符串的指针。将该行更改为以下行将停止segfault

if(isalpha((int)pch[0])){

现在,这只是代码中的一个问题。您可能还有其他问题。