2013-10-12 44 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++)

相关问题