2010-06-29 97 views
6

作爲學習C的一部分,我編寫了以下代碼以將目錄名稱與文件名稱組合在一起。例如:combine("/home/user", "filename")將導致/home/user/filename。這個功能預計可以跨平臺使用(至少在所有流行的Linux發行版以及Windows 32和64位上)。組合目錄和文件路徑 - C

這是代碼。

const char* combine(const char* path1, const char* path2) 
{ 
    if(path1 == NULL && path2 == NULL) { 
     return NULL; 
    } 

    if(path2 == NULL || strlen(path2) == 0) return path1; 
    if(path1 == NULL || strlen(path1) == 0) return path2; 

    char* directory_separator = ""; 
#ifdef WIN32 
    directory_separator = "\\"; 
#else 
    directory_separator = "/"; 
#endif 

    char p1[strlen(path1)];     // (1) 
    strcpy(p1, path1);       // (2) 
    char *last_char = &p1[strlen(path1) - 1]; // (3) 

    char *combined = malloc(strlen(path1) + 1 + strlen(path2)); 
    int append_directory_separator = 0; 
    if(strcmp(last_char, directory_separator) != 0) { 
     append_directory_separator = 1; 
    } 
    strcpy(combined, path1); 
    if(append_directory_separator) 
     strcat(combined, directory_separator); 
    strcat(combined, path2); 
    return combined; 
} 

我對以上代碼有以下問題。

  1. 考慮編號爲1,2,3的行。所有這3行都用於獲取字符串中的最後一個元素。看起來我正在爲這麼小的事情寫更多的代碼。從char*字符串獲取最後一個元素的正確方法是什麼?
  2. 要返回結果,我使用malloc分配新字符串。我不確定這是做到這一點的正確方法。來電者是否期望釋放結果?我怎麼能指出來電者他必須釋放結果?有沒有更容易出錯的方法?
  3. 你如何評價代碼(差,平均,好)?哪些領域可以被強化?

任何幫助將是偉大的。

編輯

固定討論的所有問題和實施的更改建議。這是更新的代碼。

void combine(char* destination, const char* path1, const char* path2) 
{ 
    if(path1 == NULL && path2 == NULL) { 
     strcpy(destination, "");; 
    } 
    else if(path2 == NULL || strlen(path2) == 0) { 
     strcpy(destination, path1); 
    } 
    else if(path1 == NULL || strlen(path1) == 0) { 
     strcpy(destination, path2); 
    } 
    else { 
     char directory_separator[] = "/"; 
#ifdef WIN32 
     directory_separator[0] = '\\'; 
#endif 
     const char *last_char = path1; 
     while(*last_char != '\0') 
      last_char++;   
     int append_directory_separator = 0; 
     if(strcmp(last_char, directory_separator) != 0) { 
      append_directory_separator = 1; 
     } 
     strcpy(destination, path1); 
     if(append_directory_separator) 
      strcat(destination, directory_separator); 
     strcat(destination, path2); 
    } 
} 

在新版本中,調用者來分配足夠的緩衝區,發送給combine方法。這避免了使用mallocfree問題。這是用法

int main(int argc, char **argv) 
{ 
    const char *d = "/usr/bin"; 
    const char* f = "filename.txt"; 
    char result[strlen(d) + strlen(f) + 2]; 
    combine(result, d, f); 
    printf("%s\n", result); 
    return 0; 
} 

有關更多改進的建議嗎?

回答

4

而且有內存泄漏:

const char *one = combine("foo", "file"); 
const char *two = combine("bar", ""); 
//... 
free(one); // needed 
free(two); // disaster! 

編輯:你的新代碼看起來更好。一些小的風格的變化:在管線4

    • 雙分號;;在第6行,用path2[0] == '\0''或只是!path2[0]strlen(path2) == 0替換。
    • 管線同樣9.
    • 移除循環確定last_char,並使用const char last_char = path1[strlen(path1) - 1];
    • 變化if(append_directory_separator)if(last_char != directory_separator[0])。所以你不再需要變量append_directory_separator
    • 有你的函數也返回destination,類似於strcpy(dst, src),它返回dst

    編輯:那麼你的迴路last_char錯誤:它總是返回path1結束,所以你可以以雙斜線結束//在你的答案。 (但Unix會將其視爲單斜槓,除非它在開始時)。無論如何,我的建議修復了這個問題 - 我看到的和jdmichal的答案非常相似。 我看到你的原始碼中有這個錯誤(我承認我只看了一眼 - 這對我來說太複雜了,你的新代碼要好得多)。

    ,另外兩個,稍微比較主觀意見:

    • 我會用stpcpy(),避免strcat()的低效率。 (如果需要,易於編寫自己的。)
    • 有些人對strcat()等有很強烈的意見,認爲是不安全。不過,我認爲你在這裏的用法非常好。
  • +0

    好點。我意識到泄漏。但是,對我而言,這是一場新的災難。你能解釋一下如何解決問題嗎? – 2010-06-29 16:37:04

    +1

    @Appu:'free(two)'和'free(「bar」)'是一樣的。這可能不是_disaster_。但它是不確定的。 – 2010-06-29 16:44:27

    +0

    既然你快捷,如果其他參數是空的,返回'path1'或'path2',用戶不知道它們是否應該釋放返回的值。接得好。 – jdmichal 2010-06-29 18:17:00

    -1

    看一眼顯示:

    1. 您正在使用C++註釋(//)這是不是標準的C
    2. 您聲明變量下的部分代碼的方式 - 也沒有C.它們應該被定義在函數的開始處。
    3. 您的第1個字符串p1在第2個字節中寫入了太多的字節,因爲strlen返回字符串的長度,您需要爲空終止符再添加1個字節。
    4. malloc不會分配足夠的內存 - 您需要path1的長度+ path2的長度+分隔符的長度+ null終止符。
    +0

    謝謝。代碼中的註釋僅用於解釋。我沒有真正的代碼。但是使用'''樣式註釋,我可以在C編譯器上編譯我的程序。我沒有明白這一點。爲什麼要在函數的開始時定義它?這段代碼再次編譯時沒有任何警告。 – 2010-06-29 16:03:51

    +2

    前兩點是正確的,因爲ANSI C僅支持在範圍頂部使用'/ * * /'對和變量聲明進行註釋。但是許多現代編譯器違反了這兩種編程方式。也就是說,他們接受'//'註釋,並且在編譯時會自動重新排列變量聲明。 – jdmichal 2010-06-29 16:09:09

    +5

    C++註釋和混合聲明是有效的C99。 – 2010-06-29 17:11:59

    2
    1. 使用last_char唯一的一次是在對比檢查,如果最後一個字符是一個分隔符。

    爲什麼不與本替換它:

    /* Retrieve the last character, and compare it to the directory separator character. */ 
    char directory_separator = '\\'; 
    if (path1[strlen(path1) - 1] == directory_separator) 
    { 
        append_directory_separator = 1; 
    } 
    

    如果要考慮多個字符分隔的可能性,你可以使用下面的。但分配組合的字符串添加的strlen(directory_separator)時,而不是一定只是1.

    /* First part is retrieving the address of the character which is 
        strlen(directory_separator) characters back from the end of the path1 string. 
        This can then be directly compared with the directory_separator string. */ 
    char* directory_separator = "\\"; 
    if (strcmp(&(path1[strlen(path1) - strlen(directory_separator)]), directory_separator)) 
    { 
        append_directory_separator = 1; 
    } 
    
  • 的不易出錯的方法是讓用戶給你目標緩衝區及其長度,很像strcpy的工作方式。這表明他們必須管理分配和釋放內存。

  • 這個過程看起來不錯。我認爲只有一些細節可以應用,主要是以笨重的方式做事。但是你做得很好,因爲你已經可以認識到這種情況並尋求幫助。

  • +0

    謝謝。如果調用者提供緩衝區,他如何知道要分配多少?當然,調用者知道兩個路徑,但不知道目錄分隔符。所以我想知道這會是一個不錯的選擇嗎?所有其他建議都非常好。非常感激。 – 2010-06-29 16:33:56

    +0

    @Appu:在Unix中,MAX_PATH_LEN是合理的緩衝區大小。 – 2010-06-29 17:05:18

    +0

    同樣,Windows有一個MAX_PATH,它是260個字符。 – jdmichal 2010-06-29 18:15:26

    0

    這是我用:

    #if defined(WIN32) 
    # define DIR_SEPARATOR '\\' 
    #else 
    # define DIR_SEPARATOR '/' 
    #endif 
    
    void combine(char *destination, const char *path1, const char *path2) { 
        if (path1 && *path1) { 
        auto len = strlen(path1); 
        strcpy(destination, path1); 
    
        if (destination[len - 1] == DIR_SEPARATOR) { 
         if (path2 && *path2) { 
         strcpy(destination + len, (*path2 == DIR_SEPARATOR) ? (path2 + 1) : path2); 
         } 
        } 
        else { 
         if (path2 && *path2) { 
         if (*path2 == DIR_SEPARATOR) 
          strcpy(destination + len, path2); 
         else { 
          destination[len] = DIR_SEPARATOR; 
          strcpy(destination + len + 1, path2); 
         } 
         } 
        } 
        } 
        else if (path2 && *path2) 
        strcpy(destination, path2); 
        else 
        destination[0] = '\0'; 
    } 
    
    0

    只是一點點的話,以提高你的函數:

    Windows不支持'/''\\'分離的路徑。所以,我應該能夠執行以下調用:

    const char* path1 = "C:\\foo/bar"; 
    const char* path2 = "here/is\\my/file.txt"; 
    char destination [ MAX_PATH ]; 
    combine (destination, path1, path2); 
    

    當編寫一個多平臺的項目可能是'\\'轉換爲'/'在任何輸入路徑(從用戶輸入,加載的文件......),那麼你的想法將只需要處理'/'個字符。

    問候。