At the end of the method, all my test printfs prints the same results. The last line of the file. But current printf in the while loop is working correctly. For some reason my nodes have all the same results. How can I fix it?
This is my struct unit:
struct unit
{
struct unit * next;
char *name;
};
This is my function for linked list adding lines one by one to the linked list:
void readFile(char fileName[], struct unit * units)
{
FILE * fp;
char *line = NULL;
int length = 1000;
fp = fopen(fileName, "r");
int counter = 0;
int strLength = 0;
struct unit * current;
units = (struct units*)malloc(sizeof(struct unit));
current = units;
while ( getline(&line, &length, fp) != -1)
{
strLength = strlen(&line);
current->name = (char*)malloc(sizeof(char)* strLength);
current->next = (struct units*)malloc (sizeof(struct unit));
strcpy(¤t->name, &line);
printf("\nCurrent: %s",current->name);
current = current->next;
counter++;
}
printf("\nTest %s", units->name);
printf("\nTest %s", units->next->name);
printf("\nTest %s", units->next->next->name);
printf("\nTest %s", units->next->next->next->name);
}
Why are you passing in &line into strlen and strcpy? If I remember correctly, you should just pass in line and current->name into these functions. (I don't know about getline though; maybe that's fine as-is.)
This worked for me (Built and run with a file with several lines. I had to change the getline function for my compiler: also changed several "units" for "unit" which is the name of the struct. Also the line for buffering is statically reserved with a maximum length of 255 characters):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct unit{
struct unit * next;
char *name;
};
void readFile(char fileName[], struct unit * units){
FILE * fp;
char line[255];
int length = 1000;
fp = fopen(fileName, "r");
int counter = 0;
int strLength = 0;
struct unit * current;
units = (struct unit*)malloc(sizeof(struct unit));
current = units;
while ( fgets ( line, sizeof line, fp ) != NULL ) /* read a line */
{
strLength = strlen(line);
current->name = (char*)malloc(sizeof(char)* strLength);
current->next = (struct unit*)malloc (sizeof(struct unit));
strcpy(current->name, line);
printf("\nCurrent: %s",current->name);
current = current->next;
counter++;
}
fclose ( fp );
printf("\nTest %s", units->name);
printf("\nTest %s", units->next->name);
printf("\nTest %s", units->next->next->name);
printf("\nTest %s", units->next->next->next->name);
}
int main(){
readFile("filename.txt", NULL);
}
Your code has several bad practices, and several bugs. You do not need to preallocate a node before entering your loop. You can simply allocate as-needed. There are a number of ways to ensure the newly allocated node is added to the end of the list using a technique called forward-chaining. I'll get to that in a minute. The following list is in no particular order, but I at least tried to work it top-down
Major: Your readFile() function should return the head of the list it is allocating. If you want to join this to some other list after that, feel free, but the function should start with this:
struct unit* readFile(const char fileName[])
Minor: Note also we're not modifying the file name, so there is no reason to pass it as mutable, thus it is non-const.
Major: Check your file open operation for success before using it:
fp = fopen(fileName, "r");
if (fp == NULL)
{
perror("Failed to open file.");
return NULL;
}
Major: Use properly typed variable for the API calls your making. The function getline(), a non-standard extension, is prototyped as:
ssize_t getline(char ** , size_t * , FILE *)
It returns a ssize_t (a "signed" size-type) and takes a size_t* for the second parameter. You're passing the address of an int variable, length, as the second parameter. This is no guarantee the two are compatible types. Fix this by declaring length as the proper type; size_t
size_t length = 0;
Minor: The same issue happens with the return value type of strlen(), which is also size_t, but that will become unimportant in a moment as you'll soon see.
Major: Your use of getline() apart from the second parameter mentioned before is almost correct. The initial input on the first loop is the address of a NULL pointer and a 0-valued length. With each iteration if the buffer already allocated in the previous loop is big enough, it is reused. Unfortunately reading a shorter line, then a longer, then a shorter, and then longer will introduce extra allocates that aren't needed. In fact, You can forego your malloc() logic entirely and just use getline() for allocating your buffer, since it is documented to use malloc() compatible allocation. Therefore, using your existing logic (which we will go over lastly):
while ( getline(&line, &length, fp) != -1)
{
// note: added to throw out empty lines
if (length > 0)
{
// note: added to null out trailing newline. see the
// documentation of getline() for more info.
if (line[length-1] == '\n')
line[length-1] = 0;
}
if (line[0] != 0)
{
// other code here
current->name = line;
}
else
{ // not using this. release it.
free(line);
}
// reset line and length for next iteration
line = NULL;
length = 0;
}
Major: Your original algorithm never free()d the line buffer once you were done with it, thereby introducing a one-time memory leak. Using the above alternative, you need not worry about it.
Alternate: Finally, the list population loop can be made more robust by applying everything discussed so far, and adding to it a technique called forward-chaining. This technique uses a pointer-to-pointer pp that always holds the address of the pointer that will receive the next node allocation. If the list is initially empty( and it is), it holds the address of the head pointer. With each new node added pp is assigned the address of the last node's next member. When the loop is complete (even if i didn't add any nodes), we finish by setting *pp = NULL to terminate the list.
This is the final code base for readFile. I hope you find it useful:
struct unit* readFile(char fileName[])
{
FILE * fp;
char *line = NULL;
size_t length = 0;
// used for populating the list
struct unit *head = NULL;
struct unit **pp = &head;
// open file
fp = fopen(fileName, "r");
if (fp == NULL)
{
perror("Failed to open file");
return NULL;
}
while ( getline(&line, &length, fp) != -1)
{
// note: added to throw out empty lines
if (length > 0)
{
// note: added to null out trailing newline. see the
// documentation of getline() for more info.
if (line[length-1] == '\n')
line[length-1] = 0;
}
if (line[0] != 0)
{
// allocate new node
*pp = malloc(sizeof(**pp));
if (*pp != NULL)
{
(*pp)->name = line;
pp = &(*pp)->next;
}
else
{ // could not allocate a new node. uh oh.
perror("Failed to allocate new node");
free(line);
break;
}
}
else
{ // not using this. release it.
free(line);
}
// reset line and length for next iteration
line = NULL;
length = 0;
}
*pp = NULL;
return head;
}
Related
I'm working on a crosswords program in which a word dictionary is necessary. I'm trying load a jspell dictionary file into an dynamic string array but i keep getting the
error malloc(): mismatching next->prev_size (unsorted)
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "dictionary.h"
void dict_init(Dictionary * dict, char * dict_dir, size_t w_len)
{
printf("dictionary.c (dict_init): initializing dictionary.\n");
/*Adjust this value to control the initial array size*/
size_t init_size = 1000;
/*initialize dictionary file directory*/
dict->dir = malloc(strlen(dict_dir) * sizeof(char) + 1);
strcpy(dict->dir, dict_dir);
/*create memory for words array*/
dict->words = malloc(init_size * sizeof(char *));
/*initialize array size*/
dict->size = init_size;
/*initilize word length*/
dict->w_len = w_len;
/*initialize word counter*/
dict->counter = 0;
/*load words into dictionary*/
dict_load(dict);
printf("dictionary.c (dict_init): dictionary initialized.\n");
}
void dict_add(Dictionary * dict, char * word)
{
char ** dictionary = dict->words;
/*check if word array is full*/
if(dict->counter == dict->size)
{
/*increrase size of dictionary*/
dict->size *= 1.5;
dict->words = realloc(dict->words, dict->size * sizeof(char *));
}
/*add word to dictionary*/
dictionary[dict->counter] = malloc(strlen(word) * sizeof(char) + 1);
strcpy(dictionary[dict->counter], word);
dict->counter++;
free(word);
}
void dict_free(Dictionary * dict)
{
free(dict->words);
}
void dict_load(Dictionary * dict)
{
FILE * fp;
char * line = NULL;
char * word = NULL;
size_t len = 0;
ssize_t read;
fp = fopen(dict->dir, "r");
/*check if file exists*/
if (fp == NULL)
{
perror("ERROR: File not found.");
exit(EXIT_FAILURE);
}
/*discard first line*/
if(strstr(dict->dir, ".dic"))
getline(&line, &len, fp);
/*read file lines*/
while ((read = getline(&line, &len, fp)) != -1)
{
if(((strstr(line, "[CAT=punct") == NULL) && (word = parse_line(line, dict->w_len)) != NULL)) {
dict_add(dict, word);
}
}
fclose(fp);
free(line);
printf("dictionary.c (dict_load): dictionary loaded %ld words.\n", dict->counter);
}
char * parse_line(char * line, size_t w_len)
{
int i;
char s_tmp[101] = "";
char * dlm_slash, * dlm_space, * dlm_tab , *substring;
/*get delimiter pointer*/
dlm_slash = strchr(line, '/');
dlm_space = strchr(line, ' ');
dlm_tab = strchr(line, '\t');
/*check if delimiter exists in line*/
if(dlm_slash != NULL)
i = (int)(dlm_slash - line);
else if(dlm_space != NULL)
i = (int)(dlm_space - line);
else if(dlm_tab != NULL)
i = (int)(dlm_tab - line);
else
{
/*replace '\n' with '\0'*/
line[strcspn(line, "\n")] = '\0';
i = strlen(line);
}
strncpy(s_tmp, line, i);
substring = malloc(sizeof(char) * strlen(s_tmp) + 1);
strncpy(substring, s_tmp, strlen(s_tmp));
/*lowercase word*/
lower_case(substring);
if((is_valid(substring) == 0) && (strlen(substring) <= w_len))
return substring;
free(substring);
return NULL;
}
Here's the basic problem, I think:
void dict_add(Dictionary * dict, char * word) {
char ** dictionary = dict->words; /* **** 1 **** */
/*check if word array is full*/
if(dict->counter == dict->size)
{
/*increrase size of dictionary*/
dict->size *= 1.5; /* **** 2 **** */
dict->words = realloc(dict->words, dict->size * sizeof(char *));
/* **** 3 **** */
}
/*add word to dictionary*/
This one is the problem:
dictionary[dict->counter] = malloc(strlen(word) * sizeof(char) + 1);
strcpy(dictionary[dict->counter], word);
dict->counter++;
free(word); /* **** 4 **** */
}
The problem is that dictionary was saved before you called realloc. realloc might make a brand-new memory allocation, in which case it will automatically free() the old one after copying its contents into the new one. So any copy of the pointer which you made before calling realloc might end up pointing to unallocated memory. Writing to unallocated memory is a big no-no; in this particular case, you're probably overwriting malloc's bookkeeping information about the unallocated block, which is why it detects the problem and complains. Count yourself lucky: lots of memory corruption problems go undetected for quite a while until the factory explodes.
Some other issues which I noticed while writing this, with numbered comments in the source:
There's actually no need for the variable dictionary at all.
dict->size is an integer. Forcing conversion to a floating point number and then truncating back to an integer is not very useful. Prefer dict->size += dict->size/2;. Even better would be to first make sure that dict->size isn't so big that increasing it will cause integer wraparound. (This is not undefined behaviour on unsigned types like size_t, but it's not going to produce correct results.)
Here you could actually use a temporary, because realloc might return NULL indicating a memory allocation failure. If that happens, the original allocation is not automatically freed, and you don't have a way to free it. (Actually you do, since you have a variable confusingly called dictionary, but in point 1 I recommended that you get rid of it.) A more idiomatic call would be:
if(dict->counter == dict->size) {
/*increrase size of dictionary*/
dict->size += dict->size / 2; /* See point 2, above */
char** new_words = realloc(dict->words, dict->size * sizeof(*new_words));
if (new_words == NULL) {
/* Report allocation error and free all the memory you've allocated */
/* Then probably exit(1) but if this were a library function, just
* return some kind of failure indication so that the caller can do
* their own clean-up.
*/
}
dict->words = new_words;
}
dict->words[dict->counter] = word; /* See point 4, below */
You're freeing word here because it was allocated in parse_line(). But if you know you're going to free it anyway, there wasn't much point making a copy of it first. You might as well just use it. (But you need to document the fact that this function takes ownership of the word passed as an argument.)
It might be considered cleaner to do the copy as you do but then not free the argument, leaving it for the caller to do that. That would have the advantage of allowing the caller to provide a word which hadn't been dynamically allocated, or use the word for some other purpose.
(Not indicated in this snippet, but nonetheless important). Every block of allocated memory must be freed. So your program should execute free exactly as many times as it executed malloc. But you don't do that; you just free the array of word pointers, and let the words pointed to in that array leak. You should fix that. (Note that you don't need an extra call to free for a call to realloc, since realloc itself frees the old block if it allocates a new one. You only need to match the initial malloc with a free.)
trying to parse a csv file containing lines in this format
1001,Kauri tree,1002,-1,1001,1001
and the struct node has attributes: id, name and then paths[4] // (4 paths)
my code is not working because it seg faults. I'm on a mac so I valgrind doesn't work for me. Can anyone help with my code? or better yet give me another option for debugging? I am using the Geany IDE.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
#include <string.h>
#define MAX_NAME_LENGTH 20
#define MAX_LINE_LENGTH 100
typedef struct node_s {
int id;
char name[MAX_NAME_LENGTH + 1];
int paths[4];
} Node;
Node readNode(FILE *infile)
{
char buffer[MAX_LINE_LENGTH];
Node *node = NULL;
char* tok;
const char comma[2] = ",";
char* inputLine = fgets(buffer, MAX_LINE_LENGTH, infile);
if (inputLine != NULL) {
tok = strtok(buffer, comma);
if (tok == NULL) {
node->id = -1;
} else {
node->id = *tok;
tok = strtok(NULL, comma);
strncpy(node->name, tok, MAX_NAME_LENGTH);
int i = 0;
while(tok != NULL || i < 4) {
tok = strtok(NULL, comma);
node->paths[i] = atoi(tok);
i++;
}
}
}
return *node;
}
int main(void)
{
FILE* infile = fopen("new_list.txt", "r");
Node node = readNode(infile);
while (node.id >= 0) {
printf("Node: id = %d, name = '%s', neighbours = [%d, %d, %d, %d]\n",
node.id, node.name,
node.paths[0], node.paths[1], node.paths[2], node.paths[3]);
node = readNode(infile);
}
}
Your primary problem in readNode() is your declaration of Node *node = NULL; declares a pointer that is initialized NULL and points to no valid storage. You then attempt to assign values to the memory pointed to by node (e.g. NULL) invoking Undefined Behavior and almost guaranteeing a SegFault.
You have several options to handle the issue,
provide storage for node in main() and pass as a parameter -- declare node in main() with automatic storage and pass the address of the struct as an additional parameter to readNode(), filling the values within the function (readNode can be declared as void in this case). You can also dynamically allocate node in main() and simply pass the pointer as an additional parameter,
allocate storage for node in readNode() and return a pointer -- dynamically declare storage for node within readNode() using malloc or calloc (don't forget to free the storage at the end of each loop to prevent a memory leak), or
declare readNode() as type Node -- the easiest way is simply to declare node within readNode and rely on the fact that a function can always return its own type to return a filled struct to main() as noted in the comments.
Either way will eliminate your Segmentation Fault as valid storage will be provided for node both in readNode and in main().
While not an error, the standard coding style for C avoids the use of camelCase or MixedCase variable names in favor of all lower-case while reserving upper-case names for use with macros and constants. It is a matter of style -- so it is completely up to you, but failing to follow it can lead to the wrong first impression in some circles. readnode is just a readable readNode, etc..
While it is perfectly fine to pass FILE *infile is a parameter to readnode(), it does you absolutely no good, and invites Undefined Behavior if you fail to validate that infile is open for reading before calling readnode() in main(). Library functions (and your functions should) provide a meaningful return to allow you to determine if the function call succeeded or failed -- use them, always.
Don't hard-code numbers or strings in your code (this is called using magic numbers -- don't do it). Instead, if you need a constant #define one (or more) as you have with MAX_NAME_LENGTH and MAX_LINE_LENGTH, and don't skimp on buffer sizes, e.g.
#include <stdio.h>
#include <string.h>
#define NPATHS 4
#define MAX_NAME_LENGTH 64 /* don't skimp on buffer size */
#define MAX_LINE_LENGTH 512
typedef struct node_s {
int id;
char name[MAX_NAME_LENGTH + 1];
int paths[NPATHS];
} node_t;
(note: an exception is when numeric values are required in your code such as when specifying the scanf field-width modifier, etc. where a defined constant or variable is not allowed)
Reading with fgets is a good approach, but you need to validate that a complete line was read by checking the length against MAX_LINE_LENGTH - 1 and that the last character in the buffer is the newline character. Further, while parsing with strtok is fine, when reading formatted input, calling sscanf on the buffer filled by fgets and validating the number of conversions simplifies the conversion process. It is also helpful to fill a temporary struct with values when parsing with sscanf to protect against some number of members less than all being filled and rendering your node.id >= 0 check invalid in main(). You could do something like the following:
node_t readnode (FILE *infile)
{
char buffer[MAX_LINE_LENGTH];
node_t node = {.id = -1}; /* initialize node to indicate error */
if (fgets (buffer, MAX_LINE_LENGTH, infile)) { /* validate line read */
node_t tmp = {.id = 0}; /* parse to temporary node */
size_t len = strlen (buffer); /* get length */
/* validate complete line read */
if (len == MAX_LINE_LENGTH - 1 && buffer[len-1] != '\n') {
/* handle line too long */
fputs ("error: line too long.\n", stderr);
/* discard remaining characters in line */
while (fgets (buffer, MAX_LINE_LENGTH, infile)) {
len = strlen (buffer);
if (len && buffer[len-1] == '\n')
break;
}
}
/* parse csv values using sscanf, validate return */
if (sscanf (buffer, "%d,%64[^,],%d,%d,%d,%d", &tmp.id, tmp.name,
&tmp.paths[0], &tmp.paths[1], &tmp.paths[2],
&tmp.paths[3]) == 6)
node = tmp; /* good parse, assign tmp to node */
else /* parse failed, issue error, return zeroed node */
fputs ("readnode() error: parse of line failed.\n", stderr);
}
return node; /* return filled node on success, zeroed node otherwise */
}
Putting all the pieces together in a short example based on your code, you could do something similar to to the following that will read from the filename provided as the first-argument to your program (or from stdin by default if no argument is given):
#include <stdio.h>
#include <string.h>
#define NPATHS 4
#define MAX_NAME_LENGTH 64 /* don't skimp on buffer size */
#define MAX_LINE_LENGTH 512
typedef struct node_s {
int id;
char name[MAX_NAME_LENGTH + 1];
int paths[NPATHS];
} node_t;
node_t readnode (FILE *infile)
{
char buffer[MAX_LINE_LENGTH];
node_t node = {.id = -1}; /* initialize node to indicate error */
if (fgets (buffer, MAX_LINE_LENGTH, infile)) { /* validate line read */
node_t tmp = {.id = 0}; /* parse to temporary node */
size_t len = strlen (buffer); /* get length */
/* validate complete line read */
if (len == MAX_LINE_LENGTH - 1 && buffer[len-1] != '\n') {
/* handle line too long */
fputs ("error: line too long.\n", stderr);
/* discard remaining characters in line */
while (fgets (buffer, MAX_LINE_LENGTH, infile)) {
len = strlen (buffer);
if (len && buffer[len-1] == '\n')
break;
}
}
/* parse csv values using sscanf, validate return */
if (sscanf (buffer, "%d,%64[^,],%d,%d,%d,%d", &tmp.id, tmp.name,
&tmp.paths[0], &tmp.paths[1], &tmp.paths[2],
&tmp.paths[3]) == 6)
node = tmp; /* good parse, assign tmp to node */
else /* parse failed, issue error, return zeroed node */
fputs ("readnode() error: parse of line failed.\n", stderr);
}
return node; /* return filled node on success, zeroed node otherwise */
}
int main (int argc, char **argv)
{
node_t node = {.id = 0};
FILE *infile = argc > 1 ? fopen (argv[1], "r") : stdin;
/* validate file open for reading */
if (infile == NULL) {
perror ("fopen-infile");
return 1;
}
node = readnode (infile); /* struct can be assigned */
while (node.id >= 0) {
printf ("Node: id = %4d, name = '%s',%*s"
"neighbours = [%d, % 4d, %d, %d]\n",
node.id, node.name, 11 - (int)strlen(node.name), " ",
node.paths[0], node.paths[1], node.paths[2], node.paths[3]);
node = readnode (infile);
}
if (infile != stdin) fclose (infile); /* if not stdin, close file */
return 0;
}
Example Input File
$ cat dat/struct_node.csv
1001,Kauri tree,1002,-1,1001,1001
1002,Beach tree,1003,-2,1002,1002
1003,Pine tree,1004,-10,1003,1003
1004,Elm tree,1005,-100,1004,1004
Example Use/Output
$ ./bin/struct_rd_csv_node <dat/struct_node.csv
Node: id = 1001, name = 'Kauri tree', neighbours = [1002, -1, 1001, 1001]
Node: id = 1002, name = 'Beach tree', neighbours = [1003, -2, 1002, 1002]
Node: id = 1003, name = 'Pine tree', neighbours = [1004, -10, 1003, 1003]
Node: id = 1004, name = 'Elm tree', neighbours = [1005, -100, 1004, 1004]
Look things over and let me know if you have further questions.
I have a problem with my code. I want to load a dictionary which works fine with a small one. But when i try to load the larger version, my while loop stops at the 701th word which is " acclimatization" and then the programs continues. I searched a lot on forums and tried a lot of things, but i just can't find the reason this is caused. Does anyone have an idea of how this occurs?
Dictionary.c
bool load(const char* dictionary)
{
// reserve space for word
char* word = malloc(sizeof(char*));
// open file
FILE* dict = fopen(dictionary, "r");
if (dict == NULL)
{
fclose(dict);
fprintf(dict, "Could not load %s.\n", dictionary);
return 1;
}
root = (struct node *) malloc(sizeof(struct node));
root->is_word = false;
//Loops over word aslong the EOF is not reached
while (fgets(word,LENGTH,dict) != NULL)
{
printf("word = %s\n", word);
int word_length = strlen(word) -1;
node* current = root;
word_count++;
//Loops over letters
for (int i = 0; i < word_length; i++)
{
int index;
node *next_node;
// checks if letter isnt a apostrophe
if(word[i] == 39)
{
index = MAX_CHARS - 1;
}
// gets nummeric value of letter
else
{
index = tolower(word[i]) - 'a';
}
next_node = current->children[index];
// creates new node if letter didnt exists before
if(next_node == NULL)
{
next_node = malloc(sizeof(node));
current->children[index] = next_node;
current->is_word = false;
printf("new letter: %c\n", word[i]);
}
else
{
printf("letter: %c\n", word[i]);
}
// checks for end of the word
if(i == word_length - 1)
{
next_node->is_word = true;
}
current = next_node;
}
}
return true;
}
The node is defined by:
// node
typedef struct node
{
bool is_word;
struct node* children[27];
}
node;
char* word = malloc(sizeof(char*));
Depending on platform it can be 4 or 8 . You need to allocate more memory.
char* word;
word = malloc(LENGTH); // LENGTH as you use it here while (fgets(word,LENGTH,dict) != NULL)
if(word!=NULL){ // and checking if malloc is successful
// your code
free(word); // freeing allocated memory
return true;
}
else { // executed only if malloc fails
//handle error
}
You can give any desired size.
Note - Using function free() , you need to free every time you allocate memory.
You allocate very little space for word, it's probably 8 or 4 bytes depending on your platform.
You are allocating space for 1 char pointer, so when you read from the file LENGTH characters you can be storing bytes beyond the limits of the allocated buffer. The problem is, that the behavior is undefined thus the program might work or it might stop or anything can happen.
You don't need to allocate it dynamically, just like this it's ok
char word[100];
while (fgets(word, sizeof(word), file) != NULL) ...
/* ^ this only works with arrays, */
/* the benefit is that you can */
/* change the definition of word */
/* and resize it without changing */
/* this part. */
/* */
/* It will NOT work if you use `malloc()' */
Also, you would have a memory leak if fopen() failes, every malloc() requires a corresponding free().
Suggestion:
for (int i = 0; i < word_length; i++)
can be written like this too
for (int i = 0; ((word[i] != '\n') && (word[i] != '\0')); i++)
and you avoid calling strlen() which will also iterate through the characters.
I'm in trouble with my code again.
I want to fscanf result.txt to structures with linked list, but it don't work;
I think the simply linked list must be enought;
The problem is: the program just write the first line, but nothing else.
result.txt format:
point name (for examples)
623 john
457 peter
312 chuck
etc.
The code:
#include <stdio.h>
#include <stdlib.h>
typedef struct ranklist {
int point;
char* name;
struct ranklist *next;
} ranklist;
int how_many_records(FILE *fp){
char ch;
int line=0;
int status;
rewind(fp);
while((status=fscanf(fp, "%*d %*[^\n]%c", &ch))==1)
++line;
if(status != EOF){
++line;
}
rewind(fp);
return line;
}
int how_many_letter(FILE *fp){
int letter = 0;
long pos = ftell(fp);
//fscanf(fp, " %*[^\n]%n", &letter);
fscanf(fp, " %*s%n", &letter);
fseek(fp, pos, SEEK_SET);
return letter;
}
int main(void){
FILE *fp = fopen("result.txt","r");
int name_length;
int lines = how_many_records(fp);
ranklist *r = malloc(lines * sizeof(*r));
ranklist *first = r;
for ( r=first ;r != NULL; r = r->next){
fscanf(fp, "%d", &(r->point));
name_length = how_many_letter(fp);
r->name = malloc(name_length + 1);
fscanf(fp,"%s", r->name);
}
fclose(fp);
for ( r=first ;r != NULL; r = r->next){
printf("%d %s\n", r->point, r->name);
}
free(r);
return 0;
}
fscanf(fp, "%d", &r[y].point);
Here, y is uninitialized.
You need to put y = 0, or, IMO, better to use &(r->point) [and the same goes for r[y].name also]
Suggestion: for reading and parsing whole lines better to use fgets()
You have multiple problems with the creation of the list.
Lets start with the loop:
for ( r=first ;r != NULL; r = r->next){
Nowhere in the loop do you initialize r->next, so after the first iteration you will make y point to totally random memory, leading to undefined behavior.
As mentioned in another answer you don't initialize the variable y, which is another cause of undefined behavior.
You also change r, so r will not point to the original memory you allocated so doing r[y] is a third cause of undefined behavior.
And after the loop you call free with the modified pointer y, which will also lead to undefined behavior. And you don't free the names you allocate in the loop, so have multiple memory leaks as well.
There is no need to pre-allocate the nodes at all, just allocate when needed.
Something like
ranklist *head = NULL;
FILE *fp = fopen(...);
// Read the file and create the list
char buffer[256];
while (fgets(buffer, sizeof(buffer), fp) != NULL)
{
ranklist *node = malloc(sizeof(ranklist));
node->name = malloc(strlen(buffer)); // Will allocate a little more than needed
sscanf(buffer, "%d %s", &node->point, node->name);
node->next = head;
head = node;
}
// Print the list
for (ranklist *n = head; n != NULL; n = n->next)
{
printf("%d %s\n", n->point, n->name);
}
// Free the list
while (head != NULL)
{
// Unlink the first node in the list
ranklist *n = head;
head = n->next;
// And free it
free(n->name);
free(n);
}
The code above doesn't have any error checking, it also have some overhead when allocating space for the name, but it will be safe, it will not handle unrealistically long names, and the list will actually be a stack (last item read will be first in the list).
Function to get the length of the name from the buffer read with fgets:
size_t get_name_length(char *buffer)
{
// Since the buffer was read with `fgets` we need to get rid
// of the newline at the end, if it's there
size_t length = strlen(buffer);
if (buffer[length - 1] == '\n')
buffer[length - 1] = '\0'; // "Remove" by terminating the string
// Find the space dividing the point and the name
char *space = strchr(buffer, ' ');
// Just in case there are multiple whitespace characters in the string
while (*space != '\0' && isspace(*space))
++space;
// Now `space` points to the first non-space letter in the name
// (or to the string terminator, if there's no name
// Then length of the name is the remainder of the string
return strlen(space);
}
FILE *fp = fopen("result.txt","r");
int name_length;
//Just create a link if you use as an array
//int lines = how_many_records(fp);
//ranklist *r = malloc(lines * sizeof(*r));
ranklist *first=NULL, *curr, *r;
int point;
while(1==fscanf(fp, "%d", &point)){
r = malloc(sizeof(*r));//allocate one record
r->next = NULL;
r->point = point;
name_length = how_many_letter(fp);
r->name = malloc(name_length + 1);
fscanf(fp, "%s", r->name);
//make link
if(first == NULL)
first = curr = r;
else
curr = curr->next = r;
}
fclose(fp);
for (r = first; r != NULL; r = r->next){
printf("%d %s\n", r->point, r->name);
}
for (r = first; r != NULL; ){
ranklist *tmp = r->next;//Save next for free(r)
free(r->name);
free(r);
r = tmp;
}
//output the list
void print(ranklist *first){
while(first != NULL){
printf("%d %s\n", first->point, first->name);
first = first->next;
}
}
I want to read a file and write each line to array of char. As I don't know the amount of lines, therefore I thought the most efficient way is to use 2D array of char pointer. However I get segmentation fault.
My question might be duplicate of this one :
2D array of char pointers --> Segmentation fault?
But I couldn't figure the correct syntax for C so I couldn't try.
Here's my code:
FILE *file = fopen ( filename, "r" );
if ( file != NULL )
{
char line [ 128 ]; /* or other suitable maximum line size */
char **new_line;
int i = 0;
while ( fgets ( line, sizeof line, file ) != NULL ) /* read a line */
{
strcpy(new_line[i], line);
i++;
}
Memory is not allocated for new_line which causes the segmentation fault.
If you know the no of lines, then you can declare that as local array itself. In that case your accessing method will works fine.
#define MAX_LINES 20
#define MAX_CHARS 128
...
char new_line[MAX_LINES][MAX_CHARS] = {0};
...
Your problem here is you dont know the maximum number of lines. So you have selected double pointer. In that case you need to first malloc with some n number of lines and then you need to keep on using realloc to increase the buffer size.
#define MAX_CHARS 128
#define N_NO_OF_LINES 10
...
char line[MAX_CHARS] = {0};
char **new_line = NULL;
int noOfLines = 0;
int lineCount = 0;
new_line = malloc(sizeof(char*) * N_NO_OF_LINES);
noOfLines = N_NO_OF_LINES;
while (fgets (line, sizeof line, file) != NULL) /* read a line */
{
if (lineCount >= noOfLines)
{
new_line = realloc(new_line, (sizeof(char*)*(noOfLines+N_NO_OF_LINES)));
noOfLines += N_NO_OF_LINES;
}
new_line[lineCount] = strdup(line);
lineCount++;
}
Note : Take care of null check for malloc and realloc
new_line is not initialized to a valid chunk of memory.
Roughly:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
FILE *file = fopen ( "test.txt", "r" );
if ( file != NULL )
{
#define MAXLINES 128
#define MAXLINELEN 100
char line [ MAXLINELEN ]; /* or other suitable maximum line size */
char **new_line = (char **)malloc(sizeof(char *) * MAXLINES);
int i = 0;
if (!new_line) exit(-1);
while ( i < MAXLINES && (fgets ( line, sizeof line, file ) != NULL )) /* read a line */
{
new_line[i] = strdup(line);
i++;
}
printf("read %d lines\n", i);
}
exit(0);
}
You didn't allocate any memory for the new_line array. You need something like:
char **new_line = malloc(sizeof(char *) * MAX_LINES);
And then for each line, don't use strcpy, which will copy into a garbage pointer (the uninitialized new_line array). You probably want strdup(3):
new_line[i] = strdup(line);
You declare new_line as a pointer to char *, but it is never initialized so it points to some invalid memory address. You get the error when you write to that address.
You will probably want to allocate the memory, assign it to new_line, and then you can copy strings to it.
You need to alloc space for your strings.
Malloc returns a memory slot with the size you want but doesn't allow memory reallocation. For that you have realloc.
With malloc you would end up with a fixed size for your table, just like if you had only declared it has static but with later initialization (Well, I'm a bit agains this sentence because malloc is much more than that, but for this purpose it is safe to say it).
Realloc does that, reallocates memory, but it can be pretty dangerous if you don't use i correctly. And, in my opinion, is not the most correct way to do it.
When you want to save something that you don't know the size, dynamic structures are the way to go.
You can use 'linked lists like' data structures so you can have as many words as you want and then convert that list to an array.
I would go with something like this:
typedef struct _words{ //Structure to the dynamic insertion of words
char *word;
struct _words *next;
}words;
words *last; //Easier implementation for this case. Not always the best solution
words *init(){ //Is good practice to start with an empty structure for reference passing
words *new = malloc(sizeof(words));
if(new == NULL) exit(0);
new->next = NULL; //A good end/next reference
return new;
}
void insertWord(char *word){
words *new = malloc (sizeof(words));
if(new == NULL) exit(0);
new->word = malloc(strlen(word)*sizeof(char));
if(new->word == NULL) exit(0);
new->next = NULL; //Or new->next = last->next; wich is also null.
last->next = new;
last = new;
}
int main(){ //Or the name of your function
FILE *file = fopen ( filename, "r" );
words *list = init();
last = list;
if ( file != NULL )
{
char line [ 128 ]; /* or other suitable maximum line size */
int i = 0;
while ( fgets ( line, sizeof line, file ) != NULL ) /* read a line */
{
insertWord(line);
i++;
}
//Here, you already have all the words in your dynamic structure. You can now save them into an array
char **array = malloc(i*sizeof(char*)); //i is the number of words.
if(array == NULL) exit(0);
word *aux = list->next;
if(aux != NULL){
for(int j=0;j<i && aux != NULL;j++){
array[j] = malloc(strlen(aux->word)*sizeof(char));
if(array[j] == NULL) exit(0);
strcpy(array[j], aux->word);
aux = aux->next; // jump to the next word
}
}
...
}
I think this might work but I didn't try it. Is just to give you an idea on how to implement dynamic structures.
It misses frees and is not an actual stack, even if is close.
Hope this helps.