Add data dynamically to array of pointers to structures within function - c

I tried many combinations but really nothing worked. It's been long enough so I decided to write this issue.
I just want an array of pointers to structures so I could later easiely sort it by swaping the addresses. I have a function to get data from file and write to array. Unfortunately, it's impossible for me to read this data outside the function.
My last attempt (I deleted file operations as those are not the issue):
Header.h:
#pragma once
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int32_t year;
uint16_t month;
uint16_t day;
} Date;
typedef struct {
char name[16];
Date date;
uint32_t number;
} Player;
size_t readData(Player** players);
Source.c:
#include "Header.h"
size_t readData(Player** players) {
players = NULL;
players = realloc(players, sizeof(Player*) * 1);
players[0] = malloc(sizeof(Player));
strcpy(players[0]->name, "asd");
printf("$ %s\n", players[0]->name);//OK
return 1;
}
int main() {
Player **players = NULL;
uint32_t elemCount, i;
elemCount = readData(players);
printf("> %s", players[0]->name);//BUM - ERROR!
return 0;
}
I'm using Visual Studio Community 2015. I know it's not so much for coding in C but I managed to configure the solution and it doesn't seem to be the thing imo.
I will be very thankful for help. Yet, I'd like to see it as a remake of my code. I tried some other answers to questions on StackOverFlow like this but they didn't help.

If a parameter of a function is not only read and should be an output too, you have to pass a pointer to your data to the function. If your data type is Player** your paramter type has to be Player ***, so the list of players itselfe is able to be changed inside the function.
size_t readData(char* fname, Player*** players) {
// ^ players is a input and output parameter
Player **tempPlayers = *players; // local list of players
tempPlayers = realloc(tempPlayers, sizeof(Player*) * 1);
tempPlayers[0] = malloc(sizeof(Player));
strcpy(tempPlayers[0]->name, "asd");
printf("$ %s\n", tempPlayers[0]->name);//OK
*players = tempPlayers; // write back list of players to paramter
return 1;
}
int main() {
Player **players = NULL;
uint32_t elemCount, i;
char *fileName = NULL;
elemCount = readData(&players);
// ^
printf("> %s", players[0]->name);//BUM - ERROR!
return 0;
}
If you don't want to use *** you can do it like this:
Player* *readData(char* fname, Player** players, size_t *size) {
players = realloc(players, sizeof(Player*) * 1);
players[0] = malloc(sizeof(Player));
strcpy(players[0]->name, "asd");
printf("$ %s\n", players[0]->name);//OK
*size = 1;
return players;
}
players = readData( fileName, players, &elemCount );

You're passing the pointer to the function by value; the original players in main is not changed. This is no different from doing:
#include <stdio.h>
void func(int x)
{
x = 4;
}
int main()
{
int x = 0;
func(x);
printf("%d\n", x); // zero
}
I'm assuming it's the pointers that got you confused, so you should see your mistake now.
To make the modification visible outside of readData, you will need to use three levels of indirection:
size_t readData(Player*** players) {
*players = malloc(sizeof(Player*) * 1);
*(players)[0] = malloc(sizeof(Player));
strcpy((*players)[0]->name, "asd");
printf("$ %s\n", (*players)[0]->name);//OK
return 1;
}
int main() {
Player **players = NULL;
uint32_t elemCount, i;
elemCount = readData(&players);
printf("> %s", players[0]->name); // prints 'asd'
return 0;
}
There is no point using realloc when you know the pointer being passed is always NULL, so I've changed it to malloc instead.
And on a side note: Visual Studio is perfectly suited to C development, and implements parts of C99 and even C11 (historically MSVC was always stuck at C89). You can just use the .c extension for your source files, and this will make the compiler assume the code is C, or you can explicitly set this in the property pages.

parameter passed to readData() must be the address of the caller's pointer 'players'
each of the references in readData() to players must take that into account.
Othewise the caller's pointer 'player' will not be updated.
Then the main() function, call to printf() is trying to output from address 0, resulting in undefiined behaviour, leading to a seg fault event.
in the main() function, if you insert:
printf( "player value; %p\n", (void*)players);
before the current call to printf() you will see the players pointer still contains NULL.

Related

What am I doing wrong in passing a struct around in C?

So I am working on a project in C that requires that I pass pointers to a struct into functions. The project is structured as follows:
struct structName {
unsigned short thing2;
char thing1[];
};
void function_1(struct structName *s) {
strcpy(s->thing1, "Hello");
printf("Function 1\n%s\n\n", s->thing1); // prints correctly
}
void function_2(struct structName *s) {
// can read thing2's value correctly
// thing1 comes out as a series of arbitrary characters
// I'm guessing it's an address being cast to a string or something?
printf("Function 2\n%s\n\n", s->thing1); // prints arbitrary characters ('É·/¨')
}
int main() {
struct structName s;
function_1(&s);
printf("Main\n%s\n\n", s.thing1);
function_2(&s);
printf("Main 2\n%s\n\n", s.thing1);
}
This code outputs the following:
Function 1
Hello
Main
Hello
Function 2
É·/¨
Main 2
É·/¨
Obviously, the program has more than just what I've written here; this is just a simplified version; so if there's anything I should check that might be causing this let me know. In all honesty I reckon it's probably just a stupid rookie error I'm making somewhere.
[EDIT: Seems like s.thing1 is being mutated in some way in the call to function_2(), since the odd value is replicated in main() - I should point out that in my program the printf()s are located right before the function call and in the first line of the function, so there's no chance that it's being written to by anything I'm doing. I've updated the example code above to show this.]
Thanks in advance!
The structure contains a flexible member at its end, if you declare a static object with this type, the length of this member will be zero, so strcpy(s->thing1, "Hello"); will have undefined behavior.
You are supposed to allocate instances of this type of structure with enough extra space to handle whatever data you wish to store into the flexible array.
Here is an example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct pstring {
size_t length;
char data[];
} pstring;
pstring *allocate_pstring(const char *s) {
size_t length = strlen(s);
pstring *p = malloc(sizeof(*p) + length + 1);
if (p != NULL) {
p->length = length;
strcpy(p->data, s);
}
return p;
}
void free_pstring(pstring *p) {
free(p);
}
int main() {
pstring *p = allocate_pstring("Hello");
printf("Main\n%.*s\n\n", (int)p->length, p->data);
free_pstring(p);
return 0;
}

Passing struct into function

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 100000
typedef struct {
int day;
int month;
int year;
} DATE;
typedef struct {
char name[100];
int age;
float hrlyWage;
float hrsWorked;
float regPay;
float otPay;
float totalPay;
DATE payDate;
} PAYRECORD;
int newRecord(struct PAYRECORD record[], int index){
//set name to \0 so it can work as string
record[index].name = {'\0'};
index++;
return index;
}
int main(){
char menuChoice = 'X';
struct PAYRECORD record[SIZE];
int index = 0;
while (menuChoice != 'Q'){
system("pause");
system("cls");
menuChoice = runMenu();
switch (menuChoice){
case 'A':
index = newRecord(record, index);
}
}
}
main sets up an array of structs the gets passed into newRecord, and the goal is to make it so that I can input the data here and then return the new index to keep track of my array of structs. However something is going wrong where my program doesn't seem to be recognizing newRecord as a function, which ends up throwing the whole program off.
I get syntax errors for all the functions inside of newRecord, though I beleive it's because, as I mentioned, the program seems to be unable to recognize newRecord as a User defined Function.
Use of struct PAYRECORD is wrong since there is no such type. You only have a typedef named PAYRECORD.
If you want to be able to use struct PAYRECORD as well as just PAYRECORD, change the definition of the struct to:
typedef struct PAYRECORD {
char name[100];
int age;
float hrlyWage;
float hrsWorked;
float regPay;
float otPay;
float totalPay;
DATE payDate;
} PAYRECORD;
If that's not your goal, change the use of struct PAYRECORD by just PAYRECORD.
Also, the line:
record[index].name = {'\0'};
in newRecord is not correct. You cannot assign to an array like that. Change it to:
record[index].name[0] = '\0';
The struct PAYRECORD does not exist, the compiler has no idea how big that is.
Note that PAYRECORD is a typedef to an anonymous struct. So your function
should look like this:
int newRecord(PAYRECORD record[], int index){
//set name to \0 so it can work as string
record[index].name[0] = 0;
index++;
return index;
}
Also note that {'\0'}; works only when initializing a array when you declare
it:
char arr1[10] = { '\0' }; // OK
char arr2[10];
arr2 = { '\0' }; // NOT OK
// error: expected expression before ‘{’ token
// a = { '\0' };
// ^
And when writing functions that take arrays as an argument, you should also pass
the size of the array.
int newRecord(PAYRECORD record[], int index, size_t len){
if(record == NULL)
return -1; // error, NULL passed
if(index >= len)
return -1; // error, cannot access array
record[index].name[0] = 0;
index++;
return index;
}
And then you can call it from main like this:
PAYRECORD record[SIZE];
...
int index = 0;
if(newRecord(record, index, sizeof record / sizeof *record) != index)
{
// error handling
}
This makes the code more robust. You always have to check the array boundaries,
otherwise you might read/write out of bounds. And also check that NULL has not
been passed as well, if you dereference NULL, your program will crash with
segfault.
Also, the parameter to newRecord could be a PAYARRAY, not an array directly; based on declaring
typedef struct { } PAYRECORD, PAYARRAY[SIZE];
int newRecord(PAYARRAY record, int index) {...}
int main(){
...
PAYARRAY record;
...
case 'A':
index = newRecord(&record, index);
}
The compiler should be converting the PAYARRAY or PAYRECORD[] argument to a PAYRECORD * pointing to the first element, so use of the '&' is indicated for the function call.

Segmentation fault, cannot increment

There is a function that asks the user which text file to open, opens it and then passes the array of structures that was passed into the function along with the file pointer to another function that reads in data from file into the structure. The array structure for testing purposes only has the value char name[25];. I can assign one line at a time from the file to the same structure index all I want but when I try an increment it I get a segmentation fault no matter what approach I've taken.
The structure has been type defined as well.
The code is:
void oSesame(char usrTxt[], int len, FILE * pFile, Country * p)
{
pFile = fopen(usrTxt, "rw");
if(pFile != NULL)
{
readIn(pFile, &p);
}
else
{
printf("Error opening %s , check your spelling and try again.\n", usrTxt);
}
}
void readIn(FILE * pfile, Country ** p)
{
int count = 0;
int i = 0;
for(i = 0; i<3; i++)
{
fgets((*p[i]).cntName, MAX_COUNTRY_LENGTH, pfile);
}
fclose(pfile);
}
The header file:
//Header.h
#define COUNTRY_MAX 10
#define MAX_COUNTRY_LENGTH 25
#define MAX_CAPITAL_LENGTH 25
typedef struct country
{
char cntName[MAX_COUNTRY_LENGTH];
char capName[MAX_CAPITAL_LENGTH];
double population;
}Country;
int ask(char * usrTxt);
void oSesame(char usrTxt[], int len, FILE * pFile, Country * p);
void readIn(FILE * pFile, Country ** p);
The main code:
#include <stdio.h> //for testing within main
#include <string.h> //for testing within main
#include "headers.h"
int main()
{
int len;
FILE * fileP;
char UI[25];
Country c[10];
Country * ptr;
ptr = c;
len = ask(UI);
oSesame(UI, len, fileP, ptr);
return 0;
}
You are passing Country** for some reason and then handling it as *p[index]. This is wrong. You could use (*p)[index] but the correct way is not to take a reference to the Country* in the first place.
The way you're doing it means you have a pointer to pointer to Country. When you index that you are moving to next pointer to pointer, which is not the same as moving to the next pointer. Undefined behaviour happens.

Can't more than two strings compare?

If we compared integers we would assign one of them as the largest/smallest one.
However, when I try comparing more than two strings, I can't manage assaigment.
In my code "for loop" compares two of the strings. This is good method but I need to compare one of them to the others individually. (I can predict that I need to use two for loop, but also I can't implement) What is your suggestions?
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct wordSorting
{
char name[15];
int i = 0;
};
int main()
{
wordSorting *wordElement = (wordSorting *)malloc(sizeof(wordSorting));
wordElement = (wordSorting *)malloc(sizeof(wordSorting));
printf("-- Enter three person name --\n\n");
for (wordElement->i = 0; wordElement->i < 3; wordElement->i++)
{
printf("Enter %d. person name: ", wordElement->i + 1);
scanf("%s", wordElement[wordElement->i].name);
}
printf("\n");
for (wordElement->i = 0; wordElement->i < 3; wordElement->i++)
{
if ((strcmp(wordElement[wordElement->i].name, wordElement[wordElement->i + 1].name))<0)
{
printf("%s", wordElement[wordElement->i].name);
}
}
}
First
typedef struct wordSorting
{
char name[15];
int i = 0;
};
Members of typedef/struct cannot be initied.
That is not the way to define a typedef, change it as:
typedef struct
{
char name[15];
int i;
}wordSorting;
Second:
wordElement = (wordSorting *)malloc(sizeof(wordSorting));
makes no sense. malloc returns void pointer, and you already init your variable at the first element in the first line of code.
And, as someone edited: do not cast malloc return, please.
Third, :
wordSorting *wordElement = (wordSorting *)malloc(sizeof(wordSorting));
wordElement = (wordSorting *)malloc(sizeof(wordSorting));
printf("-- Enter three person name --\n\n");
for (wordElement->i = 0; wordElement->i < 3; wordElement->i++)
{
printf("Enter %d. person name: ", wordElement->i + 1);
scanf("%s", wordElement[wordElement->i].name);
}
You are allocating space for one element, no array are defined then wordElement[wordElement->i].name is undefined Behaviour.
Finally
I don't know what compiler are you using, but gcc cannot compile such a bad code full of errors...
Suggestion.
What I think you need is to use array, but you must allocate the number of member you need, by:
wordSorting *wordElement = malloc(sizeof(wordSorting)*num_of_elements);
or simply, using a local array:
wordSorting wordElement[num_of_elements];

dynamically expanding array of struct

I am (trying to) write a server-side daemon in c, and it accepts connections from clients. I need a struct that keeps information on each open connection, so I have created an array of my defined struct, and I have it dynamically re-sizing with realloc.
The problem I have is creating the struct within the array. I keep getting this error:
test.c:41: error: conversion to non-scalar type requested
What am I doing wrong?
I spend most of my time in PHP, and am a noob with c. I realize that I am making some simple, beginner mistakes (in other words, feel free to make fun of me). If I am doing something stupid, please let me know. I've put my quality time in with google, but have not figured it out. I have reproduced the issue on smaller scale, as below:
here is my test.h:
typedef struct test_ test;
and here is my test.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "test.h"
//define the struct
struct test_ {
int id;
char *ip;
int user;
char *str;
};
//yes, the list needs to be global
test *test_list;
//
// add an item to the global list
//
int add(int id, char *ip, int size)
{
//
// increment size
if(id>size) {
size = id;
//try to expand the list
test *tmp = realloc(test_list,size);
if(tmp) {
//it worked; copy list back
test_list = tmp;
} else {
//out of memory
printf("could now expand list\n");
exit(1);
}
}
//
// HERE IS THE TROUBLE CODE::
test_list[id] = (struct test)malloc(sizeof(test)+(sizeof(int)*5)+strlen(ip)+1);
test_list[id].id = id;
test_list[id].ip = malloc(strlen(ip));
strcpy(test_list[id].ip,ip);
test_list[id].user = 0;
test_list[id].str = NULL;
}
//
// main
//
int main(void)
{
//initialize
int size = 1;
test_list = malloc(size*sizeof(test));
//add 10 dummy items
int i;
for(i=0; i<10; i++) {
size = add(i, "sample-ip-addr", size);
}
//that's it!
return 0;
}
Try changing
test *tmp = realloc(test_list,size);
to
test *tmp = realloc(test_list,size*sizeof(test));
then delete
test_list[id] = (struct test)malloc(sizeof(test)+(sizeof(int)*5)+strlen(ip)+1);
When you allocate for test_list, there's already space for each member of the struct allocated, so you don't need to do it again. You just have to allocate for any pointers within the struct
The return value from 'malloc' is the memory address you've allocated. You can't cast it to a struct. What would that even mean?
You want something like: test_list=realloc(test_list, num_alloc * sizeof(test_));

Resources