Struct arrays in C - c

Hi I'm having trouble trying to initializing each element of the struct array. When I try and assign the value ZERO to both 'bSize' and 'msgs', it doesn't work as it errors out when i get to malloc. In the printf statement it prints a -1852803823 number. Excuse the messy code as i'm playing around trying to figure it out.
struct message{
int *data;
int bSize;
int msgs;
};
int main(int argc, char *argv[]) {
.....
}
void getSchedFile (FILE *file, int **schd) {
struct message sMsg[nodeCount];
const int pakSize = 6;
// Iniitialise message buffer
for (int i=0; i<nodeCount; i++){
sMsg[i].bSize = 0;
sMsg[i].msgs = 0;
printf("bSize %d\n",sMsg[i].bSize);
}
/* Get the number of bytes */
fseek(file, 0L, SEEK_SET);
int time;
while((fscanf(file, "%d", &time)) != EOF){
int src;
fscanf(file, "%d", &src); // get source node id
// These are here for easier reading code
int aPos = sMsg[src].bSize;
int nMsg = sMsg[src].msgs;
printf("size %d\n", sMsg[src].bSize);
if (sMsg[src].bSize==0){
sMsg[src].data = malloc( pakSize * sizeof(int));
}else{
sMsg[src].data = realloc(sMsg[src].data, (aPos+pakSize)*sizeof(int));
}

Where is the nodeCount value coming from? Is it a global variable? You should be very careful with global variables, and avoid using them if possible.
Pass the nodeCount in the method parameter and as Charlie mentioned, check it for > 0

Related

creating a generic array taken from an array of structures

I need to pass to qsort a generic array. That array must be taken from the colons of a file structured like this:
int,string,int,float.
I've created an appropriate struct type but I'm having troubles creating a dinamically allocated array of structures.
here I created an array of pointers to structures
struct *p_structure=malloc(n_records*sizeof(struct record_type*));
and then I have no idea on what to do :(
Any help would be really appreciated, thanks
PS I calculated the n_records in advance
I've created an appropriate struct type but I'm having troubles creating a dinamically allocated array of structures.
Assuming this is your struct:
struct record {
int i1;
char *s;
int i2;
float f;
};
This should be the array declaration:
struct record *array = malloc(sizeof(*array) * n_records);
and then I have no idea on what to do
In case you didn't do it already, you need to parse the file (see Appendix).
Once parsed, you have to define how your array items should compare against each another. In other words, what are the criteria that should be taken into consideration to compare (and of course, sort) your items. Programmatically, you need to provide a function that does that. That function's signature must match the signature of the function that qsort accepts as argument. For example (considering i1 as the comparison criteria):
int compare_record(const void *p1, const void *p2)
{
const struct record *r1 = p1;
const struct record *r2 = p2;
if (r1->i1 > r2->i1)
return 1;
if (r1->i1 < r2->i1)
return -1;
return 0;
}
Now, calling qsort becomes straightforward:
qsort(array, n_records, sizeof(*array), compare_record);
Appendix
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct record {
int i1;
char *s;
int i2;
float f;
};
int compare_record(const void *p1, const void *p2)
{
const struct record *r1 = p1;
const struct record *r2 = p2;
if (r1->i1 > r2->i1)
return 1;
if (r1->i1 < r2->i1)
return -1;
return 0;
}
int main(void)
{
int n_records = 3;
FILE *file = fopen("filename.txt", "r");
if (!file) {
printf("Opening file failed.\n");
exit(EXIT_FAILURE);
}
struct record *array = malloc(sizeof(*array) * n_records);
if (!array) {
printf("An internal error has occurred.\n");
exit(EXIT_FAILURE);
}
int index = 0;
char line[1024]; // Large enough to hold a line?
int i1, i2;
float f;
char s[256];
while (fgets(line, sizeof line, file)) { // PS I calculated the n_records in advance
if (sscanf(line, "%d,%255[^\n,],%d,%f", &i1, s, &i2, &f) != 4) {
// Problematic line...
printf("Problematic line %d...\n", index+1);
}
array[index].i1 = i1;
array[index].s = strdup(s);
array[index].i2 = i2;
array[index].f = f;
++index;
printf("%d | %s | %d | %f\n", i1, s, i2, f);
}
fclose(file);
qsort(array, n_records, sizeof(*array), compare_record);
for (int i = 0; i < n_records; ++i)
printf("%d | %s | %d | %f\n", array[i].i1, array[i].s, array[i].i2, array[i].f);
// Don't forget to free the memory allocated by strdup().
}
It seems that you just created a structure pointer but you don't give it a variable name.
Try this:
struct p_structure *your_var_name =malloc(n_records*sizeof(struct record_type*));
If it won't work, could you provide the code of your structures please ?

SegFault when switching from small test file to 31 mb file

Below is my (incomplete) code for a merge sort project. This worked fine for the parts I have implemented until I switched from the 128 line test file to the 31 mb file that is supposed to be sorted. Now getting a segfault and I'm not sure what to do in order to solve this.
Removed some lines I believe are inconsequential because "mostly code".
struct Record {
char key[KEYSIZE+1];
char data[DATASIZE+1];
};
int threadCount;
int tiers;
static struct ThdArg {
int thdNum; // Thread number 0,1,2,3
struct Record * lowRec; // First record of group or first index of record
struct Record * hiRec; // Last record of group or last index of record
};
int lines;
int tiers;
void *threadFunc(void *var)
{
struct ThdArg temp2 = *((struct ThdArg*)var);
qsort((temp2.lowRec), lines/threadCount, sizeof(struct Record), comparator);
for(int k=0;k<tiers;k++)
if(temp2.thdNum%(int)(pow(2,k+1))==0)
{
qsort((temp2.lowRec), lines/(threadCount/(int)pow(2,k+1)), sizeof(struct Record),comparator);
}
}
int main(int argc, char **argv[])
{
if (argc!=2)
{
printf("Please enter a file name");
return 0;
}
threadCount =8;
tiers =(int)log2((double)threadCount);
pthread_t threads[threadCount];
FILE *recordFile=fopen(argv[1], "r");
char ch;
fseek(recordFile, 0, SEEK_END);
lines = ftell(recordFile);
fseek(recordFile, 0, SEEK_SET);
lines=lines/64;
struct Record recArr[lines];
char buffer[9];
char buffer2[57];
for(int j=0;j<lines;j++)
{
fgets(buffer, 9, recordFile);
for(int i=0;i<8;i++)
{
recArr[j].key[i]=buffer[i];
}
recArr[j].key[8]='\0';
fgets(buffer2, 57, recordFile);
for(int i=0;i<56;i++)
{
recArr[j].data[i]=buffer2[i];
}
recArr[j].data[57]='\0';
}
struct ThdArg temp[threadCount];
for(int i=0;i<threadCount;i++)
{
temp[i].thdNum = i;
temp[i].lowRec=&recArr[(lines/threadCount)*i];
temp[i].hiRec=&recArr[(lines/threadCount)*(i+1)-1];
pthread_create(&threads[i],NULL, threadFunc, (void *)&temp[i]);
}
for(int i=0;i<threadCount;i++)
{
pthread_join(threads[i], NULL);
}
}
The following line:
struct Record recArr[lines];
allocates memory on the stack. Its size is restricted.
If you read a file which can be be very big use malloc:
#include <stdlib.h>
typedef struct {
char key[KEYSIZE +1];
char data[DATASIZE +1];
}Record;
...
recArr = malloc(sizeof(Record) * lines);
...
free(recArr);
You can use the pointer like an array. (In fact, they are the same)

Hash Table in C (find the frequency of every word)

I want to create a hash table for an exercise I have to send in my University.
The program will open a number of files, break each file's content to <<words>> (tokens) and it will save each <<word>> in a hash table with the frequency of each <<word>>.
In case the word is already in the hash table , the program will increase the word's frequency.
At the end the program will print the words and it's frequencies accordingly.
Also the frequencies should be printed from the highest word frequency to the lowest.
The comparison of the <<words>> will ignore upper and lower case letters.
For example if a file contains : one two three four Two Three Four THREE FOUR FoUr
It should print:
four 4
three 3
two 2
one 1
The professor gave us a template that we should complete but I'm really confused on what to do with the insert_ht() and clear_ht() functions as well as the compare one.
Here is the code :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define HTABLE_SIZ 1001
#define MAX_LINE_SIZ 1024
/* Hash Table */
typedef struct node* link;
struct node { char *token; int freq; link next; };
link htable[HTABLE_SIZ] = { NULL }; /* Table of lists (#buckets) */
int size = 0; /* Size (number of elements) of hash table */
unsigned int hash (char *tok );
void insert_ht (char *data);
void clear_ht ( );
void print_ht ( );
void Process(FILE *fp);
int main(int argc, char *argv[])
{
int i;
FILE *fp;
for (i=1; i < argc; i++)
{
fp = fopen(argv[i],"r");
if (NULL == fp)
{
fprintf(stderr,"Problem opening file: %s\n",argv[i]);
continue;
}
Process(fp);
fclose(fp);
}
print_ht();
clear_ht();
return 0;
}
void Process(FILE *fp)
{
const char *seperators = " ?!'\";,.:+-*&%(){}[]<>\\\t\n";
char line[MAX_LINE_SIZ];
char *s;
while((fgets(line,MAX_LINE_SIZ, fp)) != NULL)
{
for (s=strtok(line,seperators); s; s=strtok(NULL,seperators))
insert_ht(s);
}
}
/* Hash Function */
unsigned int hash(char *tok)
{
unsigned int hv = 0;
while (*tok)
hv = (hv << 4) | toupper(*tok++);
return hv % HTABLE_SIZ;
}
void insert_ht(char *token)
{
……………………………………………
}
void clear_ht()
{
……………………………………………
}
int compare(const void *elem1, const void *elem2)
{
……………………………………………
}
void print_ht()
{
int i, j=0;
link l, *vector = (link*) malloc(sizeof(link)*size);
for (i=0; i < HTABLE_SIZ; i++)
for (l=htable[i]; l; l=l->next)
vector[j++] = l;
qsort(vector,size,sizeof(link),compare);
for (i=0; i < size; i++)
printf("%-50s\t%7d\n",vector[i]->token,vector[i]->freq);
free(vector);
}
I'll answer you in a new post because it's hard to be exhaustive in comments.
1. Malloc
Why would I need to use malloc then ? Shouldn't i write directly to the htable? (on the insert_ht() funtion)
You need to use malloc because you declare a char pointer in struct (char *token). The thing is that you never initialize the pointer to anything, and as far you don't know the size of the token, you need to malloc every token. But, as you use strdup(token), you don't need to malloc token because strdup does. So don't forget to free every token in order to avoid memory leaks.
2. Segfault
I can't test you code, but it seems like the following line causes the segmentation fault :
list = htable[hashval]->token
Indeed, you try to access token while htable[hashval] is NULL, and to assign a char * to a link type (list).
You need to loop with this :
for(list = htable[hashval]; list != NULL; list = list->next) { ... }
3. Notes
if (x=1) should be if(x==1).
Don't malloc new_list if you don't need to.
Because new_list if used when htable[hashval] is NULL, new_list->next = htable[hashval]; will set new_list->next to NULL.
You should use the -Wall option in gcc (for warnings) and you may use valgrind to understand your segmentation faults. In this case, use gcc with debug mode (-g).
Double and Final edit : Ι found the solution. Apparently for some reason my compare function was wrong.
I still haven't figured out why but here is the correct one, hopefully someone else will find this post helpful!
int compare(const void *elem1, const void *elem2)
{
return (*(link*)elem2)->freq - (*(link*)elem1)->freq;
}
Edit: deleted old answer . Found the correct way I think but I have another problem right now.
The compare function doesn't work correctly. My printf is fine but it doesnt sort them with the frequiencies. I want them to be sorted from the highest to lowest .
In this example: the file contains -> one two three four Two Three Four THREE FOUR FoUr
And I get:
two 2
one 1
four 4
three 3
While I should be getting :
four 4
three 3
two 2
one 1
Here is the code. Feel free to help!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define HTABLE_SIZ 1001
#define MAX_LINE_SIZ 1024
/* Hash Table */
typedef struct node* link;
struct node { char *token; int freq; link next; };
link htable[HTABLE_SIZ] = { NULL }; /* Table of lists (#buckets) */
int size = 0; /* Size (number of elements) of hash table */
unsigned int hash (char *tok );
void insert_ht (char *data);
void clear_ht ( );
void print_ht ( );
void Process(FILE *fp);
int main(int argc, char *argv[])
{
int i;
FILE *fp;
printf("prin tin for \n");
for (i=1; i < argc; i++)
{
printf("prin tin fopen \n");
fp = fopen(argv[i],"r");
if (NULL == fp)
{
fprintf(stderr,"Problem opening file: %s\n",argv[i]);
continue;
}
printf("prin tin process \n");
Process(fp);
fclose(fp);
}
print_ht();
//clear_ht();
return 0;
}
void Process(FILE *fp)
{
const char *seperators = " ?!'\";,.:+-*&%(){}[]<>\\\t\n";
char line[MAX_LINE_SIZ];
char *s;
while((fgets(line,MAX_LINE_SIZ, fp)) != NULL)
{
for (s=strtok(line,seperators); s; s=strtok(NULL,seperators)){
printf("prin tin insert %s \n",s);
insert_ht(s);
}
}
}
/* Hash Function */
unsigned int hash(char *tok)
{
printf("bike stin hash \n");
unsigned int hv = 0;
while (*tok)
hv = (hv << 4) | toupper(*tok++);
printf("VGAINEIIIIIIIIIIIIII %d \n",hv);
return hv % HTABLE_SIZ;
}
void insert_ht(char *token)
{
printf("bike stin insert %s \n",token);
unsigned int hashval = hash(token);
if (htable[hashval]==NULL){
printf("mesa stin prwti if %u %s \n",hashval,token);
//token = strdup(token);
htable[hashval] = malloc(sizeof(token));
htable[hashval]->token = token ;
htable[hashval]->freq = 1;
size++;
}else {
htable[hashval]->freq++;
}
printf("ta evale epitixws \n");
}
int compare(const void *elem1, const void *elem2)
{
const struct node *p1 = elem1;
const struct node *p2 = elem2;
if ( p1->freq < p2->freq)
return -1;
else if (p1->freq > p2->freq)
return 1;
else
return 0;
}
void print_ht()
{
int i, j=0;
link l, *vector = (link*) malloc(sizeof(link)*size);
for (i=0; i < HTABLE_SIZ; i++)
for (l=htable[i]; l; l=l->next)
vector[j++] = l;
qsort(vector,size,sizeof(link),compare);
for (i=0; i < size; i++)
printf("%-50s\t%7d\n",vector[i]->token,vector[i]->freq);
free(vector);
}
Sorry for my bad english.
I think that :
insert(char *token) takes a word of the file and puts into the hash table. In brief, if the word exists in the hash table, you just have to increment its frequencie. Otherwise, you need to create another node and put the frequencie to 1, then ad it to the array. At the end, you will have one entry for each unique word.
compare(const void *elem1, const void *elem2) will be used by qsort. It returns 0 if elem1 = elem2, a negative number if elem1 < elem2 and a number > 0 if elem1 > elem2. By passing compare to qsort, you allow qsort to sort you array according to your own criteria.
clear_ht() may set all the values of the array to NULL, in order to restart another count ?

Passing struct to functions

Hey I'm not sure why when I pass a Struct array to a function; When I try to access it's members it prints random number. Below the statement "printf("%d\n", netTopo[0].nodes[1]);" works correct but I'm in the function and try print the same data, it prints a bunch of random number? Not sure what I'm doing wrong.
int main(int argc, char *argv[]) {
if (argc != 3){
printf("Incorrect command line arguments. Required 2 files.\n");
exit(-1);
}
FILE *netFile, *schFile; // put into a loop
netFile = fopen(argv[1], "r");
schFile = fopen(argv[2], "r");
int *sched = getSchedFile(schFile);
struct nodeInfo *netTopo = getTopology(netFile);
printf("%d\n", netTopo[0].nodes[1]);
int nodeSocks[nodeCount];
for (int i=0; i<nodeCount; i++){
nodeSocks[i]=getSocketNo();
}
get_elapsed_time(); // start clock
for (int i=0; i<nodeCount; i++){
if (fork()==0){
nodeExecution(i, nodeSocks, netTopo, sched);
exit(0);
}
}
}
void nodeExecution(int id, int nodes[], struct nodeInfo *netTopo, int *schd){
printf("%d\n", netTopo[0].nodes[1]);
......
so you return a pointer to local var on stack from getTopology()? that's the bug.
netTopo is on stack and when you return from getTopology() there are other function calls which would reuse the memory region where netTopo is stored. That memory is modified and you get different output when calling nodeExecution()
ADD: to fix this you may allocate memory in getTopology():
struct nodeInfo* getTopology(FILE *file){
int id, digit=0, totLinks=0;
fscanf(file, "%d", &nodeCount);
struct nodeInfo * netTopo = malloc(sizeof(struct nodeInfo)*nodeCount);
....

How do I unpack and extract data properly using msgpack-c?

I'm currently trying to use msgpack in a project written in C. I'm using msgpack for the purpose of serializing the contents of a struct, which is then to be sent over the network, and deserialized back into a corresponding struct on the other side.
Condensed version of what I'm trying to do:
#include <stdio.h>
#include <msgpack.h>
#include <stdbool.h>
typedef someStruct{
uint32_t a;
uint32_t b;
float c;
} someStruct;
int main (void){
someStruct data;
/* ... Fill 'data' with some data for test purposes ...*/
msgpack_sbuffer* buff = msgpack_sbuffer_new();
msgpack_packer* pck = msgpack_packer_new(buff, msgpack_sbuffer_write);
someStruct* structs = malloc(sizeof(someStruct) * 10);
/* ... Fill 'structs' with members containing test data ... */
// Serialize
msgpack_pack_array (pck, 10);
int i;
for(i = 0 ; i < 10 ; i++){
msgpack_pack_array (pck, 3);
msgpack_pack_uint32 (pck, structs[i].a);
msgpack_pack_uint32 (pck, structs[i].b);
msgpack_pack_float (pck, structs[i].c);
}
free(structs);
msgpack_packer_free(pck);
// Deserialize
msgpack_unpacked msg;
msgpack_unpacked_init(&msg);
bool deserialize_success = msgpack_unpack_next
(&msg, buff->data, buff->size, NULL);
if(!deserialize_success) /* Error */
msgpack_object obj = msg.data;
msgpack_object_print(stdout,obj); // This seems to work perfectly, indicating serialize / deserialize works as intended...
someStruct deserialized_data;
/* Insert code to extract and cast deserialized data to 'deserialized_data */
// Clean
msgpack_sbuffer_free(buff);
msgpack_packer_free(pck);
return 0;
}
The code listed is more or less ripped straight from here, which seems to be one of very few resources on msgpack-c.
Can anyone point me in the right direction as to a way to 'recreate' the original struct on the other side of the wire? The only way I've found to actually utilize the deserialized data, is to use the msgpack_object_print() call to print from the messagepack_object. This does, however seem to work, so I'm certain the data is there.
Do I need to somehow loop through the serialized data and use msgpack_unpack_next() with an offset to retrieve each someStruct member? Using memcpy to a local byte buffer?
Any help is greatly appreciated!
Please find below a rewritten version that illustrates how to pack / unpack your data.
The whole idea is to pack each successive field of your struct, in a contiguous fashion, and apply (of course), the same logic at unpack time.
Right after pack, you are free to use the buffer the way you want (e.g send over the network, save on-disk, etc).
#include <stdio.h>
#include <assert.h>
#include <msgpack.h>
typedef struct some_struct {
uint32_t a;
uint32_t b;
float c;
} some_struct;
static char *pack(const some_struct *s, int num, int *size);
static some_struct *unpack(const void *ptr, int size, int *num);
/* Fixtures */
some_struct ary[] = {
{ 1234, 5678, 3.14f },
{ 4321, 8765, 4.13f },
{ 2143, 6587, 1.34f }
};
int main(void) {
/** PACK */
int size;
char *buf = pack(ary, sizeof(ary)/sizeof(ary[0]), &size);
printf("pack %zd struct(s): %d byte(s)\n", sizeof(ary)/sizeof(ary[0]), size);
/** UNPACK */
int num;
some_struct *s = unpack(buf, size, &num);
printf("unpack: %d struct(s)\n", num);
/** CHECK */
assert(num == (int) sizeof(ary)/sizeof(ary[0]));
for (int i = 0; i < num; i++) {
assert(s[i].a == ary[i].a);
assert(s[i].b == ary[i].b);
assert(s[i].c == ary[i].c);
}
printf("check ok. Exiting...\n");
free(buf);
free(s);
return 0;
}
static char *pack(const some_struct *s, int num, int *size) {
assert(num > 0);
char *buf = NULL;
msgpack_sbuffer sbuf;
msgpack_sbuffer_init(&sbuf);
msgpack_packer pck;
msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
/* The array will store `num` contiguous blocks made of a, b, c attributes */
msgpack_pack_array(&pck, 3 * num);
for (int i = 0; i < num; ++i) {
msgpack_pack_uint32(&pck, s[i].a);
msgpack_pack_uint32(&pck, s[i].b);
msgpack_pack_float(&pck, s[i].c);
}
*size = sbuf.size;
buf = malloc(sbuf.size);
memcpy(buf, sbuf.data, sbuf.size);
msgpack_sbuffer_destroy(&sbuf);
return buf;
}
static some_struct *unpack(const void *ptr, int size, int *num) {
some_struct *s = NULL;
msgpack_unpacked msg;
msgpack_unpacked_init(&msg);
if (msgpack_unpack_next(&msg, ptr, size, NULL)) {
msgpack_object root = msg.data;
if (root.type == MSGPACK_OBJECT_ARRAY) {
assert(root.via.array.size % 3 == 0);
*num = root.via.array.size / 3;
s = malloc(root.via.array.size*sizeof(*s));
for (int i = 0, j = 0; i < root.via.array.size; i += 3, j++) {
s[j].a = root.via.array.ptr[i].via.u64;
s[j].b = root.via.array.ptr[i + 1].via.u64;
s[j].c = root.via.array.ptr[i + 2].via.dec;
}
}
}
msgpack_unpacked_destroy(&msg);
return s;
}

Resources