Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I got this code:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <dirent.h>
#include <stdlib.h>
#define W 1031
#define B 256
struct position {
int x;
int y;
struct position *next;
};
struct wordFiles {
char *fileName;
struct position *cor;
struct wordFiles *next;
};
struct wordTree {
char *word;
struct wordFiles *files;
struct wordTree *left;
struct wordTree *right;
};
struct wordTree *hashTable[W];
typedef struct wordFiles *files_Ptr;
typedef struct position *pos_ptr;
typedef struct wordTree *wordTreePtr;
long int power(int a, long b){
long int value,i;
value = 1;
for (i = 0; i < b; i++)
value *= a;
return value;
}
int hashValue (char *word){
long int i=0,s=0,n;
n = strlen(word);
for (i=0; i<n; i++){
s += power(B,n-i-1) * word[i];
}
return (s%W);
}
void readword(char *word , FILE *curr_file, int *x_axis, int *y_axis, int *newline, int *endfile){
char c;
c = (char) malloc(sizeof(char));
if ((fscanf(curr_file, "%s", word))!=1 || fscanf(curr_file, "%c", &c)!=1){
*endfile=1;
}
*x_axis += strlen(word);
if (strlen(word)==1 && c=='\n'){
*newline = 1;
return;
}
if (c==' ') {
*x_axis +=1;
}
else if (c=='\n') {
*newline = 1;
}
return;
}
void coordinateslistInsert (pos_ptr *lp,int x, int y){
pos_ptr prev,curr;
prev = NULL;
curr = *lp;
while (curr != NULL){
prev = curr;
curr = curr->next;
}
pos_ptr n = (pos_ptr) malloc(sizeof(struct position));
if (n == NULL) {
printf("Out of memory\n");
return;
}
n->next = NULL;
n->x = x;
n->y = y;
if (prev==NULL) {
*lp = n;
}
else {
prev->next = n;
}
return;
}
void filelistInsert (files_Ptr *lp, char *filename, int x, int y, int k) {
files_Ptr prev, curr;
prev = NULL;
curr = *lp;
if ( curr!=NULL && k == 1 && strcmp(curr->fileName, filename) == 0 ){
coordinateslistInsert(&(*lp)->cor, x, y);
return;
}
while (curr != NULL){
prev = curr;
curr = curr->next;
}
files_Ptr n = (files_Ptr)malloc(sizeof(struct wordFiles));
if (n == NULL) {
printf("Out of memory\n");
return;
}
n->fileName = filename;
n->next = NULL;
coordinateslistInsert (&(*n).cor , x ,y);
if (prev==NULL) {
*lp = n;
}
else {
prev->next = n;
}
return;
}
void treeBalancedInsert (wordTreePtr *curr_tree, char *word, char *filename, int x, int y) {
int k=0;
if (*curr_tree == NULL) {
*curr_tree =(wordTreePtr) malloc(sizeof(struct wordTree));
if (*curr_tree == NULL) {
printf("Out of memory\n");
exit(1);
}
(*curr_tree)->word=malloc(30*sizeof(char));
(*curr_tree)->left = (*curr_tree)->right = NULL;
strcpy((*curr_tree)->word,word);
filelistInsert (&(*curr_tree)->files , filename,x,y,k);
}
else {
if (strcmp((*curr_tree)->word,word) == 0){
k=1;
filelistInsert (&(*curr_tree)->files , filename,x,y,k);
return;
}
else if (strcmp((*curr_tree)->word,word) < 0)
treeBalancedInsert(&(((*curr_tree)->left)), word, filename, x, y);
else
treeBalancedInsert(&(((*curr_tree)->right)), word, filename,x ,y);
}
}
void search (char *word, int h_value, struct wordTree *hashtable[]){
wordTreePtr n = hashTable[h_value];
while(n!=NULL && strcmp ( n->word , word ) !=0){
if (strcmp ( n->word , word ) > 0 ){
n = n->right;
}
else if(strcmp ( n->word , word ) < 0){
n = n->left;
}
}
if (n==NULL){
printf("NOT FOUND");
return;
}
printf("%s\n",n->word);
files_Ptr k = n->files;
while (k!=NULL) {
pos_ptr q = k->cor ;
while (q!=NULL) {
printf("%s(%d,%d)\n",k->fileName,q->y,q->x);
q = q->next;
}
k = k->next;
}
return;
}
int main(int argc, char *argv[])
{
int j,i;
for (i=0; i<W; i++){
hashTable[i] = NULL;
}
for (j=1; j<argc; j++){
FILE *curr_file=fopen(argv[j], "r+");
int h_value = 0, x_axis = 1, y_axis=1, newline=0,endfile=0;
if (curr_file == NULL) {
perror("Error: ");
return (-1);
}
char *word=NULL , *filename;
filename = (char *) malloc(30*sizeof(char));
filename = argv[j];
while (endfile!=1){
word = (char *) malloc(20*sizeof(char));
readword(word, curr_file, &x_axis, &y_axis, &newline, &endfile);
h_value = hashValue(word);
treeBalancedInsert(&hashTable[h_value], word, filename, x_axis-(unsigned)strlen(word)-1, y_axis);
if (newline==1){
y_axis +=1;
x_axis=1;
newline=0;
}
}
fclose(curr_file);
free(word);
}
char *wordToSearch;
wordToSearch = (char *) malloc(20*sizeof(char));
scanf("%s",wordToSearch);
search(wordToSearch,hashValue(wordToSearch),hashTable);
return 0;
}
and it was written on a mac, and supposedly works. But when i compile and run on my machine it just wont.
What it does is it takes text files as arguments and sorts the words in binary trees which are placed in the hashtable depending the hashvalue of the word. And then you can type a word and it tells you the coordinates it appears and which files.
Anyway, debugging on eclipse step by step stucks at the (curr=curr->next) of the filelistInsert and code blocks shows 2 more problems, one at the treebalancedinsert function where it calls for the filelistinsert function and at the main when it calls for the treebalancedinsertfunction.
I cant find what's wrong in the filelistinsert and im short on time. (i know it's an awful question, but im desperate)
Change:
char c;
c = (char) malloc(sizeof(char));
to
char c;
Note: this is an error in your program but it is does not explain why your program is crashing.
Elsewhere:
filename = (char *) malloc(30*sizeof(char));
filename = argv[j];
It is a memory leak and also if you then suppose filename to be an array of 30 characters you might have troubles.
I see several problems.
The first thing that has alarm bells going off in my head are lines like
(*curr_tree)->word=malloc(30*sizeof(char));
and
word = (char *) malloc(20*sizeof(char));
If you read any word that's longer than 19 characters from your input file, you're going to have a buffer overflow, which will cause heartburn at some point. I'm reasonably sure that this is the source of your problem (when I run this program on its own source text, I start getting segfaults after reading "(*curr_tree)->word=malloc(30*sizeof(char));", most likely because we overflow a 20-character buffer and clobber something else.
The readword function should decide how much memory to allocate for a word based on the contents of the input file. This means you're going to have to make the readword function a bit smarter, and have it allocate/reallocate a buffer as it's reading input, something like the following:
void readword(char **word , FILE *curr_file, int *x_axis, int *y_axis, int *newline, int *endfile){
int c;
size_t bufLen = 0;
const size_t bufExtent=10;
size_t idx = 0;
*word = NULL;
/**
* Read input one character at a time until we see a space or EOF
*/
while ( ( c = fgetc( curr_file ) ) != EOF && !isspace( c ))
{
/**
* Are we at the end of our buffer?
*/
if ( idx == bufLen )
{
/**
* Extend the buffer
*/
char *tmp = realloc( *word, bufLen + bufExtent );
if ( tmp )
{
bufLen += bufExtent;
*word = tmp;
}
else
{
fprintf( stderr, "readword: Could not allocate memory to extend word\n" );
return;
}
}
(*word)[idx++] = c;
}
/**
* If we read a string, 0-terminate it
*/
if ( *word )
{
(*word)[idx] = 0;
*x_axis += strlen(*word);
}
However, even with this I'm still getting runtime errors, so there are other time bombs hiding in this code.
Then there's this:
filename = (char *) malloc(30*sizeof(char));
filename = argv[j];
This does not copy the contents of argv[j] to the memory you just allocated; instead, it assigns the pointer value of argv[j] to filename, overwriting the pointer to the memory you just allocated, leading to a memory leak. And again, you're picking an arbitrary length for the filename. Try something like the following:
filename = malloc( strlen( argv[j] ) + 1 );
strcpy( filename, argv[j] );
Style nit:
Hiding pointer types behind typedefs is not recommended, unless the type is meant to be opaque and never derferenced directly. If I see the * in the object declaration, I immediately know how it's supposed to be used in an expression. Using a foo_ptr typedef may make the code scan a little better, but it hinders undersatnding IMO. I've been bitten by this enough over the years to where I avoid using typedefs in general.
Related
i'm trying to sort alphabetically a linked list in C. The linked list i used is the following:
struct lineList{ //structure with all the line of the input file
char *line;
struct lineList *next;
};
typedef struct lineList llist;
I saved into the string line some path taken from this file:
/home/user/Scrivania/find/try
/home/user/Scrivania/find/try1
/home/user/Scrivania/tryDir
Then i tried to sort them alphabetically with this portion of code:
char *line;
size_t len = 0;
ssize_t line_size;
llist *lHead = NULL;
llist *lTail = NULL;
FILE *fInput;
fInput = fopen(inputFile, "r"); //the file that contains the path of the file in which search.
if(fInput == NULL){
fprintf(stderr, "Cannot open %s, exiting. . .\n", inputFile);
exit(1);
}
while((line_size = getline(&line, &len, fInput)) != -1){
//saving into the lineList structure
llist *l = malloc (sizeof(llist));
l->line = line;
l->next = NULL;
//sort alphabetically
if(lHead == NULL || strcmp(l->line, lHead->line) < 0){
l->next = lHead;
lHead = l;
} else {
lTail = lHead;
while((lTail->next != NULL) && (strcmp(l->line, lTail->next->line) >= 0)){
lTail = lTail->next;
}
l->next = lTail->next;
lTail->next = l;
}
}
fclose(fInput);
In the variable inputFile is stored the path of the file showed above. If i try to iterating through the list and printing out the content of line i always get the last path of the file:
/home/user/Scrivania/tryDir
/home/user/Scrivania/tryDir
/home/user/Scrivania/tryDir
What's wrong with my code?
There's some problem with string handling. To solve the problem I changed the line assignment in this way:
llist *l = malloc (sizeof(llist));
l->line = (char*)malloc((strlen(line)+1)*sizeof(char));
strcpy(l->line,line);
l->next = NULL;
The sorting was correct, the string assignment was the problem.
You can use qsort from the standard library.
The following is an example on how to use it.
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))
#include <stdio.h>
#include <stdlib.h>
int values[] = { 88, 56, 100, 2, 25 };
int cmpfunc (const void * a, const void * b) {
return ( *(int*)a - *(int*)b );
}
int main () {
int n;
printf("Before sorting the list is: \n");
for( n = 0 ; n < 5; n++ ) {
printf("%d ", values[n]);
}
qsort(values, 5, sizeof(int), cmpfunc);
printf("\nAfter sorting the list is: \n");
for( n = 0 ; n < 5; n++ ) {
printf("%d ", values[n]);
}
return(0);
}
I have the following data structure:
typedef struct Word {
char *word;
int occur;
struct Word *next_word;
} * WordList;
I'm trying to implement a function that adds a string (word) to a WordList. If it's already present in the list, then increment its occurrences, otherwise, add it to the head. This function also returns the occurrences of said word in the list.
What follows is my implementation:
#include <stdlib.h>
#include <string.h>
int addAtHead(WordList *w, char *word) {
WordList head = *w;
while (*w && strcmp((*w)->word, word) != 0)
w = &(*w)->next_word;
if (!*w) {
WordList new = malloc(sizeof(struct Word));
size_t length = strlen(word) + 1;
new->word = malloc(length);
memcpy(new->word, word, length);
new->occur = 0;
new->next_word = head;
*w = new;
}
return ++(*w)->occur;
}
I have these next functions to test the previous one:
#include <stdio.h>
void printWordList(WordList w) {
for ( ; w; w = w->next_word)
printf("Word: %s\nOccurrences: %d\n\n",
w->word, w->occur);
}
int main(void) {
WordList w = NULL;
addAtHead(&w, "world");
addAtHead(&w, "hello");
printWordList(w);
return 0;
}
When I compile and run the executable, I get this result:
> Word: world Occurrences: 1
>
> Word: hello Occurrences: 1
>
> Word: world Occurrences: 1
>
> Word: hello Occurrences: 1
>
> Word: world Occurrences: 1
>
> Word: hello Occurrences: 1
and on, and on..
I assumed that somewhere in my code I link the last element to the first, so I drew the following diagrams to figure out where this happens.
I then hypothesized that the problem lies in the line *w = new;.
How do I go about setting *w to start of the list again, without creating a circular list?
I simplified your code a little bit ... maybe you'll get the idea:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Word {
char *word;
int occur;
struct Word *next_word;
}WordList;
int addAtHead(WordList **w_ptr, char *word) {
WordList *head, *w;
w = *w_ptr;
head = w;
while (w != NULL && strcmp(w->word, word) != 0)
w = w->next_word;
if (w == NULL) {
WordList *newstruct;
newstruct = malloc(sizeof *newstruct);
if(newstruct == NULL) /* out of memory etc. */
return -1;
size_t length = strlen(word) + 1;
newstruct->word = malloc(length);
if(newstruct->word == NULL){
free(newstruct);
return -2;
}
memcpy(newstruct->word, word, length);
newstruct->occur = 0;
newstruct->next_word = head;
w = newstruct;
printf("address: %p, head: %p\n", w, head);
*w_ptr = newstruct;
}
return ++(w->occur);
}
void printWordList(WordList *w) {
for ( ; w; w = w->next_word)
printf("Word: %s\nOccurrences: %d\n\n",
w->word, w->occur);
}
int main(void) {
int rv = 0;
WordList *w = NULL;
rv = addAtHead(&w, "world");
printf("addAtHead = %d\n",rv);
rv = addAtHead(&w, "hello");
printf("addAtHead = %d\n",rv);
if(w == NULL){
printf("w == NULL\n");
} else {
printf("pointer address: %p\n",w);
}
printWordList(w);
return 0;
}
If you want to change a pointer in the function and want to get this changed pointer back: Either you return a pointer (the function is build like Wordlist * addAtHead(....) or you can use (in our case) a pointer to that pointer. You have to get a reference to that pointer.
I am really trying to learn if someone wouldn't mind to educate me in the principles I may be missing out on here. I thought I had everything covered but it seems I am doing something incorrectly.
The following code gives me a segmentation fault, and I cannot figure out why? I am adding the & in front of the arguments name being passed in to fscanf.
int word_size = 0;
#define HASH_SIZE 65536
#define LENGTH = 45
node* global_hash[HASH_SIZE] = {NULL};
typedef struct node {
char word[LENGTH + 1];
struct node* next;
} node;
int hash_func(char* hash_val){
int h = 0;
for (int i = 0, j = strlen(hash_val); i < j; i++){
h = (h << 2) ^ hash_val[i];
}
return h % HASH_SIZE;
}
bool load(const char *dictionary)
{
char* string;
FILE* dic = fopen(dictionary, "r");
if(dic == NULL){
fprintf(stdout, "Error: File is NULL.");
return false;
}
while(fscanf(dic, "%ms", &string) != EOF){
node* new_node = malloc(sizeof(node));
if(new_node == NULL){
return false;
}
strcpy(new_node->word, string);
new_node->next = NULL;
int hash_indx = hash_func(new_node->word);
node* first = global_hash[hash_indx];
if(first == NULL){
global_hash[hash_indx] = new_node;
} else {
new_node->next = global_hash[hash_indx];
global_hash[hash_indx] = new_node;
}
word_size++;
free(new_node);
}
fclose(dic);
return true;
}
dictionary.c:25:16: runtime error: left shift of 2127912344 by 2 places cannot be represented in type 'int'
dictionary.c:71:23: runtime error: index -10167 out of bounds for type 'node *[65536]'
dictionary.c:73:13: runtime error: index -10167 out of bounds for type 'node *[65536]'
dictionary.c:75:30: runtime error: index -22161 out of bounds for type 'node *[65536]'
dictionary.c:76:13: runtime error: index -22161 out of bounds for type 'node *[65536]'
Segmentation fault
Update after OP posted more code
The problem is that your hash_func works with signed integers and that it overflows. Therefore you get a negative return value (or rather undefined behavior).
That is also what these lines tell you:
dictionary.c:25:16: runtime error: left shift of 2127912344 by 2 places cannot be represented in type 'int'
Here it tells you that you have a signed integer overflow
dictionary.c:71:23: runtime error: index -10167 out of bounds for type 'node *[65536]'
Here it tells you that you use a negative index into an array (i.e. global_hash)
Try using unsigned integer instead
unsigned int hash_func(char* hash_val){
unsigned int h = 0;
for (int i = 0, j = strlen(hash_val); i < j; i++){
h = (h << 2) ^ hash_val[i];
}
return h % HASH_SIZE;
}
and call it like:
unsigned int hash_indx = hash_func(new_node->word);
Original answer
I'm not sure this is the root cause of all problems but it seems you have some problems with memory allocation.
Each time you call fscanf you get new dynamic memory allocated for string du to %ms. However, you never free that memory so you have a leak.
Further, this looks like a major problem:
global_hash[hash_indx] = new_node; // Here you save new_node
} else {
new_node->next = global_hash[hash_indx];
global_hash[hash_indx] = new_node; // Here you save new_node
}
word_size++;
free(new_node); // But here you free the memory
So it seems your table holds pointers to memory that have been free'd already.
That is a major problem that may cause seg faults when you use the pointers.
Maybe change this
free(new_node);
to
free(string);
In general I'll suggest that you avoid %ms and also avoid fscanf. Use char string[LENGTH + 1] and fgets instead.
There are multiple issues in the code posted. Here are the major ones:
you should use unsigned arithmetic for the hash code computation to ensure that the hash value is positive. The current implementation has undefined behavior as words longer than 15 letters cause an arithmetic overflow, which may produce a negative value and cause the modulo to be negative as well, indexing outside the bounds of global_hash.
You free the newly allocated node with free(new_node);. It has been stored into the global_hash array: later dereferencing it for another word with the same hash value will cause undefined behavior. You probably meant to free the parsed word instead with free(string);.
Here are the other issues:
you should check the length of the string before copying it to the node structure array with strcpy(new_node->word, string);
fscanf(dic, "%ms", &string) is not portable. the m modifier causes fscanf to allocate memory for the word, but it is an extension supported by the glibc that may not be available in other environments. You might want to write a simple function for better portability.
the main loop should test for successful conversion with while(fscanf(dic, "%ms", &string) == 1) instead of just end of file with EOF. It may not cause a problem in this specific case, but it is a common cause of undefined behavior for other conversion specifiers.
the definition #define HASH_SIZE 65536; has a extra ; which may cause unexpected behavior if HASH_SIZE is used in expressions.
the definition #define LENGTH = 45; is incorrect: the code does not compile as posted.
Here is a modified version:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#define HASH_SIZE 65536
#define LENGTH 45
typedef struct node {
char word[LENGTH + 1];
struct node *next;
} node;
int word_size = 0;
node *global_hash[HASH_SIZE];
unsigned hash_func(const char *hash_val) {
unsigned h = 0;
for (size_t i = 0, j = strlen(hash_val); i < j; i++) {
h = ((h << 2) | (h >> 30)) ^ (unsigned char)hash_val[i];
}
return h % HASH_SIZE;
}
/* read a word from fp, skipping initial whitespace.
return the length of the word read or EOF at end of file
store the word into the destination array, truncating it as needed
*/
int get_word(char *buf, size_t size, FILE *fp) {
int c;
size_t i;
while (isspace(c = getc(fp)))
continue;
if (c == EOF)
return EOF;
for (i = 0;; i++) {
if (i < size)
buf[i] = c;
c = getc(fp);
if (c == EOF)
break;
if (isspace(c)) {
ungetc(c, fp);
break;
}
}
if (i < size)
buf[i] = '\0';
else if (size > 0)
buf[size - 1] = '\0';
return i;
}
bool load(const char *dictionary) {
char buf[LENGTH + 1];
FILE *dic = fopen(dictionary, "r");
if (dic == NULL) {
fprintf(stderr, "Error: cannot open dictionary file %s\n", dictionary);
return false;
}
while (get_word(buf, sizeof buf, dic) != EOF) {
node *new_node = malloc(sizeof(node));
if (new_node == NULL) {
fprintf(stderr, "Error: out of memory\n");
fclose(dic);
return false;
}
unsigned hash_indx = hash_func(buf);
strcpy(new_node->word, buf);
new_node->next = global_hash[hash_indx];
global_hash[hash_indx] = new_node;
word_size++;
}
fclose(dic);
return true;
}
the following proposed code:
cleanly compiles
still has a major problem with the function: hash_func()
separates the definition of the struct from the typedef for that struct for clarity and flexibility.
properly formats the #define statements
properly handles errors from fopen() and malloc()
properly limits the length of the string read from the 'dictionary' file
assumes that no text from the 'dictionary' file will be greater than 45 bytes.
and now, the proposed code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
//prototypes
bool load(const char *dictionary);
int hash_func(char* hash_val);
#define HASH_SIZE 65536
#define LENGTH 45
struct node
{
char word[LENGTH + 1];
struct node* next;
};
typedef struct node node;
node* global_hash[HASH_SIZE] = {NULL};
int word_size = 0;
int hash_func(char* hash_val)
{
int h = 0;
for ( size_t i = 0, j = strlen(hash_val); i < j; i++)
{
h = (h << 2) ^ hash_val[i];
}
return h % HASH_SIZE;
}
bool load(const char *dictionary)
{
char string[ LENGTH+1 ];
FILE* dic = fopen(dictionary, "r");
if(dic == NULL)
{
perror( "fopen failed" );
//fprintf(stdout, "Error: File is NULL.");
return false;
}
while( fscanf( dic, "%45s", string) == 1 )
{
node* new_node = malloc(sizeof(node));
if(new_node == NULL)
{
perror( "malloc failed" );
return false;
}
strcpy(new_node->word, string);
new_node->next = NULL;
int hash_indx = hash_func(new_node->word);
// following statement for debug:
printf( "index returned from hash_func(): %d\n", hash_indx );
if( !global_hash[hash_indx] )
{
global_hash[hash_indx] = new_node;
}
else
{
new_node->next = global_hash[hash_indx];
global_hash[hash_indx] = new_node;
}
word_size++;
}
fclose(dic);
return true;
}
First I will explain what I want to program. I want to open a directory and get all the files names and their number (in the directory). I want to allocate certain amount of memory for the number of files I have found, and a certain amount of memory for their names. I will give an example.
Lets say we have got a directory named dir and that directory has 4 files in it. The 4 files are named as follows: lalo, camo, mara, sarw.
I want my program to work as follows
Go into dir and while finding files keep allocating memory in order to store them (like a table of strings where I don't know the exact size because I don't know how many files I will find). I am storing the filenames character by character and I keep allocating memory as needed, every time I read one char I am realocating memory and incrementing by 1. Same logic I follow to the files number where I keep incrementing the number of files and reallocating memory as needed. A table like this (pointers) is what I imagine filenames[numberOfFiles][stringCharacter].
I get a segmentation fault in function GetFiles(...) on the second loop here : *(*(entries+*number)+size) = dp->d_name[size];
int main()
{
int number,i;
char *mainDirectory = NULL,**names = NULL;
printf("Give the movies directory: ");
mainDirectory = ReadMainDirectory();
if(GetFiles(mainDirectory,&names,&number) != 0)
{
system("PAUSE");
return 1;
}
system("PAUSE");
return 0;
}
char* ReadMainDirectory()
{
char *dir,c;
int size = 1;
dir = (char*)malloc(size+1);
dir[size-1] = '\0';
while((c = getchar()) != '\n')
{
dir[size-1] = c;
size++;
dir = (char*)realloc(dir,size+1);
dir[size-1] = '\0';
}
return dir;
}
int GetFiles(char *dir,char ***names,int *number)
{
struct dirent *dp;
DIR *fd;
int size;
char **entries = NULL;
if ((fd = opendir(dir)) == NULL)
{
printf("Can't open directory %s!\n",dir);
return 0;
}
*number = 0;
size = 0;
entries = (char**)malloc((*number+1) * sizeof(char*));
*entries = (char*)malloc((size+1) * sizeof(char));
while ((dp = readdir(fd)) != NULL)
{
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
{
continue;
}
size = 0;
while(dp->d_name[size] != '\0')
{
*(*(entries+*number)+size) = dp->d_name[size];
size++;
*(entries+*number) = (char*)realloc(entries[*number],(size+1) * sizeof(char));
}
entries[*number][size] = '\0';
printf("%s\n",entries[*number]);
(*number)++;
entries = (char**)realloc(entries,(*number+1) * sizeof(char*));
}
*names = entries;
closedir(fd);
return 1;
}
int GetStringSize(char *string)
{
int size = 0;
while(string[size] != '\0')
{
size++;
}
return size;
}
int StringEndsWith(char* string,char* extension)
{
return !strcmp(string + strlen(string) - strlen(extension), extension);
}
A proper implementation of getfiles (gets all the files in a directory in a dynamic array) and provides a deallocator:
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
/* returns a NULL terminated array of files in directory */
char **getfilelist(const char *dirname) {
DIR *dp;
char **entries = NULL;
struct dirent *entry;
off_t index = 0;
dp = opendir(dirname);
if (!dp) {
perror(dirname);
return NULL;
}
while((entry = readdir(dp)) != NULL) {
/* increase entries array size by one pointer-size */
entries = realloc(entries, (1+index)*sizeof(char *));
/* strdup returns a newly allocated duplicate of a string that
* you must free yourself
*/
entries[index] = strdup(entry->d_name);
index++;
}
/* need one more entry for NULL termination */
entries = realloc(entries, (1+index)*sizeof(char *));
entries[index] = NULL;
return entries;
}
void putfilelist(char **entries) {
char **p;
if(!entries)
return;
for(p = entries; *p !=NULL ; p++) {
free(*p); /* free all the strdups */
}
free(entries); /* free the array of pointers */
}
An example of how you could use it:
int main(int argc, char **argv) {
char **p, **entries;
char *dir = (argc == 1 ? "." : argv[1]);
entries = getfilelist(dir);
for (p = entries; p && *p; p++) {
printf("%s\n", *p);
}
putfilelist(entries);
}
Updated solution,
In order to implement your solution without using any of the library code, and keep it to system calls, here is an example that implements everything that the above example does, without relying on higher-level library calls.
Note
The bottom functions are the same as the above example.
/*
* A partially low-level implementation using a direct system calls
* and internal memory management
*
* This is an opinionated (linux only) implementation of OP
*
* linux headers are only included for some of the constants and
* where it would be trivial to re-implement system calls (e.g. open, write etc.)
* which I am too lazy to do in this example.
*
*/
#define NDEBUG
#include <stdarg.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/fcntl.h>
#include <linux/unistd.h>
/* replace this macro with your own error handling messages */
#define handle_error(msg) \
do { my_fdputs(2, msg); my_exit(-127); } while (0)
#if !defined(NDEBUG)
#define assert(x) do { int __rv = (x); if(!__rv) { my_fdprintf(2, "assertion failed %s\n", #x); my_exit(__rv); } } while(0)
#else
#define assert(x) do {} while(0)
#endif
/* returns a NULL terminated array of files in directory */
/* low-level list builder using getdents */
void my_exit(int x)
{
syscall(SYS_exit_group, x);
}
/* a trivial malloc / realloc / free */
/* simple linked list memory accounting */
struct memblock {
struct memblock *next;
size_t size;
int free; /* flag */
int magic; /* canary value for debugging */
};
#define METASIZE sizeof(struct memblock)
void *global_base = NULL;
struct memblock *find_free_block(struct memblock **last, size_t size)
{
struct memblock *current = global_base;
while(current && !(current->free && current->size >= size)) {
*last = current;
current = current->next;
}
return current;
}
/*
* instead of using sbrk, we should really use mmap on newer
* linux kernels and do better accounting, however this is a
* simple example to get you started
*/
struct memblock *request_space(struct memblock *last, size_t size)
{
struct memblock *block;
void *request;
block = sbrk(0); /* get current program break */
request = sbrk(size + METASIZE);
assert((void *)block == request);
if(request == (void *)-1) {
return NULL;
}
if(last) {
last->next = block;
}
block->size = size;
block->next = NULL;
block->free = 0;
block->magic = 0x12345678;
return block;
}
struct memblock *get_memblock_ptr(void *ptr)
{
return (struct memblock *)ptr - 1;
}
/* a simple memcpy, can be optimized by taking alignment into account */
void *my_memcpy(void *dst, void *src, size_t len)
{
size_t i;
char *d = dst;
const char *s = src;
struct memblock *bd, *bs;
bd = get_memblock_ptr(dst);
for(i = 0; i < len; i++) {
d[i] = s[i];
}
return dst;
}
/* now to implement malloc */
void *my_malloc(size_t size)
{
struct memblock *block;
if(size == 0)
return NULL;
if(!global_base) {
block = request_space(NULL, size);
if(!block)
return NULL;
global_base = block;
}
else {
struct memblock *last = global_base;
block = find_free_block(&last, size);
if(!block) {
block = request_space(last, size);
if(!block) {
return NULL;
}
}
else {
block->free = 0;
block->magic = 0x77777777;
}
}
return (block+1);
}
void my_free(void *ptr)
{
struct memblock *block;
if (!ptr)
return;
block = get_memblock_ptr(ptr);
assert(block->free == 0);
assert(block->magic == 0x77777777 || block->magic == 0x12345678);
block->free = 1;
block->magic = 0x55555555;
}
void *my_realloc(void *ptr, size_t size)
{
struct memblock *block;
void *newptr;
if(!ptr)
return my_malloc(size);
block = get_memblock_ptr(ptr);
if(block->size >= size)
return ptr;
newptr = my_malloc(size);
if(!newptr) {
return NULL;
}
my_memcpy(newptr, ptr, block->size);
my_free(ptr);
return newptr;
}
/* trivial string functions */
size_t my_strlen(const char *src) {
size_t len = 0;
while(src[len])
len++;
return len;
}
char *my_strdup(const char *src)
{
char *dst;
char *p;
size_t len = 0, i;
len = my_strlen(src);
dst = my_malloc(1+len);
if(!dst)
return NULL;
for(i = 0; i < len; i++) {
dst[i] = src[i];
}
dst[i] = 0;
return dst;
}
/* trivial output functions */
my_fdputs(int fd, const char *str)
{
return write(fd, str, my_strlen(str));
}
int my_fdputc(int fd, char c)
{
return write(fd, &c, sizeof(char));
}
/* a very limited implementation of printf */
int my_fdvprintf(int fd, const char *fmt, va_list ap)
{
const char *p;
int count = 0;
for(p = fmt; p && *p; p++ ) {
if(*p == '%') {
p++;
switch(*p) {
case 's':
count += my_fdputs(fd, va_arg(ap, char *));
break;
case '%':
count += my_fdputc(fd, '%');
break;
default:
#ifndef NDEBUG
my_fdputs(2, "warning: unimplemented printf format specifier %");
my_fdputc(2, *p);
my_fdputc(2, '\n');
#endif
break;
}
}
else {
my_fdputc(fd, *p);
}
}
return count;
}
int my_fdprintf(int fd, const char *fmt, ...)
{
int rv;
va_list ap;
va_start(ap, fmt);
rv = my_fdvprintf(fd, fmt, ap);
va_end(ap);
return rv;
}
/* wrapper to linux getdents directory entry call */
/* linux dirent structure */
struct linux_dirent {
long d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[];
};
/* system call wrapper */
int getdents(int fd, void *buf, size_t bufsize)
{
return syscall(SYS_getdents, fd, buf, bufsize);
}
/* reimplement getfilelist using our getdents */
#define BUF_SIZE 1024
char **getfilelist(const char *dirname) {
int fd, nread;
char **entries = NULL;
off_t index = 0;
char buf[BUF_SIZE];
struct linux_dirent *d;
int bpos;
/* O_DIRECTORY since kernel 2.1 */
fd = open(dirname, O_DIRECTORY|O_RDONLY);
if (fd < 0) {
handle_error(dirname);
}
for(;;) {
nread = getdents(fd, buf, BUF_SIZE);
if (nread == -1)
handle_error("getdents");
if (nread == 0)
break;
for (bpos = 0; bpos < nread;) {
d = (struct linux_dirent *) (buf + bpos);
entries = my_realloc(entries, (1+index) * sizeof(char *));
entries[index++] = my_strdup(d->d_name);
bpos += d->d_reclen;
}
}
/* need one more entry for NULL termination */
entries = my_realloc(entries, (1+index)*sizeof(char *));
entries[index] = NULL;
close(fd);
return entries;
}
void putfilelist(char **entries) {
char **p;
if(!entries)
return;
for(p = entries; *p !=NULL ; p++) {
my_free(*p); /* free all the strdups */
}
my_free(entries); /* free the array of pointers */
}
int main(int argc, char **argv) {
char **p, **entries;
char *dir = (argc == 1 ? "." : argv[1]);
entries = getfilelist(dir);
for (p = entries; p && *p; p++) {
my_fdprintf(1, "%s\n", *p);
}
putfilelist(entries);
}
Hope you enjoy
EDIT: I should add how I have this all set up. The struct definition and prototypes are in mystring.h. The function definitions are in mystring.c. The main is in mystringtest.c. For mystring.c and mystringtest.c, I have #include "mystring.h" at the top. I'm compiling like gcc -o test.exe mystring.c mystringtest.c. Not sure if any of that matters, but I'm new with C so I'm just trying to include everything.
I have a good deal of experience with Java but am pretty new to C. I imagine this is related to pointers and memory but I'm totally at a loss here for what's going on. Here's my code:
typedef struct {
char *chars;
int length;
int maxSize;
} String;
int main() {
char *a;
a = readline();
String *s = newString(a);
int b = length(s);
printf("length is %d \n", b);
}
I run the program and enter "hello" (as prompted by readline()). I've stepped through the program and after length(s), s->chars is still a pointer to the array of chars 'hello'. After the print statement, s->chars becomes a pointer to the array of chars 'Length is %d \n'. I'm totally at a loss for what I'm doing wrong. I'm working on a virtual machine if that matters at all. Any help is greatly appreciated. I'll give the code for newString and length too.
int length(String *s) {
char *temp = s->chars;
char b = *temp;
int count;
if (b == '\0') { count = 0; }
else { count = 1; }
while (b != '\0') {
b = *(temp+count);
count++;
}
return count;
}
String *newString(char *s) {
String st;
st.length = 20;
st.maxSize = MAXCHAR;
char *temp = malloc(20 * sizeof(char));
char b = *s;
int count = 0;
while (b != '\0') {
*(temp + count) = b;
count++;
b = *(s+count);
if (count == st.maxSize) { break; }
if (count == st.length) {
st.length = st.length + 20;
temp = realloc(temp, st.length * sizeof(char));
}
}
st.chars = temp;
return &st;
}
String *newString(char *s) {
String st;
...
return &st;
}
You are returning a pointer to a local variable. After newString returns, the local variable no longer exists, so you have a dangling pointer.
Either allocate st with malloc, or return it by value.
you must null terminate the string after the while loop, you have not left space for the null terminator. Also I don't see why you need to realloc
//using strlen will eliminate the need for realloc, +1 is for the null terminator
int len = strlen(s)
char *temp = malloc((len * sizeof(char)) +1);
//null terminate
*(temp+count) = '\0';
st.chars = temp;