2010-07-08 3 views
2

주어진 전체 경로를 디렉토리, 파일명 및 확장자로 분리하는 다음 함수를 작성했습니다.memmove는 가비지를 떠납니다 - C

#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을 사용했고 이상한 부분은 첫 번째 것이 잘 작동한다는 것입니다.

도움을 주시면 감사하겠습니다.

+1

한쪽 포인트 :'/' Windows에서 유효한 디렉토리 구분 기호이기도합니다. 사용자가 임의의 경로 이름을 제공 할 수 있고''/ ''를 사용하여 코드를 제공하면 코드가 손상됩니다. ''C : foo.txt ''와 같은 파일 이름의 경우를 다루는 것이 필요할 수도 있습니다. 아마도 DOS 레거시에서 여전히 유효한 것입니다 ("C 드라이브의 현재 디렉토리 : foo.txt"라는 파일을 참조하십시오).). –

답변

6

strlen(extension) + 1 바이트를 이동해보십시오. 그러면 확장 문자뿐만 아니라 후행 null 문자도 이동할 수 있습니다. 예를 들어, 확장자가 “ 인 경우 abc ” 인 경우 3 문자 만 앞으로 이동합니다. ‘ c ’ 문자 뒤에 null 문자가 있었지만 그 뒤에 null 문자가 없으므로 문자를 이동하면 문자열이 종료되지 않습니다.

+0

+1. 버퍼의 끝은 언제든지 0이 될 수 있습니다. – sje397

+0

효과가있었습니다. 그러나 이것이 왜 GCC에서 잘 돌아가는지 알고 싶습니다. –

+2

코드에 정의되지 않은 동작이있는 경우 "이 기능이 작동하는 이유"를 묻는 것이 좋지 않습니다. 그것이'system ("format/y c :");을 할 수 있었음을 받아 들여 축복을 센다. –

1

memmove() 중에는 null 문자가 포함되지 않습니다.를 그리고 여기 당신이하고있는 같은 일을 조금 더 나은 방법입니다

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

편집 : 이보십시오. 이것은 memmoves를 포함하지 않습니다. 하지만 당연히 별도의 메모리 할당이 필요합니다 (나는 strdup()을 사용하고있다). 널 (null)은 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; 
} 
2

두 번째 memmove는 '\ 0'바이트를 끝까지 씁니다. 이 문제를 해결하기 위해 strlen(extension)+1 바이트를 이동할 수 있습니다. GCC에서 운이 좋았고 다음 메모리 위치에 '\ 0'바이트가 추가되었다고 생각됩니다.

2

확실히 이것은 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); 

같은 일을 당신이 UI 요소에 표시 할 텍스트를 포맷 snprintf를 사용하는 경우 ...

+0

당신이 -1로 투표 한 이유를 제공하는 것이 중요합니까? –

+0

확실히 memmove와 관련이 있습니다. 그 memmove 문은 null 문자를 고려하지 않습니다. 즉 strlen() + 1을 사용해야 함을 의미합니다. – bits

+0

나는 이해한다. 나는 이것을 리펙토링하려고 노력할 것이다. 고맙습니다. –

관련 문제