I have narrowed a segmentation fault down to the specific line of code causing it. Here is a straightforward example showing the issue I am having.
int main()
{
char** files;
int sum;
int i;
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d)
{
sum = file_sum();
char* files[sum];
i = 0;
while ((dir = readdir(d)) != NULL)
{
files[i] = dir->d_name;
i++;
}
closedir(d);
}
else
{
return -1;
}
int len = strlen(files[0]); /*segmentation fault here*/
return 0;
}
Essentially what the program is doing is taking the names of all of the files from the current directory and putting them into an array. I am then going to try to get the sizes of all of the file names, but I have been getting a segmentation fault. My theory is that perhaps the file names are not null-terminated? If this is true, I am unsure of a workaround for that, but any assistance is appreciated.
Thanks
EDIT: Sorry I had an error here. The segmentation fault only occurs when I try to store the strlen return value, as I have now changed the code to above
Inside of your if block, you're defining a variable named files. This masks the variable of the same name at the top of the function. It is not specifying the size of the array at the higher scope, which seems to be your intent. So when you exit the if block, the inner files goes out of scope, and the outer files is left uninitialized. You then dereference the uninitialized pointer, causing the core dump.
What you want to do is dynamically allocate the necessary memory inside of the if block for the memory you need.
Also, the directory name retrieved into dir->d_name probably gets overwritten on each call to readdir, so you need to allocate space for that as well.
EDIT:
You also don't need a separate function to get the file count. You can allocate the array with a default size and realloc to expand as needed:
int main()
{
char** files;
int sum;
int i;
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d)
{
int size = 10;
sum = 0;
files = malloc(size * sizeof(char *)); // allocate the array
if (files == NULL) {
perror("malloc failed");
exit(1);
}
while ((dir = readdir(d)) != NULL)
{
if (sum >= size) {
// if the array is full, realloc twice the size
char **tmp;
size *= 2;
tmp = realloc(size * sizeof(char *));
if (tmp == NULL) {
perror("realloc failed");
exit(1);
}
files = tmp;
}
files[sum] = strdup(dir->d_name); // allocate and copy each string
sum++;
}
closedir(d);
}
else
{
return -1;
}
strlen(files[0]);
// free the individual strings
for (i=0; i<sum; i++) {
free(files[i]);
}
// free the array
free(files);
return 0;
}
Using "char* files[sum]" is only safe under 2 conditons:
you not trying to access the array after the end of it's declaration range: you shouln't try to access the "files" array after the end of the "if" where it's declared
that char* target need to be safely reserved. It's reasonnable to assume that the target is valid right after dir->d_name but after having closed the d DIR it's pretty risky
Here's a simple fix if you know how many files there can be in your directory : copying the dirent struct to dirList is the simplest but it's quite a big struct. You can also copy only a pointer to the dirent using "dirListPtr" but you need to be sure that your dirent struct is still valid. That's why i have put "closedir" a bit farther down.
If you're not sure about the number of files, dynamic allocation is the way to go (see dbush answer). If you want to use the data in another function, allocation is required too.
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
int main()
{
int sum;
int i;
DIR *d;
struct dirent* dir;
// you must be sure to have less than 255 files
struct dirent dirList[255];
struct dirent* dirListPtr[255];
d = opendir(".");
if (d)
{
i = 0;
while ((dir = readdir(d)) != NULL)
{
// copy the dirent information to our local variable which will live until the end of the main
dirList[i] = *dir;
// or keep only a pointer to the dirent struct: the system will surely not discard the dirent at least as long as d is open
dirListPtr[i] = dir;
i++;
}
}
else
{
return -1;
}
printf("size of filename: %lu size of dirent %lu\n", strlen(dirList[0].d_name), sizeof(dirList[0]));
printf("size of filename: %lu\n", strlen(dirListPtr[0]->d_name));
closedir(d);
return 0;
}
size of filename: 2 size of dirent 280
size of filename: 2
Related
I am trying to get data from a .tsv4 file. Whenever the code is run it returns with a segmentation fault: 11. The goal of the code is to store the unknown numbers into an array without knowing the amount of numbers in the file. The file I'm trying to access has 92 float values, and it stores them properly. But it will still give the segmentation fault error. I know this is because of a memory issue, just unsure of how to handle it
The file seems to get the segmentation fault from line 24
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int getFileInfo(const char *filename, float list[]);
int main(void)
{
float list[] = {0};
int size = getFileInfo("numbers.tsv4",list);
return 0;
}
int getFileInfo(const char *filename, float list[])
{
FILE* spData;
if((spData = fopen(filename, "r")) == NULL)
{
fprintf(stderr, "Error opening file %s.\n",filename);
exit(1);
}
int i = 0;
while(fscanf(spData, "%f", &list[i]) != EOF)
{
i++;
}
if (fclose(spData) == EOF)
{
fprintf(stderr, "Error closing file %s.\n", filename);
exit(2);
}
return i + 1;
}
The goal of the code is to store the unknown numbers into an array
without knowing the amount of numbers in the file
In order to this read floats one by one and allocate the array on the heap.
When you write
float list[] = {0};
int size = getFileInfo("numbers.tsv4",list);
you allocate an array with one value on the stack and that can never change.
Instead pass a pointer to the function and let the function manipulate that pointer
float* list = NULL;
int size = getFileInfo("numbers.tsv4",&list);
Inside your function read the float values from the file and as the need increases realloc the list array E.g.
int size = 10; // lets start with 10 values
int readFloats = 0; // number of floats read
*list = malloc(sizeof(float)*size);
...
if (readFloats > size)
{
float* tmp = realloc(*list, sizeof(float)*(size+10));
if ( tmp != NULL )
{
size += 10;
*list = tmp;
}
... put in the float
}
At the end realloc again with the current number of floats to get the right size
tmp = realloc(*list, readCurrent*sizeof(float));
if ( tmp != NULL )
{
size = readCurrent;
*list = tmp;
}
...
I am having trouble sorting a dirent struct in C. I have tried everything and cannot get the values of my struct array to appear in my comparison. My code looks like this:
void printSortedNames(){
struct dirent **file_list = (dirent**)malloc(5 * sizeof(dirent*));
int i = 0;
for (i = 0; i < directory_size; ++i){
file_list[i] = (dirent*)malloc(50 * sizeof(dirent));
}
DIR *dir;
struct dirent *sd;
dir = opendir(".");
if (dir == NULL){
printf("Error! unable to open directory.\n");
exit(1);
}
int count = 0;
while ((sd = readdir(dir)) != NULL){
file_list[count] = sd;
printf("%s\n", file_list[count]->d_name);
++count;
}
size_t file_list_size = sizeof(&file_list) / sizeof(struct dirent);
qsort(file_list, file_list_size, sizeof(struct dirent), sizeCompare);
}
I have created a simple function sizeCompare to show that my function is working but I get null values. My function looks like:
int sizeCompare(const void* a, const void* b){
printf("%s\n", ((const struct dirent*)a)->d_name);
}
Can someone explain to me why my sizeCompare is not retrieve the array values correctly?
UPDATE:
I have tried playing around with the size in the qsort and my value as a result was no longer null. the following line gives me an output:
qsort(file_list, 1000, sizeof(struct dirent), sizeCompare);
Obviously 1000 is not a good solution. Does anybody know the correct size for an array like this?
UPDATE 2:
sizeCompare function only takes the first parameter and the second one is null.
int sizeCompare(const void* a, const void* b){
const struct dirent *first_dirent = *(const struct dirent **) a;
const struct dirent *second_dirent = *(const struct dirent **) b;
.......
//first one works but second one is NULL
}
In the comparison function you need to dereference the pointers by first casting to struct dirent **, like this
const struct dirent *first_dirent = *(const struct dirent **) first_parameter;
this is because the address of each element is passed and since elements are pointers, the pointers passed to the funcion are pointers to pointers. Their void * addresses are the same, but you can't cast const struct dirent ** directly to const struct dirent *.
You have another important problem this,
file_list_size = sizeof(&file_list) / sizeof(struct dirent);
is wrong, try to print the value and see and it should be1
file_list_size = count;
because your code computes the size of a pointer divided by the size of struct dirent which is probably resulting in 0, read about the sizeof operator, it's result depends on the passed argument. When it's a variable, the size of the type is the result, when the variable is an array it's the size of the array.
Since file_list is a pointer to pointer, i.e. Not an array, then the result of
file_list_size = sizeof(&file_list) / sizeof(struct dirent);
is not what you think it is or what it should actually be.
There is no correct size, perhaps you should count the entries first and predict a value for the first malloc() in your code. Or use realloc() and dynamically count the entries and allocate the poitners simultaneously.
Also:
Your code leaks memory as pointed out by #user3629249 in this comment
You don't need to cast the return value from malloc()
Try not to mix code with declarations, it makes it hard to track variables and their scope.
You allocate space for 5 struct dirent * pointers but you never check for the count variable whether it reached or went beyond that value. That could lead to undefined behavior.
Your code also leaks memory because you never call closedir().
Here is an example of alphabetically sorting the entries
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
int
compareSize(const void *const A, const void *const B)
{
return strcmp((*(struct dirent **) A)->d_name, (*(struct dirent **) B)->d_name);
}
void printSortedNames(const char *const path)
{
int count;
DIR *dir;
struct dirent **list;
struct dirent *entry;
dir = opendir(path);
if (dir == NULL)
{
fprintf(stderr, "cannot open `%s'\n", path);
return;
}
/* First determine the number of entries */
count = 0;
while ((entry = readdir(dir)) != NULL)
++count;
/* Allocate enough space */
list = malloc(count * sizeof(*list));
if (list == NULL)
{
closedir(dir);
fprintf(stderr, "memory exhausted.\n");
return;
}
/* You don't need to allocate the list elements
* you can just store pointers to them in the
* pointer array `list'
*/
rewinddir(dir); /* reset position */
/* Save the pointers allocated by `opendir()' */
count = 0;
while ((entry = readdir(dir)) != NULL)
list[count++] = entry;
/* Call `qsort()', read about the `sizeof' operator */
qsort(list, count, sizeof(*list), compareSize);
/* Print the sorted entries now */
for (int index = 0 ; index < count ; ++index)
fprintf(stderr, "%s\n", list[index]->d_name);
closedir(dir);
}
int
main(void)
{
printSortedNames("/home/iharob");
return 0;
}
1Remember to limit the value of count to the maximum number of pointers you allocated space for.
Your sizeCompare function is not returning anything. You need to implement is such that it returns -1 when a < b, 0 when a = b and 1 when a > b.
int sizeCompare(const void* a, const void* b) {
// The implementation should return something
}
Also, you're calculating the file_list_size incorrectly. Can't you pass count as the second argument instead?
qsort(file_list, count, sizeof(struct dirent), sizeCompare);
I made an array of Node structs and I am trying to sort nodes in alphabetical order based on their char* variable called "word".
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "memwatch.h"
#include "concord.h"
#define BUFFSIZE 1000
int main(int argc, char** argv)
{
Node** list;
printf("%s%s\n","The file name is ", argv[1]);
readInputFile(argv[1], list);
return 0;
}
int compareWords(const void* nodeA, const void* nodeB)
{
Node* nodeAA = (Node *) nodeA;
Node* nodeBB = (Node *) nodeB;
puts("now here\n");
printf("%s\n", nodeAA->word);
printf("%s\n", nodeBB->word);
return strcmp(nodeAA->word, nodeBB->word);
}
void readInputFile(char* filename, Node** wordList)
{
FILE* file;
file = fopen(filename, "r");
wordList = calloc(BUFFSIZE, sizeof(Node*));
char* currentWord;
currentWord = (char*) malloc(sizeof(char) *BUFFSIZE);
int i;
i = 0;
while(fscanf(file, "%s", currentWord) == 1)
{
wordList[i] = (Node*) malloc(sizeof(Node));
wordList[i]->word = strdup(currentWord);
puts(wordList[i]->word);
}
fclose(file);
qsort(wordList, i, sizeof(Node), compareWords);
}
Before I was printing out garbage when I tried to print out the word in the compare function, now it looks like the function is not even being called.
now it looks like the function is not even being called.
That is because to sort a list of 0 elements you never need to compare two elements:
// ...
int i;
i = 0; // --- set to 0
while(fscanf(file, "%s", currentWord) == 1)
{
// i not changed ... causes other problems, too
// (contents omited)
}
fclose(file);
// i is still 0
qsort(wordList, i, sizeof(Node), compareWords);
// ...
Apart from that your usage of an "out parameters" is wrong, as pointed out in the comment by David C. Rankin. In this case, i'd also advise to just use the return value.
Moreover, I'd split that function into multiple functions:
// Does the file opening and closing, calls readInput
Node * readInputFile(char const *);
// The actual reading
Node * readInput(FILE *)
// Probably do the sorting outside of these functions
[First of all you question is incomplete as it misses to show us the definition of Node.]
However, three issues here:
I made an array of Node structs
You don't.
Here
wordList = calloc(BUFFSIZE, sizeof(Node*));
you allocate memory for an array of pointers to Node.
And then here
wordList[i] = (Node*) malloc(sizeof(Node));
you allocated a separate chunk of memory to each element of the pointer array created previously.
The latter may be scattered all over the process's memory. They will not be located in a continous block of memory, as expected by qsort(), which might have been the reason for:
I was printing out garbage [before]
On returning from readInputFile() the value of wordList is lost.
The reading loop does not increment the index counter i.
To fix 1. and 2. create an array and return a reference to it back up to the caller of readInputFile() like so
*wordList = calloc(BUFFSIZE, sizeof **wordList);
and call qsort() like this:
qsort(*wordList, i, sizeof(Node), compareWords);
To fix 3. do this:
size_t i = 0; /* No need for negative indexes here .*/
while((i < BUFFSIZE) /* Male sure not to overflow the array. */
&& (fscanf(file, "%s", currentWord) == 1))
{
(*wordList)[i].word = strdup(currentWord); /* This is POSIX not Standard C. */
puts((*wordList)[i].word);
++i;
}
void allocateFolderTree(char **tree)
{
int i;
tree = (char **)malloc(sizeof(char*)*MAX_FOLDERS);
for(i=0;i<MAX_FOLDERS;i++)
tree[i] = (char *)malloc(sizeof(char)*MAX_FOLDERS*MAX_FILENAME);
}
void getFolderTree (char **tree, char *path, int i)
{
DIR *dir = opendir(path);
struct dirent *entry;
while (entry = readdir(dir))
{
if( !strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
continue;
if (entry->d_type & DT_DIR)//check file type
{
//segfault for the next 3 lines
strcpy(tree[i], path);
strcat(tree[i], "/");
strcat(tree[i], entry->d_name);
i++;
char *new_path = malloc(sizeof(char)*(strlen(path)+MAX_FILENAME));
strcpy(new_path, path);
strcat(new_path, "/");
strcat(new_path, entry->d_name);
getFolderTree(tree, new_path, i);
free(new_path);
}
}
closedir (dir);
}
int main ()
{
char **folderTree;
allocateFolderTree(folderTree);
getFolderTree(folderTree, ROOT, 0);
free(folderTree);
return 0;
}
Why am I getting segfault and how do I solve this?
PS:
MAX_FOLDERS=1000
MAX_FILENAME=30
The folders I'm reading are less than 10 and each has a name less than 30!
This code:
void allocateFolderTree(char **tree)
{
int i;
tree = (char **)malloc(sizeof(char*)*MAX_FOLDERS);
for(i=0;i<MAX_FOLDERS;i++)
tree[i] = (char *)malloc(sizeof(char)*MAX_FOLDERS*MAX_FILENAME);
}
modifies the local copy of tree in the function, but never returns it to the calling code, so the allocated memory is all lost immediately the function returns. You have at least two choices on how to fix this:
char **allocateFolderTree(void)
{
int i;
char **tree = (char **)malloc(sizeof(char*)*MAX_FOLDERS);
for(i=0;i<MAX_FOLDERS;i++)
tree[i] = (char *)malloc(sizeof(char)*MAX_FOLDERS*MAX_FILENAME);
return tree;
}
or:
void allocateFolderTree(char ***tree)
{
int i;
*tree = (char **)malloc(sizeof(char*)*MAX_FOLDERS);
for(i=0;i<MAX_FOLDERS;i++)
(*tree)[i] = (char *)malloc(sizeof(char)*MAX_FOLDERS*MAX_FILENAME);
}
On the whole, avoiding triple pointers is a good idea, so I'd normally go with the other option.
I also observe that all the loops are allocating a lot of space in the inner loop. Are you sure you want to include MAX_FOLDERS in the size? On the face of it, you should be allocating the size of MAX_FILENAME, not MAX_FOLDERS * MAX_FILENAME. (Each inner allocation is currently allocating around 30 KiB, so in total you're allocating around 30 MiB of space.)
Note that sizeof(char) == 1 by definition, so there is little need to include it in the size calculation.
This fixes your memory allocation problems.
I changed a little. Your main problem was the first malloc in allocateFolderTree. In allocateFolderTree you can dereference to one level and malloc, but not up two levels which over-writes the address of folderTree (the argument) and that means when the function returns you have no way of accessing folderTree.
MAX_FOLDERS is baaaad. You need to calculate the number of folders first and then allocate based on this number.
Eg when I run the program, folderTree has an address of: 0x0021edf0.
Each folderTree array element is a null pointer before allocateFolderTree is called.
Then in allocateFolderTree each array element is allocated memory on the heap.
When you return from allocateFolderTree, the tree parameter still retains the address 0x0021edf0 but each array element now contains the address of memory on the heap.
#define MAX_FOLDERS 1000
#define MAX_FILENAME 256
#include <sys/types.h>
#include "dirent.h"
void allocateFolderTree(char** tree)
{
int i;
for(i=0;i<MAX_FOLDERS;i++)
tree[i] = (char *)malloc(sizeof(char)*MAX_FOLDERS*MAX_FILENAME);
}
void getFolderTree (char **tree, char *path, int i)
{
DIR *dir = opendir(path);
struct dirent *entry;
while (entry = readdir(dir))
{
if( !strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
continue;
if (entry->d_type & DT_DIR)//check file type
{
//segfault for the next 3 lines
strcpy(tree[i], path);
strcat(tree[i], "/");
strcat(tree[i], entry->d_name);
i++;
char *new_path = (char*)malloc(sizeof(char)*(strlen(path)+MAX_FILENAME));
strcpy(new_path, path);
strcat(new_path, "/");
strcat(new_path, entry->d_name);
getFolderTree(tree, new_path, i);
free(new_path);
}
}
closedir (dir);
}
int main ()
{
char* ROOT = "/";
char* folderTree[MAX_FOLDERS] = {0};
allocateFolderTree(folderTree);
getFolderTree(folderTree, ROOT, 0);
//You will have to create your own free_foldertree function to free each array item
//free(folderTree);
return 0;
}
I am trying to get the contents of a directory. Ideally, I would like to store them in a string array. Is there a way to do this in c other than opening the directory, iterating through its contents, and populating an array as it goes?
I am working on a system running OS X 10.9
You can obtain an allocated directory listing with the POSIX scandir function, which takes a path and optional filtering and sorting callbacks, and returns an array of dirent structures. OS X also provides an equivalent function which takes blocks rather than callbacks for sorting and filtering.
int scandir(const char *dirname, struct dirent ***namelist,
int (*select)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **));
Just retrieving an unsorted list of entries is very straightforward:
int num_entries;
struct dirent **entries = NULL;
num_entries = scandir("/", &entries, NULL, NULL);
for(int i = 0; i < num_entries; i++)
puts(entries[i]->d_name);
//entries is ours to free
for(int i = 0; i < num_entries; i++)
free(entries[i]);
free(entries);
POSIX also provides a pre-made sorting function to use with scandir for alphabetical ordering. To use it, just pass alphasort as the last argument.
Be careful of scandir returning an error (-1). The above code is structured in such a way that an explicit check isn't necessary, but that may not be possible in more elaborate uses.
You might want to run using system libc call and fopen.
Here is the sample code, take care of all the array lengths, there is NO validation done here.
#include
#include
#include
int
main(int argc, char* argv[])
{
char cmd[254] = "ls ";
char arr[1024];
char line[254];
FILE *fp;
if(argc < 2) return -1;
if(argv[1]) strcat(cmd, argv[1]);
strcat(cmd, " > /tmp/out");
system(cmd);
fp = fopen("/tmp/out", "r");
if(!fp){
perror("");
return fprintf(stderr, "could not open /tmp/out!\n");
}
while(fgets(line, 254, fp) != NULL) {
strcat(arr, line);
}
printf("%s\n", arr);
return 0;
}