Memmove оставляя мусор - C - PullRequest
2 голосов
/ 08 июля 2010

Я написал следующую функцию, чтобы разделить заданный полный путь на каталог, имя файла и расширение.

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

struct path_info {
    char *directory;
    char *filename;
    char *extension;
};

#ifdef WIN32
const char directory_separator[] = "\\";
#else
const char directory_separator[] = "/";
#endif

struct path_info* splitpath(const char *full_path)
{
    size_t length = strlen(full_path);
    struct path_info *p = (struct path_info*) malloc(sizeof(struct path_info) + length + 3);  /* Extra space for padding and shifting */
    if(p)
    {
        char *path = (char *) &p[1];    /* copy of the path */
        char *end = &path[length + 1];  
        char *extension;
        char *last_separator;

        /* copy the path */
        strcpy(path, full_path);
        *end = '\0';
        p->directory = end;
        p->extension = end;
        p->filename  = path;

        last_separator = strrchr(path, directory_separator[0]);    /* Finding the last directory separator */
        if(last_separator) {
            memmove(last_separator + 1, last_separator, strlen(last_separator));  /* inserting a directory separator where null terminator will be inserted */
            p->directory = path;
            *(++last_separator) = '\0';      /* Truncate the directory path */
            p->filename = ++last_separator;  /* Taking the remaining as file name */
        }

        /* Finding the extension starts from second character. This allows handling filenames 
           starts with '.' like '.emacs'.*/
        extension = strrchr(&p->filename[1], '.');
        if(extension) {

            /* shifting the bytes to preserve the extension */
            memmove(extension + 1, extension, strlen(extension));   /* problem happens here */
            p->extension = extension + 1;

            *extension = '\0';  /* Truncates the file name */
        }
    }
    return p;
}


int main(void)
{
    struct path_info *p = splitpath("C:\\my documents\\some.txt");
    printf("Directory : %s\n", p->directory);
    printf("Filename : %s\n", p->filename);
    printf("Extension : %s\n", p->extension);
    return 0;
}

Это хорошо работает для данного ввода в GCC. Но это не помогает MSVC, оставляя некоторые данные мусора в переменной extension. Я добавил комментарий о том, где что-то идет не так. Я не понимаю, почему memmove ведет себя по-другому на MSVC? Я использовал memmove в двух местах, и странная часть в том, что первый работает нормально.

Любая помощь будет оценена.

Ответы [ 4 ]

6 голосов
/ 08 июля 2010

Попробуйте переместить strlen(extension) + 1 байт, чтобы переместить не только символы расширения, но и завершающий нулевой символ. Например, если расширение было & ldquo; abc & rdquo ;, то вы перемещаете только на 3 символа вперед на один пробел. Возможно, после символа & lsquo; c & rsquo; после этого символа, но после смещения символов строка становится неопределенной, поэтому строка становится неопределенной.

2 голосов
/ 08 июля 2010

Уверен, это не имеет ничего общего с memmove, а скорее с остальной частью вашей строковой логики, которая беспорядочная и очень неэффективная.Вместо того, чтобы копировать в начале, почему бы просто не определить 3 части вашей строки и их соответствующие длины, а затем скопировать их в буфер назначения с правильными смещениями?

Или если вам просто нужно использовать результаты сprintf, даже не делайте копию!Просто определите длину и сделайте что-то вроде этого:

printf("Directory: %.*s\n", dir_len, full_pathname);
printf("Filename: %.s*\n", name_len, full_pathname+name_start);
printf("Extension: %.*s\n", ext_len, full_pathname+ext_start);

То же самое работает, если вы используете snprintf для форматирования текста для отображения в элементах пользовательского интерфейса ...

2 голосов
/ 08 июля 2010

Ваш второй memmove записывает поверх завершающего байта '\ 0'. Вы можете переместить strlen(extension)+1 байт, чтобы решить эту проблему. Я подозреваю, что в GCC вам повезло, и в следующей ячейке памяти оказался дополнительный байт '\ 0'.

1 голос
/ 08 июля 2010

Вы не включаете нулевой символ во время memmove ().Попробуйте это:

        memmove(last_separator + 1, last_separator, strlen(last_separator)+1); 
        memmove(extension + 1, extension, strlen(extension)+1);*/

РЕДАКТИРОВАТЬ: И вот немного лучший способ того же, что вы делаете.Это не касается записок.Но, конечно, вам понадобится отдельное выделение памяти (я использую strdup ()).Об нулях также заботятся в том же пространстве памяти, которое было выделено strdup ().

    struct path_info* splitpath1(const char *full_path)
{
    char * path = strdup(full_path);
    char * fileWithExt = strtok((char *)strrchr(path,'\\'),"\\");

    struct path_info *p = (struct path_info*) malloc(sizeof(struct path_info));  /* Extra space for padding and shifting */
    p->filename = strtok(fileWithExt, ".");
    p->extension = strtok(NULL, ".");

    strtok((char *)strchr(path,'\\'),"\\");
    p->directory = path;

    return p;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...