代码之家  ›  专栏  ›  技术社区  ›  Navaneeth K N

Memmove离开垃圾-C

  •  2
  • Navaneeth K N  · 技术社区  · 15 年前

    我编写了以下函数,将给定的完整路径拆分为目录、文件名和扩展名。

    #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上的行为是否不同?我已经用过 默默斯 在两个地方,奇怪的是,第一个工作得很好。

    任何帮助都将不胜感激。

    4 回复  |  直到 15 年前
        1
  •  6
  •   dreamlax    15 年前

    尝试移动 strlen(extension) + 1 字节,这样不仅可以移动扩展字符,还可以移动后面的空字符。例如,如果扩展名是“abc”,那么您只能向前移动3个字符。“c”字符后面可能有一个空字符,但之后没有空字符,因此当您移动字符时,字符串将变为未终止。

        2
  •  2
  •   Daniel Stutzbach Edward Leno    15 年前

    您的第二个memmove在终止的\0'字节上写入。你可以移动 strlen(extension)+1 字节来解决这个问题。我怀疑在GCC上你很幸运,并且在下一个内存位置上碰巧有一个额外的'\0'字节。

        3
  •  2
  •   R.. GitHub STOP HELPING ICE    15 年前

    很肯定,这与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 要格式化文本以在UI元素中显示…

        4
  •  1
  •   bits    15 年前

    memmove()时不包括空字符。 试试这个:

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

    编辑: 这里有一个更好的方法来处理你正在做的事情。这不涉及memmoves。当然,您需要单独的内存分配(我使用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;
    }