2013-10-12 85 views
3

我試圖找到某個類型的目錄中的所有文件(此處硬編碼爲tif)並將它們複製到數組中。所有東西都乾淨地編譯(gcc -Wall沒有提供任何錯誤或警告),但是有一些內存問題。儘管我編寫的程序似乎運行得很乾淨(沒有段錯誤),但某些文件名是您在字符串中使用ascii值之外的其他字符時會出現的奇怪字符。這導致我使用valgrind運行,它顯示錯誤(下面的輸出),但我無法找到實際問題。在一些目錄中,valgrind會自我分段(程序在同一個目錄中運行乾淨)。將文件名複製到一個數組中C使用C

#include <sys/types.h> 
#include <dirent.h> 
#include <stdio.h> 
#include <search.h> 
#include <string.h> 
#include <error.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <stdbool.h> 



#define min(X, Y) ((X) < (Y) ? (X) : (Y)) 

int exitStatus = 0; 

/*------------------------------------------------------------------------------ 
* array_find 
* 
* ARGS - Takes a pointer to a string, a pointer to an array of strings, and an 
* int representing the length of the array. 
* 
* RETURN - returns an int indicating the first index of the key in the array, 
* or -1 if the key was not found 
*-----------------------------------------------------------------------------*/ 

int array_find(char *key, char *argv[], int argc){ 
    int i; 
    for (i = 0; i < argc; i++) 
    { 
     #ifdef DEBUG_array_find 
     printf("strncmp(%s, %s, %d) = %d\n", key, argv[i], min(strlen(key), strlen(argv[i])), strncmp(key, argv[i], min(strlen(key), strlen(argv[i])))); 
     #endif 
     if (strncmp(key, argv[i], min(strlen(key), strlen(argv[i]))) == 0) 
     { 
      return i; 
     } 
    } 
    return -1; 
} 


/*------------------------------------------------------------------------------ 
* ends_with 
* 
* ARGS - str = string to be checked 
*  sub = string to look for 
* 
* RETURN - Returns true if str ends with sub or both strings are NULL. 
      False otherwise. 
*-----------------------------------------------------------------------------*/ 

bool ends_with(char *str, char *sub){ 
    if (str == NULL && sub == NULL) 
    { 
     return true; 
    } 
    if (str == NULL || sub == NULL) 
    { 
     return false; 
    } 
    char *last_instance_of_sub = rindex(str, *sub); //Finds the last index of the first char of sub 
    int sub_len = strlen(sub); 
    if (last_instance_of_sub == NULL || strlen(last_instance_of_sub) != sub_len) 
    { 
     return false; 
    } 
    return strncmp(last_instance_of_sub, sub, sub_len) == 0; 
} 

int main(int argc, char *argv[]) 
{ 
    /*Parse args*/ 
    DIR *dir; 
    int index = array_find("-d", argv, argc); 
    char *dirname; 
    if (index >= 0) 
    { 
     dirname = argv[index + 1]; 
     dir = opendir(dirname); 
    } 
    else 
    { 
     dirname = getcwd(NULL, 0); 
     if (dirname == NULL) 
     { 
      perror("Error getting current directory name."); 
      exit(1); 
     } 
     dir = opendir(dirname); 
    } 
    if (dir == NULL) 
    { 
     perror(dirname); 
     exit(1); 
    } 

    #ifdef DEBUG_MAIN 
     printf("dirname = %s\n", dirname); 
    #endif 

    int threads = 1; 
    index = array_find("-t", argv, argc); 
    if (index >= 0) 
    { 
     threads = atoi(argv[index + 1]); 
    } 
    #ifdef DEBUG_MAIN 
     printf("threads = %d\n", threads); 
    #endif 

    struct dirent *entry = readdir(dir); 
    int num_files = 0; 
    while (entry != NULL) 
    { 
     if (ends_with(entry->d_name, ".tif")){ 
      #ifdef DEBUG_MAIN 
       printf("%s\n", entry->d_name); 
      #endif 
      num_files++; 
     } 
     entry = readdir(dir); 
    } 

    if (closedir(dir) != 0) 
    { 
     perror("Failed to close directory."); 
    } 

    #ifdef DEBUG_MAIN 
     printf("Num files = %d\n", num_files); 
    #endif 

    dir = opendir(dirname); 
    if (dir == NULL) 
    { 
     perror(dirname); 
     exit(1); 
    } 

    entry = readdir(dir); 

    char *file_names[num_files]; 
    int i = 0; 
    for(; entry != NULL; i++) 
    { 
     if (ends_with(entry->d_name, ".tif")){ 
      file_names[i] = strdup(entry->d_name); 
      if (file_names[i] == NULL) 
      { 
       perror("Could not create the filename array.\n"); 
       exit(1); 
      } 
     } 
     entry = readdir(dir); 
    } 

/* #ifdef DEBUG_MAIN*/ 
     for (i = 0; i < num_files; i++) 
     { 
      printf("%s\n", file_names[i]); 
/*   free(file_names[i]);*/ 
     } 
/* #endif*/ 



    free(dir); 
    return exitStatus; 
} 

Valgrind的輸出:

==24488== Memcheck, a memory error detector 
==24488== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. 
==24488== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info 
==24488== Command: ./myprogram -d /home/chris/Pictures/Catalinas\ with\ Christie/Processed/ 
==24488== 
dirname = /home/chris/Pictures/Catalinas with Christie/Processed/ 
threads = 1 
cacti2_lzn.tif 
DSC_2139_lzn.tif 
DSC_1512_lzn.tif 
DSC_1296_lzn.tif 
DSC_1577_lzn.tif 
DSC_1658_lzn.tif 
DSC_1293_lzn.tif 
DSC_1631_lzn.tif 
DSC_1418_lzn.tif 
DSC_1315_2crop_lzn.tif 
DSC_1377_lzn2crop.tif 
DSC_2167_lzn.tif 
1981-1985-HDR3_lzn2.tif 
DSC_2129_lzn.tif 
DSC_1448_lzn.tif 
DSC_1607_lzn.tif 
DSC_1564_lzn.tif 
DSC_2052-DSC_2072_lzn.tif 
DSC_1487_lzn.tif 
DSC_1591_2_lzn.tif 
DSC_2124_lzn.tif 
DSC_1622_lzn.tif 
DSC_2157_lzn.tif 
DSC_1685_lzn.tif 
Num files = 24 
cacti2_lzn.tif 
DSC_2139_lzn.tif 
DSC_1512_lzn.tif 
DSC_1296_lzn.tif 
DSC_1577_lzn.tif 
DSC_1658_lzn.tif 
==24488== Use of uninitialised value of size 8 
==24488== at 0x4C2D7C2: __GI_strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24488== by 0x4EA4ECB: puts (ioputs.c:36) 
==24488== by 0x400D52: main (batch-convert.c:161) 
==24488== 
==24488== Invalid read of size 1 
==24488== at 0x4C2D7C2: __GI_strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24488== by 0x4EA4ECB: puts (ioputs.c:36) 
==24488== by 0x400D52: main (batch-convert.c:161) 
==24488== Address 0x0 is not stack'd, malloc'd or (recently) free'd 
==24488== 
==24488== 
==24488== Process terminating with default action of signal 11 (SIGSEGV) 
==24488== Access not within mapped region at address 0x0 
==24488== at 0x4C2D7C2: __GI_strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24488== by 0x4EA4ECB: puts (ioputs.c:36) 
==24488== by 0x400D52: main (batch-convert.c:161) 
==24488== If you believe this happened as a result of a stack 
==24488== overflow in your program's main thread (unlikely but 
==24488== possible), you can try to increase the size of the 
==24488== main thread stack using the --main-stacksize= flag. 
==24488== The main thread stack size used in this run was 8388608. 
==24488== 
==24488== HEAP SUMMARY: 
==24488==  in use at exit: 33,243 bytes in 25 blocks 
==24488== total heap usage: 26 allocs, 1 frees, 66,051 bytes allocated 
==24488== 
==24488== LEAK SUMMARY: 
==24488== definitely lost: 0 bytes in 0 blocks 
==24488== indirectly lost: 0 bytes in 0 blocks 
==24488==  possibly lost: 0 bytes in 0 blocks 
==24488== still reachable: 33,243 bytes in 25 blocks 
==24488==   suppressed: 0 bytes in 0 blocks 
==24488== Rerun with --leak-check=full to see details of leaked memory 
==24488== 
==24488== For counts of detected and suppressed errors, rerun with: -v 
==24488== Use --track-origins=yes to see where uninitialised values come from 
==24488== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2) 
Segmentation fault (core dumped) 

它是因爲我已經用c在所有時間,但據我所知(從該名男子頁)的strdup應該使用malloc就分配內存堆的字符串的副本。在我想起strdup函數之前,我曾嘗試過手動完成這些操作,並且出現了相同的錯誤。我想也許我的代碼有缺陷,並認爲strdup函數會照顧它,但顯然還有其他一些問題。

誰能告訴我我做錯了什麼?

編輯1: 根據要求,我添加了程序的完整源代碼。另外,對於那些說我要檢查num_files的對象,你會看到,我提前計算了tif文件的數量,所以我知道將被複制到數組中的文件的確切數量,因此檢查索引isn'必要的。

另外,作爲說明,該程序是用DEBUG_MAIN定義的編譯的,所以#ifdef DEBUG_MAIN塊中的任何內容都會運行。沒有定義其他調試標誌。

+0

'entry'的初始值是什麼? – user4815162342

+3

1.如果您發佈了實際的[SSCCE](http://sscce.org/),而不僅僅是一個片段,這將有所幫助。 2.你的程序是否在只有ASCII文件名的目錄下工作? 3。如果沒有相應的源代碼(和行號與它一起),Valgrind輸出完全沒有幫助 – thkala

+1

另外,你是否記得用'i'的最終值更新'num_files'?你是否將'file_names'數組(具有自動存儲類)返回給調用者?發佈一個完整的例子可以解決所有這些問題,而無需詢問。 – user4815162342

回答

0

取代它的​​問題是,如果你有不匹配的模式(如...項)的任何條目,你跳過數組中的對應條目。這也意味着你去寫你的file_names陣列。當文件名匹配時,您應該只增加i

使用getcwd()而不是僅僅使用.作爲當前目錄工作,但幾乎沒有必要。

使用free(dir)而不是closedir(dir)是一個無法解決的災難。

命令行參數處理不正常。如最初所寫,它將接受-delete等同於-d。這不是很好的風格。

#include <assert.h> 
#include <dirent.h> 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <stdbool.h> 

bool ends_with(char *str, char *sub); 
int array_find(char *key, char *argv[], int argc); 

int array_find(char *key, char *argv[], int argc) 
{ 
    for (int i = 0; i < argc; i++) 
    { 
     if (strcmp(key, argv[i]) == 0) 
      return i; 
    } 
    return -1; 
} 

bool ends_with(char *str, char *sub) 
{ 
    if (str == NULL && sub == NULL) 
     return true; 
    if (str == NULL || sub == NULL) 
     return false; 
    char *last_instance_of_sub = rindex(str, *sub); 
    size_t sub_len = strlen(sub); 
    if (last_instance_of_sub == NULL || strlen(last_instance_of_sub) != sub_len) 
     return false; 
    return strcmp(last_instance_of_sub, sub) == 0; 
} 

int main(int argc, char *argv[]) 
{ 
    int index = array_find("-d", argv, argc); 
    char *dirname; 
    if (index >= 0) 
    { 
     dirname = argv[index + 1]; 
    } 
    else 
    { 
     dirname = getcwd(NULL, 0); 
     if (dirname == NULL) 
     { 
      perror("Error getting current directory name."); 
      exit(1); 
     } 
    } 
    DIR *dir = opendir(dirname); 
    if (dir == NULL) 
    { 
     perror(dirname); 
     exit(1); 
    } 
    char suffix[] = ".c"; 

    printf("dirname = %s\n", dirname); 

    struct dirent *entry; 
    int num_files = 0; 
    while ((entry = readdir(dir)) != NULL) 
    { 
     if (ends_with(entry->d_name, suffix)) 
      num_files++; 
    } 

    if (closedir(dir) != 0) 
    { 
     perror("Failed to close directory."); 
    } 

    printf("Num files = %d\n", num_files); 

    dir = opendir(dirname); 
    if (dir == NULL) 
    { 
     perror(dirname); 
     exit(1); 
    } 

    char *file_names[num_files]; 
    int i = 0; 
    while ((entry = readdir(dir)) != NULL) 
    { 
     if (ends_with(entry->d_name, suffix)) 
     { 
      file_names[i] = strdup(entry->d_name); 
      if (file_names[i++] == NULL) 
      { 
       perror("Could not create the filename array.\n"); 
       exit(1); 
      } 
     } 
    } 
    assert(i <= num_files); 
    if (i < num_files) 
     num_files = i; 

    for (i = 0; i < num_files; i++) 
    { 
     printf("%s\n", file_names[i]); 
     free(file_names[i]); 
    } 

    closedir(dir); 
    return 0; 
} 
+0

我對自己有點生氣,我沒有看到我正在通過數組前進,無論我是否添加文件。我的調試技巧在過去的幾個月裏變得糟糕透了。無論如何,感謝您的幫助,並感謝其他技巧,我會考慮到他們。 – Chris

0

數組的索引應檢查:

i<num_files 
1

在這部分代碼for(; entry != NULL; i++)是太危險了,比如讓說的num_files值是1000,如果給定的目錄包含1002什麼條目,那麼你會有一個問題。 與for(; entry != NULL && i < num_files ; i++)

相關問題