我正在尝试将目录列表写入char数组,但是在尝试使用strcpy或strcat时出现分段错误。有没有更好的方法来解决这个问题?
我只想修改以下内容以创建字符串,而不是打印到 stdout 。我猜我只是想念一些非常简单的东西,但我无法将其固定下来。
#include <stdio.h>
#include <dirent.h>
int main(void)
{
char returnData[2048];
struct dirent *de; // Pointer for directory entry
// opendir() returns a pointer of DIR type.
DIR *dr = opendir(".");
if (dr == NULL) // opendir returns NULL if couldn't open directory
{
printf("Could not open current directory" );
return 0;
}
// Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
// for readdir()
while ((de = readdir(dr)) != NULL)
printf("%s\n", de->d_name); //strcat(returnData, de->d_name); produces segmentation fault here.
closedir(dr);
return 0;
}
答案 0 :(得分:1)
第一次更改:
char returnData[2048];
到
char returnData[2048] = { '\0' };
正如注释中已经提到的,您应该使用Zeros / NUL-Terminator初始化数组,因此对strcat
的调用被定义为strcat
将'\0'
替换为src
参数。
有些编译器抱怨使用strncat
或类似的词代替strcat
。
同样不要忘记,您还需要附加'\n'
才能获得与printf
相同的输出。
您可以预先计算长度,从而导致两个循环 或动态调整缓冲区大小。
顺便说一句:为什么要将它存储在单个字符串中?
答案 1 :(得分:1)
您缺少几件事。首先不要使用幻数 ... 2048
是哪里来的? (轻轻一指并举起它,然后说“是的,那应该足够好”?)limits.h
标头提供了宏PATH_MAX
,该宏可以保证为所有对象提供足够的存储空间。文件系统条目-改用它,例如:
#include <limits.h> /* for PATH_MAX */
...
char returnData[PATH_MAX] = ""; /* initialize to all zero is good habit */
(接下来,我确定是错字,但是null
不是NULL
)
如果您只想将de->d_name
复制到returnData
,则使用将de->d_name
复制到returnData
的函数,例如strcpy
,例如>
while ((de = readdir(dr)) != NULL) {
strcpy (returnData, de->d_name);
puts (returnData);
}
(当然,它在每次迭代时都会被覆盖,因此尽管returnData
是在当前函数中用returnData返回文件列表。 >自动存储,如果在另一个函数中声明,则不能以...开头。)
因此,所有将灌木丛de->d_name
复制到returnData
的事情都使您准确地离开了开始的地方,除了一次输出一个条目的名称外,无所不能。
为每个目录条目实际分配存储空间
我怀疑您真正想做的是将目录中的所有文件读入存储,以便您可以从函数中返回名称列表以在代码中进行进一步处理。这是常见的做法,但您不能使用单个字符数组来完成此操作。
相反,您需要声明一个 pointer-to-pointer-to-char (例如“ double-pointer”,char **dlist;
),这将允许您分配一些初始值。指针数量(例如8
),然后根据需要增加realloc
个指针,以容纳任何目录中的所有文件或目录名称。然后,您仅分配每个名称所需的存储空间(无符号终止字符为+1),并将每个名称的存储空间分配给其相应的指针,然后将该名称复制到您分配的新存储空间。
这样,您便可以从任意位置返回指向名称集合的指针。 (请记住,对象将 allocation-storage 类型的生存期一直持续到释放内存或程序结束为止)
#define NPTRS 8 /* initial number of pointers to allocate */
...
char **dlist = NULL, /* ptr-to-ptr-to-char for names */
size_t idx = 0, /* current index */
nptrs = NPTRS; /* number of pointers allocated */
...
/* allocate/validate nptrs pointers */
if ((dlist = calloc (nptrs, sizeof *dlist)) == NULL) {
perror ("calloc-nptrs");
return EXIT_FAILURE;
}
while ((de = readdir (dp))) {
...
/* check if dlist pointer limit reached - realloc */
if (idx == nptrs) { /* alwasy realloc to temporary pointer */
void *tmp = realloc (dlist, nptrs * 2 * sizeof *dlist);
if (!tmp) { /* validate reallocation */
perror ("realloc-dlist");
break; /* break, don't exit, original storage still valid */
}
dlist = tmp; /* assign reallocated block to dlist */
/* (optional) set all newly allocated memory to zero */
memset (dlist + nptrs, 0, nptrs * sizeof *dlist);
nptrs *= 2; /* update the number of allocated pointers */
}
/* allocate storage for name in dlist */
if ((dlist[idx] = malloc (strlen (de->d_name) + 1)) == NULL) {
char errbuf[PATH_MAX] = ""; /* storage for perror message */
sprintf (errbuf, "malloc failed '%s'", de->d_name);
perror (errbuf);
break;
}
strcpy (dlist[idx++], de->d_name); /* copy to new storage at idx */
}
现在,所有名称都存储在dlist
中,其中idx
表示存储的名称数量。您可以从任何函数返回dlist
(您还希望通过参数返回idx
,以便在调用函数中也可以返回存储的文件数,或将重分配移至副本下方(并包含“可选” memset
)以确保您始终在最后一个有效条目之后有一个前哨NULL
指针-这提供了另一种方式来指示返回的有效名称。
您已经(或将会发现)发现readdir
不会以任何特定顺序读取目录条目。为了对输出有用,使用qsort
进行排序是订购已存储文件名的最简单方法。下面的示例显示了一个简单的升序。
完全将其放入,您可以读取任何目录中的条目(作为程序的第一个参数或默认情况下从'.'
(当前目录)传递)。该代码将分配指针并根据需要重新分配。该代码为每个条目精确分配了strlen(de->d_name) + 1
个存储字符,将新的内存块分配给dlist[idx]
,然后将该条目复制到dlist[idx]
(您可以使用dlist[idx] = strdup (de->d_name);
来分配如果库提供了strdup
,请一步一步复制-但请记住strdup
是分配内存,因此您应该 validate 成功后再继续操作。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* opendir */
#include <dirent.h> /* opendir, readdir */
#include <limits.h> /* for PATH_MAX */
#define NPTRS 8 /* initial number of pointers to allocate */
/** qsort string comparison (sort ascending) */
int cmpstr (const void *a, const void *b)
{
return strcmp (*(char * const *) a, *(char * const *) b);
}
int main (int argc, char **argv) {
char **dlist = NULL, /* ptr-to-ptr-to-char for names */
*dname = argc > 1 ? argv[1] : "."; /* dirname supplied (. default) */
size_t idx = 0, /* current index */
nptrs = NPTRS; /* number of pointers allocated */
struct dirent *de = NULL; /* dirent pointer (readdir) */
DIR *dp = opendir (dname); /* directory pointer (opendir) */
if (!dp) { /* validate directory open for reading */
char errbuf[PATH_MAX] = ""; /* storage for perror message */
sprintf (errbuf, "opendir failed on '%s'", dname);
perror (errbuf);
return EXIT_FAILURE;
}
/* allocate/validate nptrs pointers */
if ((dlist = calloc (nptrs, sizeof *dlist)) == NULL) {
perror ("calloc-nptrs");
return EXIT_FAILURE;
}
while ((de = readdir (dp))) {
/* skip dot files */
if (!strcmp (de->d_name, ".") || !strcmp (de->d_name, ".."))
continue;
/* check if dlist pointer limit reached - realloc */
if (idx == nptrs) { /* alwasy realloc to temporary pointer */
void *tmp = realloc (dlist, nptrs * 2 * sizeof *dlist);
if (!tmp) { /* validate reallocation */
perror ("realloc-dlist");
break; /* break, don't exit, original storage still valid */
}
dlist = tmp; /* assign reallocated block to dlist */
/* (optional) set all newly allocated memory to zero */
memset (dlist + nptrs, 0, nptrs * sizeof *dlist);
nptrs *= 2; /* update the number of allocated pointers */
}
/* allocate storage for name in dlist */
if ((dlist[idx] = malloc (strlen (de->d_name) + 1)) == NULL) {
char errbuf[PATH_MAX] = ""; /* storage for perror message */
sprintf (errbuf, "malloc failed '%s'", de->d_name);
perror (errbuf);
break;
}
strcpy (dlist[idx++], de->d_name); /* copy to new storage at idx */
}
closedir (dp); /* close directory */
/* qsort names stored in dlist */
qsort (dlist, idx, sizeof *dlist, cmpstr);
/* output all file/directory names stored, freeing memory as you go */
printf ("'%s' contains '%zu' files:\n\n", dname, idx);
for (size_t i = 0; i < idx; i++) {
puts (dlist[i]); /* output name */
free (dlist[i]); /* free storage for name */
}
free (dlist); /* free pointers */
return 0;
}
使用/输出示例
$ ./bin/opendir_readdir_dyn_char_basic .
'.' contains '1860' files:
3darrayaddr.c
3darrayalloc.c
3darrayfill.c
BoggleData.txt
DoubleLinkedList-old.c
DoubleLinkedList.c
DoubleLinkedList.diff
InputFile.txt
MonoSound.wav
...
xsplit.sh
xstrncpy.c
zeronotzero.c
内存使用/错误检查
还要注意,在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针 >对于内存块,因此(2)在不再需要它时可以释放。
当务之急是使用一个内存错误检查程序来确保您不会尝试访问内存或在已分配的块的边界之外/之外进行写入,不要试图以未初始化的值读取或基于条件跳转,最后,以确认您释放了已分配的所有内存。
对于Linux,valgrind
是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。
$ valgrind ./bin/opendir_readdir_dyn_char_basic .
==16528== Memcheck, a memory error detector
==16528== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==16528== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==16528== Command: ./bin/opendir_readdir_dyn_char_basic .
==16528==
'.' contains '1860' files:
3darrayaddr.c
3darrayalloc.c
3darrayfill.c
BoggleData.txt
DoubleLinkedList-old.c
DoubleLinkedList.c
DoubleLinkedList.diff
InputFile.txt
MonoSound.wav
...
xsplit.sh
xstrncpy.c
zeronotzero.c
==16528==
==16528== HEAP SUMMARY:
==16528== in use at exit: 0 bytes in 0 blocks
==16528== total heap usage: 1,872 allocs, 1,872 frees, 109,843 bytes allocated
==16528==
==16528== All heap blocks were freed -- no leaks are possible
==16528==
==16528== For counts of detected and suppressed errors, rerun with: -v
==16528== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认已释放已分配的所有内存,并且没有内存错误。
仔细检查一下,如果还有其他问题,请告诉我。