I want to ultimately insert strings from a file into elements in structs and can't get it right. Can you see what is wrong here?
int main()
{
FILE *fp;
char file_name[10] = "map7.map";
fp = fopen(file_name,"r");
int a = 1;
int *pnumberOfRows = &a;
fscanf(fp,"%d",pnumberOfRows);
typedef struct {
bool visited;
char *cityName;
} map;
map *ver = malloc(sizeof(map)*2*(*pnumberOfRows));
fscanf(fp,"%s",ver[1].cityName);
printf("%s",ver[1].cityName);
return 0;
}
It seems like you're simply missing to allocate space for the char *cityName fields, which makes you fscanf onto an unallocated pointer. You could either provide a fixed-with field, e.g.
typedef struct {
bool visited;
char cityName[81];
} map;
for a maximum length of 80 characters (i.e. excluding \0) or determine the length of the city names in the file beforehand and then allocating memory to the field using
ver[0]->cityName = (char*)malloc(sizeof(char)*(stringLength+1));
Note that sizeof(char) == 1, so feel free to leave it away, but see the answers here for more information. I left it here for the sake of being expressive about what you want to achieve.
Also, don't forget to free the memory you malloc'd at the end and also close the file descriptor after you're done (i.e. fclose(fp);).
Related
This is my first post at stack overflow, and hope someone is able to point me in the correct direction. I am writing a C funtion where my goal is to read a csv file. The data in the file is then passed to an structure array, which I then would like to return to a function call in main(), accessing the data for further use. How do I properly read and then return the complete structure array?
This function is an addon to an existing PLC program, where at the moment all system parameters is stored in retain memory. The goal is to read/write parameters to a CSV file for backup.I suspect that I am doing something wrong in the while loop, but are at this point not able to figure out what. It could also be I am not using pointers correctly. The CSV file looks like this:
2;motor nominal current;1700
3;motor nominal speed;2500.0
4;motor nominal power;1200.0
5;motor nominal voltage;690.0
6;Enable motor heating;TRUE
7;Motor heating time on;40.0
I am by the way aware that I don`t free the memory allocated in the function. This will be handled further on.
Here is the program containing the function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BSIZE 80
struct parameter{
int id;
char *name;
char *value;
};
struct parameter* readCSV(const char *file)
{
char buffer[BSIZE];
FILE *f;
char *field;
// open the CSV file
f = fopen(file,"r");
if( f == NULL)
{
printf("Unable to open file '%s'\n",file);
exit(1);
}
static struct parameter *parameters[BSIZE];
int i = 0;
// read the data
while(fgets(buffer,BSIZE,f) != NULL)
{
parameters[i] =(struct parameter*)malloc(sizeof(struct parameter));
// get id
field = strtok(buffer,";");
parameters[i]->id = atoi(field);
// get name
field = strtok(NULL,";");
parameters[i]->name = field;
// get value
field = strtok(NULL,";");
parameters[i]->value = field;
// display the result
printf("ID%d:\t%s\t%s\n",parameters[i].id, parameters[i].name, parameters[i].value);
i++;
}
//close file
fclose(f);
return *parameters;
}
int main()
{
struct parameter *parameters;
parameters = readCSV("QD_Config.csv");
printf("ID%d:\t%s\t%s\n",parameters[0]->id, parameters[0]->name, parameters[0]->value);
return(0);
}
I am able to print the contents of the file, but am not able to properly store the structure array before passing it it seems. In main(), when calling the function, I only get the last name and value in the file, but with the ID number of the first.
The problem you (probably) have is that the strtok function returns a pointer to the string you're tokenizing. It does not create a new string for you.
That means e.g.
field = strtok(NULL,";");
parameters[i]->name = field;
will make parameters[i]->name point to some character in buffer. And once the function readCSV returns the variable buffer ends its life-time and ceases to exist, leaving you with invalid pointer.
You need to allocate memory for the strings yourself and copy the data to them. This is either done by making the structure members arrays and use strcpy to copy the string to these arrays, or by using the non-standard but commonly available strdup function (which allocates memory dynamically of the heap and copies the string to it).
There's another problem related to returning your structures:
return *parameters;
is equal to
return parameters[0];
That is, you return a pointer to a single parameter structure.
If you want to return the whole array, you should do
return parameters; // Return the whole array
But note that it will decay to a pointer to its first element (i.e. ¶meters[0]) which have the type struct parameter **, so you need to adjust the return type appropriately.
You also need to initialize parameters to null-pointers, or otherwise it's going to be hard to find the end of the array:
static struct parameter *parameters[BSIZE] = { NULL };
However a better solution which I rather recommend, is that you pass in the array as an argument, and return the number of elements you fill in. Then you can use an array of structure objects (instead of an array of structure pointers) and don't have to do any dynamic allocation and risk memory leaks.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BSIZE 80
struct parameter{
int id;
char *name;
char *value;
struct parameter *next;//If you dont sure how many lins in csv you need this
};
typedef struct parameter parameter;
//I'm lazy too type struct
parameter* CreateNewQ_Q(){
parameter *Q_Q=(parameter*)malloc(sizeof(parameter));
Q_Q->name=NULL;//Nothing at first
Q_Q->value=NULL;//Nothing at first
Q_Q->next=NULL;//Nothing at first
return Q_Q;
}
void readCSV(const char *file,parameter *Q_Q)
{
char buffer[BSIZE];
FILE *f;
char *field;
parameter* A_A=Q_Q;
// open the CSV file
f = fopen(file,"r");
if( f == NULL)
{
printf("Unable to open file '%s'\n",file);
exit(1);
}
// read the data
while(fgets(buffer,BSIZE,f) != NULL)
{
if(A_A->next==NULL){//Next Nothing So Create after it
A_A->next=CreateNewQ_Q();
}
A_A=A_A->next;//A_A is New A_A now
// get id
field = strtok(buffer,";");
A_A->id = atoi(field);
// get name
field = strtok(NULL,";");
//Q_Q
//<--------Here alloc memory for your name because strtok not alloc new memory it just return a pointer in buffer[?]-------------->
A_A->name=(char *)malloc((sizeof(strlen(field)+1)*sizeof(char)));//+1 Becuz '\0' at end of string is necessary
//<--------Here Copy Result-------------->
strcpy(A_A->name, field);
//Q_Q
// get value
field = strtok(NULL,";");
//Q_Q
//<--------Here alloc memory for your value because strtok not alloc new memory it just return a pointer in buffer[?]-------------->
A_A->value=(char *)malloc((sizeof(strlen(field)+1)*sizeof(char)));//+1 Becuz '\0' at end of string is necessary
//<--------Here Copy Result-------------->
strcpy(A_A->value, field);
//Q_Q
// display the result
printf("ID%d:\t%s\t%s\n",A_A->id, A_A->name, A_A->value);
}
//close file
fclose(f);
}
void DeleteAllQ_Q(parameter *Q_Q){
if(Q_Q->next){
DeleteAllQ_Q(Q_Q->next);
Q_Q->next=NULL;
}else{
free(Q_Q->name);//I dont have next so i'm free
free(Q_Q->value);
free(Q_Q);
}
}
int main()
{
//memory control is important!!!!!!!!!!!!!!
parameter *parameters=CreateNewQ_Q();
readCSV("QD_Config.csv",parameters);
printf("Ok Load Done A_A\n");
for(parameter *loopQ_Q=parameters->next;loopQ_Q!=NULL;loopQ_Q=loopQ_Q->next){
printf("ID%d:\t%s\t%s\n",loopQ_Q->id, loopQ_Q->name, loopQ_Q->value);
}
DeleteAllQ_Q(parameters);//free parameters's next and next's next and....
free(parameters);//free self
return(0);
}
I spend sometime, i think this method control memory is more safe!
I am having some trouble starting my program. I'm new to this. I have done some research and found some resources, but I have trouble applying it to the code. It is mostly based on pointers and structures.
I mainly need help with learning how to store the data in the struct, and how to initialize everything.
The program is supposed to find the frequency of characters in a file. I need to use dynamic memory allocation. And use a dynamically allocated array of pointers to store the characters and frequencies. I am supposed to use malloc() to allocate the array and realloc() to increase the size of the array to insert more elements. But I have no idea how to do this.
The program uses these functions-
• charInCFStruct:which returns the index of charfreq struct which has the char c stored in its member variable next. If none of the charfreq structs contains c then it has to return -1.
• printCFStruct: Prints the contents of all of the charfreq structs.
• freeCFStruct: Frees all of the charfreq structs and then frees the pointer
to the structs.
Down below is what I know is right so far. I thought it would be easier to start again from there. I am not asking for code exactly, just some help with the topics I need to do this, and a push in the right direction. Thank you!
#include <stdio.h>
#include <stdlib.h>/*
* struct for storing a char and the number of times it appears in a provided text */
struct charfreq
{
int count;
char next;
};
typedef struct charfreq charfreq;
/*
* Returns the index of charfreq struct which has the char c stored in its member variable next.
* If none of the charfreq structs contains c then it returns -1.
*/
int charInCFStruct(charfreq **cfarray, int size, char c){
}
/*
* Prints the contents of all of the charfreq structs.
*/
void printCFStruct(charfreq **cfarray, int size){
}
/*
* Frees all of the charfreq structs and then frees the pointer to the structs.
*/
void freeCFStruct(charfreq **cfarray, int size){
}
int main(void)
{
charfreq **cfarray;
FILE *inputfile;
char next;
int size = 10; /* used initial value of 10 but any positive number should work */
int i = 0;
int pos;
/* open file to read from */
inputfile = fopen("chars.txt", "r");
if(inputfile == NULL)
printf("chars.txt could not be opened. Check that the file is in the same directory as you are running this code. Ensure that its name is chars.txt.\n\n");
/* allocate space for pointers to char frequency structs */
cfarray = (charfreq**)malloc(size*sizeof(charfreq*));
/* read in chars until the end of file is reached */
while(fscanf(inputfile, "%c", &next) != EOF){
/* fill in code to fill structs with data being read in */
/* call to increase space after changing size */
cfarray = realloc(cfarray,size*sizeof(charfreq*));
}
/* print out char frequency structs */
printCFStruct(cfarray,i);
/* free all char frequency structs */
freeCFStruct(cfarray,i);
/* close the file we opened earlier */
fclose(inputfile);
return 0;
}
I'm currently working on dynamically allocating my array of structures and I'm unsure how to continue. This is my structure:
struct Word_setup
{
char word[M];
int count;
} phrase[N];
I know malloc returns a pointer to a block of memory, but I'm not sure how this works when it comes to an array of structures.
If anyone could please clarify that would be much appreciated!
Probably you meant:
struct Word_setup {
char word[M];
int count;
};
It's a good idea to avoid defining variables in the same line as a struct definition anyway, to help with code readability.
Then you can allocate an array of these:
int main()
{
struct Word_setup *phrase = malloc(N * sizeof *phrase);
// use phrases[x] where 0 <= x < N
phrase = realloc(phrase, (N+5) * sizeof *phrase);
// now can go up to phrases[N+4]
free(phrase);
}
Of course you should check for failure and abort the program if malloc or realloc returns NULL.
If you also want to dynamically allocate each string inside the word then there are a few options; the simplest one to understand is to change char word[M] to char *word; and each time you allocate a phrase, write the_phrase.word = malloc(some_number); . If you allocate an array of words you'll need to loop through doing that for each word.
I suppose that N and M is a compile-time known constants. Then just use sizeof, .e.g.
struct Word_setup*ptr = malloc(sizeof(struct Word_setup)*N);
Maybe you want a flexible array member. Then, it should always be the last member of your struct, e.g.
struct Word_setup {
int count;
unsigned size;
char word[]; // of size+1 dimension
};
Of course it is meaningless to have an array of flexibly sized structures -you need an array of pointers to them.
I am trying to teach myself C from a python background. My current mini-problem is trying to do less hard-coding of things like array lengths and allocate memory dynamically based on input.
I've written the following program. I was hoping for suggestions from the community for modifying it in the following ways:
1.) Make first and last Name elements variable length. Currently their length is hardcoded as MAX_NAME_LENGTH. This will involve both change Names structdeclaration as well as the way I'm assigning values to its elements.
2.) Bonus: Figure out some way to progressively add new elements to the name_list array without having to determine its length beforehand. Basically make it an expandable list.
/* namelist.c
Loads up a list of names from a file to then do something with them.
*/
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#define DATAFILE "name_list.txt"
#define DATAFILE_FORMAT "%[^,]%*c%[^\n]%*c"
#define MAX_NAME_LENGTH 100
typedef struct {
char first[MAX_NAME_LENGTH];
char last[MAX_NAME_LENGTH];
} Name;
int main() {
FILE *fp = fopen(DATAFILE, "r");
// Get the number of names in DATAFILE at runtime.
Name aName;
int lc = 0;
while ((fscanf(fp, DATAFILE_FORMAT, aName.last, aName.first))!=EOF) lc++;
Name *name_list[lc];
// Now actually pull the data out of the file
rewind(fp);
int n = 0;
while ((fscanf(fp, DATAFILE_FORMAT, aName.last, aName.first))!=EOF)
{
Name *newName = malloc(sizeof(Name));
if (newName == NULL) {
puts("Warning: Was not able to allocate memory for ``Name`` ``newName``on the heap.");
}
memcpy(newName, &aName, sizeof(Name));
name_list[n] = newName;
n++;
}
int i = 1;
for (--n; n >= 0; n--, i++) {
printf("%d: %s %s\n", i, name_list[n]->first, name_list[n]->last);
free(name_list[n]);
name_list[n] = NULL;
}
fclose(fp);
return 0;
}
Sample contents of name_list.txt:
Washington,George
Adams,John
Jefferson,Thomas
Madison,James
Update 1:
I've implemented a linked list and some helper functions as #Williham suggested, results are below.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DATAFILE "name_list.txt"
#define MAX_NAME_LENGTH 30
#define DATAFILE_FORMAT "%29[^,]%*c%29[^\n]%*c"
static const int INPUT_BUFFER_SIZE_DEFAULT = sizeof(char) * MAX_NAME_LENGTH;
typedef struct _Name Name;
struct _Name {
char *first;
char *last;
Name *next;
};
int get_charcount(char *str);
Name * create_name_list(char *filename);
void print_name_list(Name *name);
void free_name_list (Name *name);
int main() {
// Read a list of names into memory and
// return the head of the linked list.
Name *head = create_name_list(DATAFILE);
// Now do something with all this data.
print_name_list(head);
// If you love something, let it go.
free_name_list(head);
head = NULL;
return 0;
}
int get_charcount (char *str)
{
int input_length = 1;
while (str[input_length] != '\0')
{
input_length++;
}
return input_length;
}
Name * create_name_list(char *filename)
{
FILE *fp = fopen(DATAFILE, "r");
char *first_input_buffer = malloc(INPUT_BUFFER_SIZE_DEFAULT);
char *last_input_buffer = malloc(INPUT_BUFFER_SIZE_DEFAULT);
Name *firstNamePtr;
Name *previousNamePtr;
while ((fscanf(fp, DATAFILE_FORMAT, last_input_buffer, first_input_buffer))!=EOF)
{
Name *newNamePtr = malloc(sizeof(Name));
if (previousNamePtr)
{
previousNamePtr->next = newNamePtr;
previousNamePtr = newNamePtr;
}
else
{
firstNamePtr = previousNamePtr = newNamePtr;
}
char *temp_buffer = malloc(get_charcount(first_input_buffer));
strcpy(temp_buffer, first_input_buffer);
newNamePtr->first = malloc(get_charcount(first_input_buffer));
strcpy(newNamePtr->first, temp_buffer);
realloc(temp_buffer, get_charcount(last_input_buffer));
strcpy(temp_buffer, last_input_buffer);
newNamePtr->last = malloc(get_charcount(last_input_buffer));
strcpy(newNamePtr->last, temp_buffer);
free(temp_buffer);
temp_buffer = NULL;
}
previousNamePtr->next = NULL;
previousNamePtr = NULL;
free(first_input_buffer);
free(last_input_buffer);
first_input_buffer = NULL;
last_input_buffer = NULL;
fclose(fp);
return firstNamePtr;
}
void print_name_list (Name *name)
{
static int first_iteration = 1;
if (first_iteration)
{
printf("\nList of Names\n");
printf("=============\n");
first_iteration--;
}
printf("%s %s\n",name->first, name->last);
if (name->next)
print_name_list(name->next);
else
printf("\n");
}
void free_name_list (Name *name)
{
if (name->next)
free_name_list(name->next);
free(name->first);
free(name->last);
name->first = NULL;
name->last = NULL;
name->next = NULL;
free(name);
name = NULL;
}
A very simple approach is to not use an array at all, but rather a linked list:
This can be done in a number of ways, but the simplest is probably to modify the Name struct as follows:
typedef struct _Name Name;
struct _Name {
char *first;
char *last;
Name *next;
};
The use of char * instead of char[] will necessitate some strcpying, but that's really neither here nor there. To expand the array you can now simply malloc these elements one at a time; and set next appropriately.
Note: Remember to set next to NULL when you create new tail elements.
There isn't a way to expand an array in C. All that you can do is allocate a larger block of memory and copy the items over. This is what Python does under the covers.
There's also no way to determine the size of an array, since it stores only the items an no additional information (Python arrays store the length alongside the elements.) You could put a marker at the end of the array and count elements until you hit the marker, this is how strings work (the null char '\0' is the marker.)
You'll have to have a maximum length array to take the input - you don't know how long the input is going to be. However, you only need one array of that length. Once you've got the input you can get it's length and allocate an array of just the right size to store the names.
As for growing your list you can use realloc or use a linked list of course.
Have you started reading about linked lists? It may help you to have a dynamic structure.
Also you can check what is the difference between array and linked list.
A char pointer can be member of your linked list which can be allocated memory dynamically.
Check this link from stackoverflow for more information on the same.
Since noone mentioned it yet, if you use *scanf to read untrusted input strings, you should always use maximum field width specifiers. Like
scanf("%19s", str);
Note that this specify maximum string length not including the terminating NUL, so in this example, str should be 20 chars long.
If you don't limit scanf input conversions like this, on buffer overflow you will not only get undefined behaviour, but also a security vulnerability.
Back to your question about dinamically growing buffers. Imagine you are reading some input composed of lines, and each line can be at a maximum 78 chars wide. In situations like this, you know the maximum length of each line, but have no idea of the maximum length of your input as a whole. A common way to do this is to allocate some default space, and if you need to grow that space, grow it by *2, so you don't need to realloc() many times.
I have a structure with the following definition:
typedef struct myStruct{
int a;
char* c;
int f;
} OBJECT;
I am able to populate this object and write it to a file. However I am not able to read the char* c value in it...while trying to read it, it gives me a segmentation fault error. Is there anything wrong with my code:
//writensave.c
#include "mystruct.h"
#include <stdio.h>
#include <string.h>
#define p(x) printf(x)
int main()
{
p("Creating file to write...\n");
FILE* file = fopen("struct.dat", "w");
if(file == NULL)
{
printf("Error opening file\n");
return -1;
}
p("creating structure\n");
OBJECT* myObj = (OBJECT*)malloc(sizeof(OBJECT));
myObj->a = 20;
myObj->f = 45;
myObj->c = (char*)calloc(30, sizeof(char));
strcpy(myObj->c,
"This is a test");
p("Writing object to file...\n");
fwrite(myObj, sizeof(OBJECT), 1, file);
p("Close file\n");
fclose(file);
p("End of program\n");
return 0;
}
Here is how I am trying to read it:
//readnprint.c
#include "mystruct.h"
#include <stdio.h>
#define p(x) printf(x)
int main()
{
FILE* file = fopen("struct.dat", "r");
char* buffer;
buffer = (char*) malloc(sizeof(OBJECT));
if(file == NULL)
{
p("Error opening file");
return -1;
}
fread((void *)buffer, sizeof(OBJECT), 1, file);
OBJECT* obj = (OBJECT*)buffer;
printf("obj->a = %d\nobj->f = %d \nobj->c = %s",
obj->a,
obj->f,
obj->c);
fclose(file);
return 0;
}
When you write your object, you're writing the pointer value to the file instead of the pointed-to information.
What you need to do is not just fwrite/fread your whole structure, but rather do it a field at a time. fwrite the a and the f as you're doing with the object, but then you need to do something special with the string. Try fwrite/fread of the length (not represented in your data structure, that's fine) and then fwrite/fread the character buffer. On read you'll need to allocate that, of course.
Your first code sample seems to assume that the strings are going to be no larger than 30 characters. If this is the case, then the easiest fix is probably to re-define your structure like this:
typedef struct myStruct{
int a;
char c[30];
int f;
} OBJECT;
Otherwise, you're just storing a pointer to dynamically-allocated memory that will be destroyed when your program exits (so when you retrieve this pointer later, the address is worthless and most likely illegal to access).
You're saving a pointer to a char, not the string itself. When you try to reload the file you're running in a new process with a different address space and that pointer is no longer valid. You need to save the string by value instead.
I would like to add a note about a potential portability issue, which may or may not exist depending upon the planned use of the data file.
If the data file is to be shared between computers of different endian-ness, you will need to configure file-to-host and host-to-file converters for non-char types (int, short, long, long long, ...). Furthermore, it could be prudent to use the types from stdint.h (int16_t, int32_t, ...) instead to guarantee the size you want.
However, if the data file will not be moving around anywhere, then ignore these two points.
The char * field of your structure is known as a variable length field. When you write this field, you will need a method for determining the length of the text. Two popular methods are:
1. Writing Size First
2. Writing terminal character
Writing Size First
In this method, the size of the text data is written first, followed immediately by the data.
Advantages: Text can load quicker by block reads.
Disadvantages: Two reads required, extra space required for the length data.
Example code fragment:
struct My_Struct
{
char * text_field;
};
void Write_Text_Field(struct My_Struct * p_struct, FILE * output)
{
size_t text_length = strlen(p_struct->text_field);
fprintf(output, "%d\n", text_length);
fprintf(output, "%s", p_struct->text_field);
return;
}
void Read_Text_Field(struct My_STruct * p_struct, FILE * input)
{
size_t text_length = 0;
char * p_text = NULL;
fscanf(input, "%d", &text_length);
p_text = (char *) malloc(text_length + sizeof('\0'));
if (p_text)
{
fread(p_text, 1, text_length, input);
p_text[text_length] = '\0';
}
}
Writing terminal character
In this method the text data is written followed by a "terminal" character. Very similar to a C language string.
Advantages: Requires less space than Size First.
Disadvantages: Text must be read one byte at a time so terminal character is not missed.
Fixed size field
Instead of using a char* as a member, use a char [N], where N is the maximum size of the field.
Advantages: Fixed sized records can be read as blocks.
Makes random access in files easier.
Disadvantages: Waste of space if all the field space is not used.
Problems when the field size is too small.
When writing data structures to a file, you should consider using a database. There are small ones such as SQLite and bigger ones such as MySQL. Don't waste time writing and debugging permanent storage routines for your data when they have already been written and tested.