Getting information from an array of pointers to structs in C - arrays

So i'm writing this code for an assignment for school, and what I have to do is have two functions as they're written, with createMonster returning a dynamically allocated monster with data, and readMonsters returning an array of pointers to the monsters made in createMonster. I've been having trouble understanding how to get the functions and structs to cooperate, and this is what I have so far:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct monster {
int id;
char *name;
char *element;
int population;
} monster;
monster* createMonster(char *name, char *element, int population)
{
monster *amonster = (monster*)malloc(sizeof(monster));
amonster->name = (char*)malloc(sizeof(char)*sizeof(name));
amonster->element = (char*)malloc(sizeof(char)*sizeof(element));
amonster->population = (int)malloc(sizeof(int)*sizeof(population));
amonster->name = name;
amonster->element = element;
amonster->population = population;
return amonster;
}
monster** readMonsters(FILE* infile, int *monsterCount)
{
char n[15], e[15];
int p;
monster **a_array = malloc(*monsterCount * sizeof(struct a*));
for (int i = 0; i < *monsterCount; i++) {
a_array[i] = malloc(sizeof(monster));
fscanf(infile,"%s %s %d",n, e, &p);
printf("%s %s %d\n", n, e, p);
a_array[i] = createMonster(n,e,p);
}
monster ***m = &a_array;
return *m;
}
int main(){
int monsterCount;
char name[15];
FILE *fp = fopen ( "in.txt", "r" );
fscanf(fp,"%d %s",&monsterCount,name);
//printf("test %d\n",monsterCount);
monster **mptr = readMonsters(fp,&monsterCount);
printf("%s %s %d\n", (mptr)[3]->name,(mptr)[3]->element,(mptr)[3]->population);
fclose(fp);
return 0;
}
With the input file being:
8 monsters
StAugustine Grass 12
Zoysia Grass 8
WholeWheat Bread 6
MultiGrain Bread 10
Rye Bread 10
Cinnamon Spice 5
Pepper Spice 10
Pumpkin Spice 30
However, when I run it, I can see it works as far as making them the first time, but when I try and go back to access the data it returns garbage for the strings, with this being my output:
StAugustine Grass 12
Zoysia Grass 8
WholeWheat Bread 6
MultiGrain Bread 10
Rye Bread 10
Cinnamon Spice 5
Pepper Spice 10
Pumpkin Spice 30
`7 w├?]╨# 10
w├?]╨# 10
I've tried rearranging the functions, pointers, etc. and have tried many different versions of these functions trying to follow guides online, but each time it either doesn't work at all or returns garbage. I'm looking for any help in understanding how to get this working or how it could be organized better since I will readily admit my experience with C is moderate at best.

The code you posted has the following issues:
The line
amonster->name = (char*)malloc(sizeof(char)*sizeof(name));
does not make sense. sizeof(name) is the size of the pointer, which is 32-bit or 64-bit, depending on your platform. However, you probably need to allocate more than that. You need to allocate strlen(name) + 1 bytes, because that is the length of the string that is passed to the function (including the null terminating character). The same also applies to the next line:
amonster->element = (char*)malloc(sizeof(char)*sizeof(element));
Also, the line
amonster->population = (int)malloc(sizeof(int)*sizeof(population));
does not make sense, because amonster->population is not a pointer. Why are you trying to store a pointer to dynamically allocated memory in it? You shouldn't need dynamic memory allocation here, because amonster->population is not a string, but a fixed-length variables, for which you have already allocated space, because it is part of the struct. Therefore, you can delete this line. All you need is the line amonster->population = population;, which you already have.
Additionally, the line
amonster->name = name;
does not do what you want. It does not copy the string, it only copies the pointer, i.e. the memory address. That way, you are copying a pointer which will be a dangling pointer by the time you return to the function main. In order to copy the actual string, you must write strcpy( amonster->name, name );. The same applies for the following line:
amonster->element = element;
The line
a_array[i] = malloc(sizeof(monster));
is unnecessary and only creates a memory leak. You have already allocated space for all structs in the function createMonster.
The lines
monster ***m = &a_array;
return *m;
are unnecessarily cumbersome and can be simplified to the following:
return a_array;
After applying all of these fixes, your code should look like this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct monster {
int id;
char *name;
char *element;
int population;
} monster;
monster* createMonster(char *name, char *element, int population)
{
monster *amonster = (monster*)malloc(sizeof(monster));
amonster->name = (char*)malloc(sizeof(char)*(strlen(name)+1)); //fixed
amonster->element = (char*)malloc(sizeof(char)*(strlen(element)+1)); //fixed
//amonster->population = (int)malloc(sizeof(int)*sizeof(population)); //redundant
strcpy( amonster->name, name ); //fixed
strcpy( amonster->element, element ); //fixed
amonster->population = population;
return amonster;
}
monster** readMonsters(FILE* infile, int *monsterCount)
{
char n[15], e[15];
int p;
monster **a_array = malloc(*monsterCount * sizeof(struct a*)); //what is struct a???
for (int i = 0; i < *monsterCount; i++) {
//a_array[i] = malloc(sizeof(monster)); //redundant
fscanf(infile,"%s %s %d",n, e, &p);
printf("%s %s %d\n", n, e, p);
a_array[i] = createMonster(n,e,p);
}
//monster ***m = &a_array; //removed
//return *m; //removed
return a_array;
}
int main(){
int monsterCount;
char name[15];
FILE *fp = fopen ( "in.txt", "r" );
fscanf(fp,"%d %s",&monsterCount,name);
//printf("test %d\n",monsterCount);
monster **mptr = readMonsters(fp,&monsterCount);
printf("%s %s %d\n", (mptr)[3]->name,(mptr)[3]->element,(mptr)[3]->population);
fclose(fp);
return 0;
}
As demonstrated here, your program now provides the correct output.

Related

Why am I getting segmentation fault in my while loop?

My program will read the datas from a txt into my structure.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct competitor
{
int id;
char* name;
char* nationality;
char* sex;
double weight;
char* event;
}competitor;
int sportolok_beolvas(char* filename, competitor* array)
{
FILE* file = fopen(filename, "r");
int n = 0;
while(fscanf(file, "%d %s %s %s %lf %s", &array[n].id, array[n].name, array[n].nationality, array[n].sex, &array[n].weight, array[n].event) == 6)
{
++n;
}
fclose(file);
return n;
}
int main()
{
competitor* array;
int i = sportolok_beolvas("sportolok.txt", array);
printf("%d", i);
return 0;
}
I did this function based on my previous project, which uses almost the same function. Read in the file to my struct. I don't understand what's wrong with it.
A string is really an array of characters with a null-terminator at the end.
You can create a string using plain arrays, like
char str[] = "foo";
Or you can use pointers and allocate memory dynamically, like
char *str = malloc(4);
strcpy(str, "foo");
Both those examples are almost equivalent (there are some important differences between arrays and pointers, but lets put those aside for now). Both those examples creates a string with three characters (plus the null-terminator).
However, if you have
char *str;
strcpy(str, "foo");
then you have undefined behavior because the pointer str doesn't point anywhere.
Going back to your code, the simplest solution for the strings are simply to change them from pointers to arrays:
#define NAME_SIZE 20
#define NATIONALITY_SIZE 20
#define SEX_SIZE 10
#define EVENT_SIZE 20
typedef struct competitor
{
int id;
char name[NAME_SIZE];
char nationality[NATIONALITY_SIZE];
char sex[SEX_SIZE];
double weight;
char event[EVENT_SIZE];
}competitor;
As for the array of competitor structures, I recommend you create it dynamically as needed inside the loop, reallocating as needed.
Read into a single defined competitor structure object, and copy it to the current array element.
Once done return the pointer to the first element of the array.
Perhaps something like this:
// Out dynamic "array", pointer to its first element
competitor *competitors = NULL;
// Current size of the competitor array
size_t current_size = 0;
// A structure object to store the data we read from the input
competitor current_competitor;
// Now the loop to read the data
while(fscanf(file, "%d %s %s %s %lf %s",
&current_competitor.id,
current_competitor.name,
current_competitor.nationality,
current_competitor.sex,
&current_competitor.weight,
current_competitor.event) == 6)
{
// Reallocate the array, adding one new element (+1)
// If the array doesn't exist, when competitors is NULL, create
// an array with a single element
competitor *new_array = realloc(competitors, sizeof *new_array * current_size + 1);
// Check if the allocation succeeded or failed
if (new_array == NULL)
{
// TODO: Report it
exit(EXIT_FAILURE);
}
// Make the new array our actual array
competitors = new_array;
// Copy the the structure object we just have read, into the array
competitors[current_size] = current_competitor;
// And finally increase the size
++current_size;
}
After this, the `competitors` pointer will point to the first element of an array of `current_size` elements.
Exactly how to return these values (*both* needs to be returned) is left as an exercise for the reader.

Incorrect values assigned to a member of a structure

I have a structure
struct services {
char *actived[50];
char *disactived[50];
};
and a function :
void servicesInfo(struct services *services_i) {
FILE *fp;
int status;
char *tmp;
const char *actived_cmd ="/usr/sbin/service --status-all | awk '/[+]/{ print $4 }'" ;
fp = popen(actived_cmd, "r");
int i=0;
while (fgets(tmp, 1024, fp)){
printf("service %s\n", tmp);
(services_i->actived)[i]=tmp;
i++;
}
status = pclose(fp);
}
when i call the function
struct services services_i;
servicesInfo(&services_i);
all is fine and all services printed, but if this code
for (i = 0; i < 20; ++i)
{
printf("service i=%d %s\n",i,services_i.actived[i] );
}
print just the last value (uvrandom)
You need to read up on C pointer and memory allocation. There are two misunderstandings here:
tmp is not, as it is written, a string buffer. It is just a string pointer. It can only be assigned to point to strings that are allocated somewhere else, and not contain the string itself.
You are just copying the pointer to actived (which by the way probably should be spelled activated). This means that all actived pointers all point to the same as tmp does, which is always the same, since tmp is never changed (and also has uninitialized value).
I suggest you use tmp = malloc(1024). Don't forget to use free(services_i.actived[i]) when you don't need them anymore.
I also suggest making an array of structs instead of a struct of arrays, to make it more logical.
Here is some example code of how to assign a value to structure:
#include <stdio.h>
struct date { /* global definition of type date */
int month;
int day;
int year;
};
main()
{
struct date today;
today.month = 10;
today.day = 14;
today.year = 1995;
printf("Todays date is %d/%d/%d.\n", \
today.month, today.day, today.year );
}

How to walk through array of Struct in c

I have a program which creates an array or struct and go through it for processing. Initially it initialize the array with the defined nyumber of elements. Then for some number of element in array, the name is assigned.
I pretend the code that is equal to my scenario which is tested in codebloc and get the similar error. The problem is described in comments.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _car {
char *name;
int year;
} Car;
char *getCarName(char *name, int var);
void processCar();
void printCars(Car car[]);
int INCREMENT = 10;
int main(void)
{
processCar();
return 0;
}
void processCar()
{
// create car array with amount of INCREMENT
Car CAR_ARRAY[INCREMENT];
int a=0;
// This function assign name for Car amount 10 less than INCREMENT
while (a<INCREMENT - 2) {
char *carName;
carName = getCarName(&carName, a);
CAR_ARRAY[a].name = malloc(strlen(carName) + 1);
strcpy(CAR_ARRAY[a].name, carName);
a++;
}
printCars(CAR_ARRAY);
}
void printCars(Car car[])
{
printf("IN Car \n");
int a = 0;
// when try to call name for car amount equals to INCREMENT program terminates.
while(a<INCREMENT) {
if (car[a].name != NULL) // checking if NULL
printf("Car Name : %d -> %s\n", a, car[a].name);
a++;
}
}
char *getCarName(char *name, int var)
{
name = "Toyota";
return name;
}
What is the right way to check the struct value on struct array whether it can be called?
EDIT
I created a hack to do this as follows.
// added these right after creating array
for (a = 0; a < INCREMENT; a++)
CAR_ARRAY[a].name = NULL;
I dont know if it is a standard way or not. pls advice.
You are checking for NULL before printing, which is a good idea, but it doesn't help you here, because your last two cars are uninitialised and likely contain garbage: The name pointer is not NULL, but doesn't point to a valid address either. Segmentation violation ensues.
You should initialise all cars, not only INCREMENT - 2. Alternatively, you could initialise your cars to zero by calling memset before your initialisation:
memset(CAR_ARRAY, 0, sizeof(Car) * INCREMENT);
As an aside, the way you deal with getCarName is rather shaky as well. At the moment, your name is a pointer to a string literal. Your local variable carName does a half-hearted double duty: You try to pass it by reference (but essentially you don't) and you also return it.
Basically, you could do this in one of two ways. The easier one here is to return a pointer. in that case, you don't have to pass any string:
char *getCarName(int var)
{
static char *names[3] = {"Toyota", "Dodge", "Peugeot"};
return names[var % 3];
}
and call it like so:
char *carName = getCarName(&carName, a);
Alternatively, you could pass a char pointer by reference, i.e. as pointer to pointer to char. In that case, you don't have to return anything:
void getCarName(char **name, int var)
{
static char* names[3] = {"Toyota", "Dodge", "Peugeot"};
*name = names[var % 3];
}
Call it like so:
char *carName;
getCarName(&carName, a);
There are other scenarios here, for example if you just pass a char pointer and have getCarName fill it, but I'll leave that for now - it would make everything even more complicated.

C: reading text into linked list

Im trying to read into a linked list from a textfile. The text file has the title of the book, author and year separated by ":". each book is on a separate line. the textfile entries look like this:
Absalom, Absalom!:William Faulkner:1936
After Many a Summer Dies the Swan:Aldous Huxley:1939
Ah, Wilderness!:Eugene O'Neill:1933
i'm rewriting it from scratch. comments would be appreciated.
#include <stdlib.h>
#include <stdio.h>
struct BookNode
{
char linebuffer[128];
char delim[]=":";
char * Title[50];
char * Author[50];
char * Year[5];
struct BookNode *next;
// char *token = NULL;
};
int main(void)
{
static const char booklist[]= "booklist.txt";
FILE *fr=fopen("booklist.txt", "r");
if ( fr != NULL)
{
char Title[50];
char Author[50];
char Year[5]
struct BookNode Booknode;
while (fgets(linebuffer,128, fr) != NULL &&
sscanf(line, "%49s %49s %4s",
&BookNode.Title, BookNode.Author, BookNode.Year)==3)
{
printf("%50s %50s %5s",
BookNode.Title, BookNode.Author, BookNode.Year);
}
}
There are multiple problems in your code right now.
The first one (I kid you not) is with code formatting and indentation. Your pasted sample didn't have a regular format or indentation to speak of. It's more difficult to follow code flow even in short samples such as this. Always indent your code, and pick a coding style (there are several) and stick to it.
Regarding code flow, the first problem is in error checking. Namely, you check for fopen return status, but do not take sufficient action should opening a file fail.
The second problem is a conceptual one. You don't seem to realise that an array of N characters can only hold a string with a lenght of N-1. Therefore, char[4] is hardly ever a suitable format for storing years as strings.
Now that those issues have been dealed with, here are the actual flaws that would prevent your code from working in any case:
1) The fgets function will read up until it either fills your buffer or reaches an end-of-line or an end-of-file character. Yet you still call fgets thrice to try and read a single-line entry in your file. It's unlikely what you want to do. You have to re-think the contents of your loop.
2) Your "main" loop condition is likely to be flawed. This is a very common misunderstanding of the use of feof & co. Assuming your data file contains a newline at the end (and it would only be regular for it to do so), your loop will execute one time too many.
It's better to structure your line reading loops like this:
while (fgets(buffer, BUF_SIZE, stdin)) { /* parse buffer */ }
3) You have elementary problems with memory management in your code: namely, the function addEntry fails to allocate memory to store your records. Instead, all entries in your linked list will end up pointing to the same shared buffer you allocate in your main function.
There are several ways to fix this. One is to use several calls to malloc for each member of your BookNode structs (title, author, and year). Another, perhaps preferable method is to use variable-size structs, like this:
struct BookNode {
char *title;
char *author;
char *year;
struct BookNode *next;
char buffer[]; // this shorthand requires C99
};
For each struct BookNode you allocate enough storage after them, so that you can copy there the contents of your shared buffer. title, author, and year then point to this appended storage. This way you won't end up overwriting the contents of other BookNodes in the next iteration of your loop. And you only need one free to free an entire node.
I probably didn't list all the problems in your code here. Perhaps instead of another rewrite, you should first try to tackle a smaller subproblem such as reading a single entry from stdin and building up from there?
addEntry should allocate memory for title, author and year.
Also, doing fgets three times would read 3 lines. You need one fgets per loop, and split the result to different parts (e.g. using strtok_r).
What you do is save a pointer to a static buffer. When reading the next line, this buffer is overwritten with new data.
Note that if you allocated data, you must eventually free it. The entry's destructor will need to free.
example of strtok
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char line[] = "Absalom, Absalom!:William Faulkner:1936\n";
char *p;
char * Title;
char * Author;
char * Year;
p = strtok(line, ":");
Title = strdup(p);
Author = strdup(strtok(NULL, ":"));
Year = strdup(strtok(NULL, ": \n"));
printf("\"%s\",\"%s\",\"%s\"\n", Title, Author, Year);
free(Title);
free(Author);
free(Year);
}
//result:"Absalom, Absalom!","William Faulkner","1936"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct BookNode {
char * Title;
char * Author;
char * Year;
struct BookNode * next;
} * head;
void addEntry(char * T, char * A, char * Y);
void display();
int numEntries();
//void writeBookData(struct BookNode * selection);
void free_book(struct BookNode *bnp){
if(bnp == NULL) return;
free(bnp->Title);
free(bnp->Author);
free(bnp->Year);
free_book(bnp->next);
free(bnp);
}
int main() {
FILE * fpointer;
fpointer=fopen("booklist.txt","r");
if(fpointer == NULL){
printf("Booklist could not be opened.\n");
exit(EXIT_FAILURE);
}
char Title[50+1];
char Author[50+1];
char Year[4+1];
head = NULL;
while (EOF!=fscanf(fpointer, "%50[^:]%*c%50[^:]%*c%4[^\n]%*c", Title, Author, Year)){
//note:The input number of characters is limited (Eg50), it (because minutes in excess of the limit is used in the following items) there must be large enough.
addEntry(Title, Author, Year);
}
fclose(fpointer);
int entryCount = numEntries();
printf("There are %d entries in this Book list\n", entryCount);
display();
free_book(head);
return 0;
}
void addEntry(char * T, char * A, char * Y){
struct BookNode * tempNode, * iterator;
tempNode = (struct BookNode *)malloc(sizeof(struct BookNode));
tempNode->Title = (char *)malloc(strlen(T)+1);
strcpy(tempNode->Title, T);
tempNode->Author = (char *)malloc(strlen(A)+1);
strcpy(tempNode->Author, A);
tempNode->Year = (char *)malloc(strlen(Y)+1);
strcpy(tempNode->Year, Y);
tempNode->next = NULL;
iterator = head;
if (head == NULL){
head = tempNode;
} else {
while(iterator->next != NULL){
iterator = iterator->next;
}
iterator->next = tempNode;
}
}
int numEntries(){
if(head == NULL)
return 0;
else{
int count;
struct BookNode *iterator;
for(count=0, iterator=head; iterator!=NULL; iterator = iterator->next, ++count)
;
return count;
}
}
void display(){
if(head == NULL)
return ;
else{
struct BookNode *iterator;
for(iterator=head; iterator!=NULL; iterator = iterator->next)
fprintf(stdout, "%s:%s:%s\n", iterator->Title, iterator->Author, iterator->Year);
}
}

C - split/store string of X length into an array of structs

I'm trying to split a string every X amount of characters, and then store each line in an array of structs. However, I'm wondering what would be a short and efficient way of doing it. I thought that maybe I could use sscanf, but not very sure how to. Any help will be appreciated. So far I have:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct st {char *str;};
int main ()
{
struct st **mystruct;
char tmp[] = "For configuration options (arch/xxx/config.in, and all the Config.in files),somewhat different indentation is used.";
size_t max = 20, j = 0; // max length of string
size_t alloc = strlen(tmp)/max + 1;
mystruct = malloc(alloc * sizeof *mystruct);
for (j = 0; j < alloc; j++)
mystruct[j] = malloc(sizeof *mystruct[j]);
const char *ptr = tmp;
char field [ max ];
int n;
while (*ptr != '\0') {
int line = sscanf(ptr, "%s", field, &n); // not sure how to use max in here
mystruct[j]->str = field;
field[0]='\0';
if (line == 1)
ptr += n;
if ( n != max )
break;
++ptr;
++j;
}
return 0;
}
So when I iterate over my struct, I can get something like:
For configuration op
tions (arch/xxx/conf
ig.in, and all the C
onfig.in files),some
what different inden
tation is used.
You could use strncpy.
FYI:
char field [ max ];
while (...) {
mystruct[j]->str = field;
Two problems with this: (1) every struct in your array is going to end up pointing at the same string, which will have the value of the last thing you scanned, (2) they are pointing to a variable on the stack, so when this function returns they will be trashed. That doesn't manifest itself visibly here (e.g. your program doesn't explode) because the function happens to be 'main', but if you moved this to a separate routine and called it to parse a string, you'd get back garbage.
mystruct doesn't need to be pointer to pointer. For a 1D array, just allocate a block N * sizeof *myarray for N elements.
A common C idiom when dealing with structs is to use typedef so you don't have to type struct foo all the time. For instance:
typedef struct {
int x, y;
} point;
Now instead of typing struct point pt you can just say point pt.
If your string is not going to change after you split it up, I'd recommend using a struct like this:
struct st {
char *begin;
char *end;
};
or the alternative:
struct st {
char *s;
size_t len;
};
Then instead of creating all those new strings, just mark where each one begins and ends in your struct. Keep the original string in memory.
One option is to do it character-by-character.
Calculate the number of lines as you are currently doing.
Allocate memory = (strlen(tmp) + number_of_lines) * sizeof(char)
Walk through your input string, copying characters from the input to the newly allocated memory. Every 20th character, insert a null byte to delimit that string. Save a pointer to the beginning of each line in your array of structs.
Its easy enough?
#define SMAX 20
typedef struct {char str[SMAX+1];} ST;
int main()
{
ST st[SMAX]={0};
char *tmp = "For configuration options (arch/xxx/config.in, and all the Config.in files),somewhat different indentation is used.";
int i=0,j;
for( ; (st[i++]=*(ST*)tmp).str[SMAX]=0 , strlen(tmp)>=SMAX; tmp+=SMAX );
for( j=0;j<i;++j )
puts(st[j].str);
return 0;
}
You may use (non C standard but GNU) function strndup().
#define _GNU_SOURCE
#include <string.h>
struct st {char *str;};
int main ()
{
struct st *mystruct; /* i wonder if there's need for double indirection... */
char tmp[] = "For configuration options (arch/xxx/config.in, and all the Config.in files),somewhat different indentation is used.";
size_t max = 20, j = 0; // max length of string
size_t alloc = (strlen(tmp) + max - 1)/max; /* correct round up */
mystruct = malloc(alloc * sizeof mystruct);
if(!mystruct) return 1; /* never forget testing if allocation failed! */
for(j = 0; j<alloc; j++)
{
mystruct[j].str = strndup(tmp+alloc*max, max);
}
}

Resources