最奇怪的分段错误

时间:2010-10-24 18:37:59

标签: c bash segmentation-fault

好吧基本上我正在编写一个程序,它接收两个目录并根据提供的选项处理它们。当我没有看到问题时,它会给我分段错误。导致问题的代码如下:

编辑:更新代码以包含我的整个源文件

#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>

#define OPTLIST "am:npruv"
#define DEFAULT_MOD_TIMES 1

typedef struct
{
    /* Boolean toggle to indicate whether hidden files should be
       processed or not */
    bool processHiddens;
    /* Integer number of seconds such that files with modification
       times that differ by less than this value are considered the
       same */
    int timeResolution;
    /* Boolean toggle to indicate whether the actual synchronisation
       (file creation and overwriting) should be performed or not */
    bool performSync;
    /* Boolean toggle to indicate whether subdirectories should be
       recursively processed or not */
    bool recursive;
    /* Boolean toggle to indicate whether to print the combined
       directory structure or not */
    bool print;
    /* Boolean toggle to indicate whether modification times and
       permissions should be updated or not */
    bool updateStatus;
    /* Boolean toggle to indicate whether verbose output should be
       printed or not */
    bool verbose;
    /* The name of the executable */
    char *programname;
} OPTIONS;

int main(int argc, char *argv[])
{
    static OPTIONS options;
    //static TOPLEVELS tls;
    int opt;
    char **paths;

    /*
     * Initialise default without options input.
     * Done the long way to be more verbose.
     */
    opterr = 0;
    options.processHiddens = false;
    options.timeResolution = DEFAULT_MOD_TIMES;
    options.performSync = true;
    options.recursive = false;
    options.print = false;
    options.updateStatus = true;
    options.verbose = false;
    options.programname = malloc(BUFSIZ);
    options.programname = argv[0];

    /*
     * Processing options.
     */
    while ((opt = getopt(argc, argv, OPTLIST)) != -1)
    {
        switch (opt)
        {
            case 'a':
                options.processHiddens = !(options.processHiddens);
                break;
            case 'm':
                options.timeResolution = atoi(optarg);
                break;
            case 'n':
                options.performSync = !(options.performSync);
                break;
            case 'p':
                options.print = !(options.print);
                break;
            case 'r':
                options.recursive = !(options.recursive);
                break;
            case 'u':
                options.updateStatus = !(options.updateStatus);
                break;
            case 'v':
                options.verbose = !(options.verbose);
                break;
            default:
                argc = -1;
        }
    }

    /*
     * Processing the paths array for top level directory.
     */
    char **tempPaths = paths;
    while (optind < argc)
    {
        *tempPaths++ = argv[optind++];
    }

    if (argc -optind + 1 < 3)
    {
        fprintf(stderr, "Usage: %s [-amnpruv] dir1 dir2 [dirn ... ]\n", options.programname);
        exit(EXIT_FAILURE);
    }
    else
    {
        //processTopLevelDirectories(tls, paths, nDirs, options);
        exit(EXIT_SUCCESS);
    }

    return 0;
}

我有一个bash脚本,运行时执行以下操作:

#!/bin/bash
clear
echo Running testing script
echo Removing old TestDirectory
rm -r ./TD
echo Creating new copy of TestDirectory
cp -r ./TestDirectory ./TD
echo Building program
make clean
make
echo Running mysync
./mysync ./TD/Dir1 ./TD/Dir2
echo Finished running testing script

但是,如果我尝试使用完全相同的命令手动运行程序:

./mysync ./TD/Dir1 ./TD/Dir2

我在test1和test2之间遇到了分段错误。但是,如果我要将/附加到任何一个目录,或两者,那么它再次起作用。任何想法的家伙?

编辑:source_collection.h基本上都是所有支持源代码,到目前为止它们还没有实现,所以不应该导致任何问题。 OPTIONS是一个提供的结构,因此它应该没有错误。当前的源代码仍在进行中,因此仍然会丢失一些代码以及注释掉一些代码。基本上在一天结束时,程序的目标是使用选项获取n个目录并同步目录。

2 个答案:

答案 0 :(得分:9)

您需要使用strcpy()argv[optind]复制到您刚刚分配的*tempPaths空间。

实际上,你正在摧毁(泄漏)你分配的内存,然后谁知道还有什么问题。

另外,为什么你需要复制你的论点?如果您不修改它们,则无需复制它们。


char **tempPaths = paths;
while (optind < argc)
{
    printf("test1\n");
    // Or use strdup(), but strdup() is POSIX, not Standard C
    // This wastes less space on short names and works correctly on long names.
    *tempPaths = malloc(strlen(argv[optind])+1);
    // Error check omitted!
    strcpy(*tempPaths, argv[optind]);
    printf("test2\n");
    printf("%s\n", *tempPaths);
    tempPaths++;
    optind++;
}

这假设您确实需要复制您的参数。如果您不需要修改命令行参数,则只需使用:

char **tempPaths = paths;
while (optind < argc)
    *tempPaths++ = argv[optind++];

当我编辑删除不再需要的内容时,那里的代码就消失了......

甚至可以简单地设置:

char **paths = &argv[optind];

这完全取消了循环和临时变量!


回答评论中的问题

  当你说我分配的内存泄漏时,你的意思是什么?

您的原始代码是:

*tempPaths = malloc(BUFSIZ);
*tempPaths = argv[optind];

第一个语句将内存分配给* tempPaths;第二个然后用指针argv [optind]覆盖指针(唯一的引用),从而确保你不能释放分配的内存,并确保你不使用它。此外,如果您随后尝试释放...指向的内存,那么在此阶段它将是paths而不是tempPaths ...然后您尝试释放从未分配的内存,这也是一件坏事™。

  

此外,我并不特别明白你的意思是“复制你的论点”。您是指的是用于命令行的两个目录还是其他目录?

您的代码正在复制传递给程序的参数(目录名称);使用strdup()(或大致是strdup()的主体)的修订解决方案会在argv[optind]中复制数据。但是,如果您要对数据进行所有操作而不更改它,则可以简单地复制指针,而不是复制数据。 (即使你要修改参数,如果你小心,你仍然可以使用原始数据 - 但是制作副本更安全。)

  

最后不会char ** paths =&amp; argv [optind];只给我一个目录,就是这样吗?

没有;它会给你一个指向以空字符结尾的字符串指针列表的指针,你可以单步执行:

for (i = 0; paths[i] != 0; i++)
    printf("name[%d] = %s\n", i, paths[i]);

裸露的最小工作代码

如评论中所述,扩展崩溃代码的基本问题(除了我们没有标题的事实)是paths变量未初始化为指向任何内容。毫无疑问,代码会崩溃。

基于修改后的示例 - 选项处理被删除:

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

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

    optind = 1;
    paths = &argv[optind];

    if (argc - optind + 1 < 3)
    {
        fprintf(stderr, "Usage: %s dir1 dir2 [dirn ... ]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    else
    {
        char **tmp = paths;
        while (*tmp != 0)
            printf("<<%s>>\n", *tmp++);
    }

    return 0;
}

此版本进行内存分配:

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

int main(int argc, char *argv[])
{
    optind = 1;

    if (argc - optind + 1 < 3)
    {
        fprintf(stderr, "Usage: %s dir1 dir2 [dirn ... ]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    else
    {
        int npaths = argc - optind;
        char **paths = malloc(npaths * sizeof(*paths));
        // Check allocation!
        char **tmp = paths;
        int i;
        printf("n = %d\n", npaths);
        for (i = optind; i < argc; i++)
        {
            *tmp = malloc(strlen(argv[i])+1);
            // Check allocation!
            strcpy(*tmp, argv[i]);
            tmp++;
        }
        for (i = 0; i < npaths; i++)
            printf("<<%s>>\n", paths[i]);
    }

    return 0;
}

答案 1 :(得分:0)

您已确定在以下某行中发生了段错误:

*tempPaths = malloc(BUFSIZ);
*tempPaths = argv[optind];

argc中的条目少于argv次的可能性极小,因此我们推断问题是*tempPaths的分配,因此tempPaths无法生效指针。

由于您没有展示paths如何初始化,因此无法深入挖掘。最有可能argc中的成员少于paths,因此您的tempPaths++会导致您移动到最后一个条目。