Самая странная ошибка сегментации - PullRequest
2 голосов
/ 24 октября 2010

Хорошо, в основном я пишу программу, которая берет две директории и обрабатывает их в зависимости от того, какие опции предоставлены. Дело в том, что это дает мне ошибки сегментации, когда я не вижу проблемы. Код, вызывающий проблему, следующий:

РЕДАКТИРОВАТЬ: обновить код, чтобы включить весь мой исходный файл

#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

Однако, если бы я попытался запустить программу вручную, используя ту же команду EXACT:

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

Я получаю ошибку сегментации между test1 и test2. Но если я добавлю / только к одному из каталогов или к обоим, то это снова работает. Есть идеи, ребята?

РЕДАКТИРОВАТЬ: source_collection.h в основном все вспомогательные исходные коды, пока они еще не реализованы, поэтому они не должны вызывать никаких проблем. OPTIONS - это поставляемая структура, поэтому она должна быть безошибочной. Текущий исходный код все еще находится в стадии разработки, поэтому все еще не хватает некоторого кода, а также некоторые закомментированные коды. По сути, в конце дня программа стремится взять n каталогов с опциями и синхронизировать каталоги.

Ответы [ 2 ]

9 голосов
/ 24 октября 2010

Вам нужно использовать 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];

Это полностью устраняет цикл и временные переменные!


Отвечая на вопросы в комментарии

[W] Что вы имеете в виду, когда говорите, что моя выделенная память протекает?

Ваш оригинальный код:

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

Первый оператор выделяет память для *tempPaths;второй затем перезаписывает (единственная ссылка) этот указатель указателем argv [optind], тем самым гарантируя, что вы не можете освободить выделенную память, а также гарантируя, что вы его не используете.Кроме того, если вы впоследствии попытаетесь освободить память, на которую указывает ... ну, на этом этапе это будет paths, а не tempPaths ... тогда вы пытаетесь освободить память, которая никогда не выделялась, что такжеПлохая вещь ™.

Также я не совсем понимаю, что вы имеете в виду под «копированием своих аргументов».Вы имеете в виду две директории, используемые для командной строки или для чего-то еще?

Ваш код создает копию аргументов (имен каталогов), передаваемых программе;пересмотренное решение, использующее strdup() (или примерно то же самое, что и strdup()), создает копию данных в argv[optind].Однако, если все, что вы собираетесь делать с данными, - это прочитать их без изменения, вы можете просто скопировать указатель, а не делать копию данных.(Даже если бы вы собирались изменить аргумент, если бы вы были осторожны, вы все равно могли бы использовать исходные данные - но тогда сделать копию безопаснее.)

Наконец-то не будет char ** paths = & 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;
}
0 голосов
/ 25 октября 2010

Вы определили, что ошибка происходит в одной из следующих строк:

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

Маловероятно, что в argv меньше argc записей, поэтому мы делаем вывод, что проблема заключается в выделении *tempPaths, поэтому tempPaths не может быть действительным указателем.

Поскольку вы не показываете, как инициализируется paths, копать глубже невозможно. Скорее всего, в paths меньше argc членов, поэтому ваш tempPaths++ заставляет вас пройти мимо последней записи.

...