Whats wrong when copying my nested struct? - c

I code C to get better in programming and study... and have program that should generate a static web-page. It also saves the project as a text-file. I have separate functions to make object (realloc and put a new struct...), and have extracted the problem-code to a short program for this occasion... It's just for reading the 'project'. When I run it says:
Segmentation fault (core dumped)
in the middle of the print_1_content
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define SELL_ITEM 1
#define PARAGRAPH_ITEM 2
struct SellItem {
char title[50];
int nr_of_img;
char ** image_files;//array of strings
};
struct ParagraphItem{
char * text;
};
union ContentItem{//one of the following only
struct SellItem s_item;
struct ParagraphItem p_item;
};
struct Content{
int type;//1=sellitem 2=paragraph
union ContentItem c_item;
};
int open_items_file(struct Content **, int *, char *);
int free_1_item(struct Content *);
struct Content import_1_content(char *);
void increase(struct Content**, int *);
void print_1_content(struct Content *);
struct Content import_1_content(char *);
int free_1_item(struct Content *);
int main (void)
{
struct Content * content;
int content_count=0;
open_items_file(&content, &content_count, "all_items.txt");
return 0;
}
int open_items_file(struct Content ** content, int * number_of_content, char * filename){
printf("open_items_file %s\n", filename);
FILE *fp = fopen(filename, "r");
char * line = NULL;
size_t len = 0;
ssize_t read;
int counter=0;
if(fp==NULL){
return 0;
}
//for each row
while ((read = getline(&line, &len, fp)) != -1) {
if((line[0]=='S' || line[0]=='P') && line[1]=='-'){
if(line[3]==':'){
if(line[2]=='I'){
increase(content, number_of_content);
*content[(*number_of_content)-1] = import_1_content(line);
}
else{
//not sell/paragraph item
}
}//end if line[3]==':'
}//end if line[0] =='S' eller 'P'
counter++;
}
free(line);
fclose(fp);
return counter;
}
void increase(struct Content** content, int *nr_of_content){
if((*nr_of_content)==0){
*content = malloc(sizeof(struct Content));
}
else{
*content = realloc(*content, (*nr_of_content+1) * sizeof(struct Content));
}
(*nr_of_content)++;
}
void print_1_content(struct Content * content){
//Print info
}
struct Content import_1_content(char * text_line){
struct Content temp_content_item;
char * line_pointer = text_line;
char c;
line_pointer += 4;
if(text_line[0]=='S'){
temp_content_item.type = SELL_ITEM;
temp_content_item.c_item.s_item.nr_of_img=0;
int i=0;
char * temp_text;
while(*line_pointer != '|' && *line_pointer != '\n' && i < sizeof(temp_content_item.c_item.s_item.title)-1){
temp_content_item.c_item.s_item.title[i] = *line_pointer;
i++;//target index
line_pointer++;
}
temp_content_item.c_item.s_item.title[i]='\0';
i=0;
//maybe images?
short read_img_counter=0;
if(*line_pointer == '|'){
line_pointer++; //jump over '|'
//img-file-name separ. by ';', row ends by '\n'
while(*line_pointer != '\n'){//outer image filename -loop
i=0;
while(*line_pointer != ';' && *line_pointer != '\n'){//steps thr lett
c = *line_pointer;
if(i==0){//first letter
temp_text = malloc(2);
}
else if(i>0){
temp_text = realloc(temp_text, i+2);//extra for '\0'
}
temp_text[i] = c;
line_pointer++;
i++;
}
if(*line_pointer==';'){//another image
line_pointer++;//jump over ';'
}
else{
}
temp_text[i]='\0';
//allocate
if(read_img_counter==0){//create array
temp_content_item.c_item.s_item.image_files = malloc(sizeof(char*));
}
else{//extend array
temp_content_item.c_item.s_item.image_files = realloc(temp_content_item.c_item.s_item.image_files, sizeof(char*) * (read_img_counter+1));
}
//allocate
temp_content_item.c_item.s_item.image_files[read_img_counter] = calloc(i+1, 1);
//copy
strncpy(temp_content_item.c_item.s_item.image_files[read_img_counter], temp_text, strlen(temp_text));
read_img_counter++;
temp_content_item.c_item.s_item.nr_of_img = read_img_counter;
}
}
else{
printf("Item had no img-files\n");
}
}
else{ // text_line[0]=='P'
temp_content_item.type = PARAGRAPH_ITEM;
temp_content_item.c_item.p_item.text = calloc(strlen(text_line)-4,1);
int i=0;
while(*line_pointer != '\0' && *line_pointer != '\n'){
temp_content_item.c_item.p_item.text[i] = *line_pointer;
i++;
line_pointer++;
}
}
print_1_content(&temp_content_item);
return temp_content_item;
}
int free_1_item(struct Content * item){
if(item->type==SELL_ITEM){
if(item->c_item.s_item.nr_of_img > 0){
//Freeing img-names
for(int i=0; i<item->c_item.s_item.nr_of_img; i++){
free(item->c_item.s_item.image_files[i]);
}
}
return 1;
}
else if(item->type==PARAGRAPH_ITEM){
//freeing p_item
free(item->c_item.p_item.text);
return 1;
}
else{
printf("error: unknown item\n");
}
return 0;
}
The text file to read (all_items.txt) is like this, ends with a new-line, for the two content types of "sell-item" and "paragraph-item":
S-I:Shirt of cotton|image1.jpg;image2.jpg;image3.jpg
P-I:A paragraph, as they are called.
S-I:Trousers, loose style|image4.jpg

So the problem as you've discovered is on this line:
*content[(*number_of_content)-1] = temp_content_item2;
It's because of operate precedence because *content[(*number_of_content)-1] is not the same as (*content)[(*number_of_content)-1], it's actually doing *(content[(*number_of_content)-1]). So your code is doing the array indexing and then de-referencing which is pointing at some random place in memory. Replace that line with this and that will fix the current problem.
(*content)[(*number_of_content)-1] = temp_content_item2;

Related

How can I return list in C?

I am trying to divide the string with *, and return the divided strings, as follows.
abc*d*efg*hijk -> [abc,d,efg,hijk]
This is my code, where *pattern is the given string, and I first count the number of asterisk(cnt), and make a empty list with length cnt. But it keeps getting the error and I don't get it... Can anyone help me?
error message
value computed is not used (*star_cnt++;)
function returns address of local variable(return units;)
Number 2 is my main error. I can't return the list
int Slice(char *pattern) {
int *star_cnt;
int cnt;
*star_cnt = *pattern;
cnt = 0;
while (*star_cnt != '\0') {
if (*star_cnt == '*') {
cnt++;
}
*star_cnt++;
}
int units[cnt];
int *unit;
int unit_cnt;
unit_cnt = 0;
*unit = *pattern;
while (*unit != '\0') {
int *new_unit;
while (*unit != '*'){
*new_unit = *unit;
unit++;
new_unit++;
}
unit++;
units[unit_cnt] = *new_unit;
}
return units;
I felt there were a number of things wrong, and that looking at a working example might actually help a bit more here.
You could try something like this:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
/**
* #fn Slice()
* #param [IN] pattern - pointer to string to be analysed
* #param
* #return pointer to array for strings, array is terminated by NULL
* */
char** Slice(char *pattern) {
char *star_cnt;
int cnt;
char** resultlist;
star_cnt = pattern;
cnt = 0;
while (*star_cnt != '\0') {
if (*star_cnt == '*') {
cnt++;
}
star_cnt++;
}
printf("%d items\n",cnt+1);
resultlist = malloc(sizeof(char*) * (cnt+2));
memset(resultlist,0,sizeof(char*) * (cnt+2));
star_cnt = pattern;
cnt = 0;
resultlist[cnt] = star_cnt;
//printf("item %d: %s\n",cnt,resultlist[cnt]);
cnt++;
while (*star_cnt != '\0') {
if (*star_cnt == '*') {
*star_cnt = '\0';
resultlist[cnt] = star_cnt+1;
//printf("item %d: %s\n",cnt,resultlist[cnt]);
cnt++;
}
star_cnt++;
}
return resultlist;
}
int main()
{
char working_string[] = "abc*d*efg*hijk";
char* backup_string = strdup(working_string);
char** list = NULL;
list = Slice(working_string);
int i;
i = 0;
if (list != NULL)
{
while(list[i] != NULL)
{
printf("%d : %s\n",i,list[i]);
i++;
}
free(list);
}
printf("original_string = %s\n",backup_string);
free(backup_string);
}
It produces an output like this:
4 items
0 : abc
1 : d
2 : efg
3 : hijk
original_string = abc*d*efg*hijk
The Slice function basically returns a pointer to char* strings, and the array list is terminated with a NULL in the last element. Keep in mind that in this solution the original string is modified so it cannot be used again.
For storing and returning the result you can also define string container like:
struct c_str_container{
char **arr;
size_t size;
};
And then you can define functions like init_c_str_container, add_element_to_c_str_container and free_c_str_container for dealing with the container.
then you can write the substrings function with using strchr function for finding the delimiters and splitting the string in to sub-strings.
Finally you can use this function to create the container and then after displaying the result from the container (and possibly doing other things with the container) you free the allocated memory by the predefined function free_c_str_container:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct c_str_container{
char **arr;
size_t size;
};
void init_c_str_container(struct c_str_container *container){
container->arr = NULL;
container->size = 0;
}
int add_element_to_c_str_container(struct c_str_container *container, const char *txt, size_t length){
char **newarr = (char **) realloc(container->arr, (container->size + 1) * (sizeof(char *)));
if(!newarr){
newarr = (char **) malloc((container->size + 1) * (sizeof(char *)));
if(!newarr){
return -1;
}else{
for(size_t counter = container->size; counter--;){
newarr[counter] = container->arr[counter];
}
if(container->size){
free(container->arr);
}
}
}
newarr[container->size] = malloc((length + 1) * sizeof(char));
memcpy(newarr[container->size], txt, length);
newarr[container->size][length] = '\0';
container->arr = newarr;
++container->size;
return 0;
}
void free_c_str_container(struct c_str_container *container){
for(size_t counter = container->size; counter--;){
free(container->arr[counter]);
}
free(container->arr);
container->size = 0;
}
struct c_str_container substrings(const char *input, const char delimiter){
const char *input_end = input + strlen(input);
struct c_str_container container;
init_c_str_container(&container);
while(strchr(input, delimiter) == input){
++input;
}
const char *end_point;
while((end_point = strchr(input, delimiter))){
add_element_to_c_str_container(&container, input, (end_point - input));
while(strchr(end_point, delimiter) == end_point){
++end_point;
}
input = end_point;
}
if(input < input_end){
add_element_to_c_str_container(&container, input, (input_end - input));
}
return container;
}
int main(void) {
struct c_str_container container = substrings("***as***we*grow*up", '*');
printf("number of elements is : %zu\n", container.size);
for(size_t counter = 0; counter < container.size; ++counter){
printf("element %zu is : %s\n", counter, container.arr[counter]);
}
free_c_str_container(&container);
printf("now elements are : %zu\n", container.size);
return EXIT_SUCCESS;
}
for the test string ="***as***we*grow*up" delimeter = '*' the result of the program is:
number of elements is : 4
element 0 is : as
element 1 is : we
element 2 is : grow
element 3 is : up
now elements are : 0

Problems on computing statistics and parsing a text file

a must be simple question but I couldn't manage to do it.
I have to scan on a struct a text file with entries in this format:
{"data1","data2",number1,number2}
And compute first populating a struct.
Text of the exercise:
Consider the definition of the following structure
typedef struct {
char teamHome [30];
char teamHost [30];
int goalSquadraHome;
int goalSquadraOspite;
} match;
which is used to represent the result of a football match.
Write a function that takes as parameters an array of games and its size e
returns a result structure containing the following information:
the number of games won by the home team,
the number of games won by the visiting team,
the number of ties,
the name of the team that has scored the most goals in a match.
Then write a program that, given the array containing all 380 Serie A 2019/2020 matches,
print the information contained in the result.
The code is the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
char squadraCasa[30];
char squadraOspite[30];
int golSquadraCasa;
int golSquadraOspite;
} partita;
typedef struct {
int partite_casa;
int partite_ospite;
int pareggi;
char squad_magg_num_goal[30];
} risultato;
int main(){
FILE *fp;
risultato risultati;
int maxgoal = 0;
risultati.partite_casa = 0;
risultati.partite_ospite = 0;
risultati.pareggi = 0;
partita partite[380];
int i=0;
if((fp = fopen("partiteSerieA1920.txt","rt"))==NULL){
printf("Errore nell'apertura del file\n");
exit(1);
}
while(!feof(fp)){
fscanf(fp,"{\"%s\",\"%s\",%d,%d",partite[i].squadraCasa,partite[i].squadraOspite,partite[i].golSquadraCasa,partite[i].golSquadraOspite);
i++;
}
for(i=0;i<380;i++){
if(partite[i].golSquadraCasa>partite[i].golSquadraOspite){
risultati.partite_casa++;
}else if(partite[i].golSquadraCasa<partite[i].golSquadraOspite){
risultati.partite_ospite++;
}else
risultati.pareggi++;
if(partite[i].golSquadraCasa>maxgoal){
strncpy(partite[i].squadraCasa,risultati.squad_magg_num_goal,30);
maxgoal = partite[i].golSquadraCasa;
}
if(partite[i].golSquadraOspite>maxgoal){
strncpy(partite[i].squadraOspite, risultati.squad_magg_num_goal,30);
maxgoal = partite[i].golSquadraOspite;
}
}
fclose(fp);
printf("%d %d %d %s\n",risultati.partite_casa,risultati.partite_ospite,&risultati.pareggi,&risultati.squad_magg_num_goal);
return 0;
}
Please let me know how to arrange it properly.
There may be other issues, but certainly your flow control is wrong. Instead of the incorrect while/feof loop(Why is “while ( !feof (file) )” always wrong?), try something like:
partita *p = partite;
while( 4 == fscanf(fp, "{\"%29s\",\"%29s\",%d,%d",
p->squadraCasa,
p->squadraOspite,
&p->golSquadraCasa,
&p->golSquadraOspite
) ){
p++;
}
Give this a try, its a bit of a different approach:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char squadraCasa[30];
char squadraOspite[30];
int golSquadraCasa;
int golSquadraOspite;
} partita;
typedef struct
{
int partite_casa;
int partite_ospite;
int pareggi;
char squad_magg_num_goal[30];
} risultato;
// solves given problem and stores results in risultato struct
risultato *getResult(partita **playedGames, int size)
{
risultato *result = malloc(sizeof(risultato));
result->partite_casa = 0;
result->partite_ospite = 0;
result->pareggi = 0;
int currentHighest = 0;
for (int i = 0; i < size; i++)
{
if (playedGames[i]->golSquadraCasa > playedGames[i]->golSquadraOspite){
result->partite_casa++;
if(playedGames[i]->golSquadraCasa > currentHighest ){
currentHighest = playedGames[i]->golSquadraCasa;
strcpy(result->squad_magg_num_goal, playedGames[i]->squadraCasa);
}
}
else if (playedGames[i]->golSquadraCasa < playedGames[i]->golSquadraOspite){
result->partite_ospite++;
if (playedGames[i]->golSquadraOspite > currentHighest){
currentHighest = playedGames[i]->golSquadraOspite;
strcpy(result->squad_magg_num_goal, playedGames[i]->squadraOspite);
}
}
else{
result->pareggi++;
}
}
return result;
}
// This is a custom parser of a line from the file
// data = {"data1","data2",number1,number2}
// return -> partita struct
partita *newGame(char *data){
partita *partite = malloc(sizeof(partita));
char* temp;
// Get Home Team
temp = strchr(data, ',') -1;
temp[0] = '\0';
strcpy(partite->squadraCasa, data + 2);
data = temp+1;
// Get Away Team
temp = strchr(data+1, ',') -1;
temp[0] = '\0';
strcpy(partite->squadraOspite, data + 2);
data = temp + 1;
// Get Home Score
temp = strchr(data + 1, ',');
temp[0] = '\0';
partite->golSquadraCasa = atoi(data + 1);
data = temp + 1;
// Get Away Score
temp = strchr(data, '}');
temp[0] = '\0';
partite->golSquadraOspite = atoi(data);
// Return game
return partite;
}
int main()
{
FILE *fp;
partita **partite = malloc(sizeof(partita *)); // list of size one, currently...
risultato *risultati;
char linea[50];
int indice = 0;
if ((fp = fopen("./partiteSerieA1920.txt", "rt")) == NULL)
{
printf("Errore nell'apertura del file\n");
exit(1);
}
// For each linea in the file, load a game into an array.
while (fgets(linea, 50,fp))
{
//chomp the \n
linea[strlen(linea)-1]='\0';
// increase size of list
partite = realloc(partite, sizeof(partita *) * (indice + 1));
// insert game into array of games
partite[indice] = newGame(linea);
indice++;
}
risultati = getResult(partite, indice);
// Print risultato
printf("\n----RESULT----\nHome Wins: %d\nAway Wins: %d\nTies: %d\nTeam With Most Goals: %s\n\n", risultati->partite_casa, risultati->partite_ospite, risultati->pareggi, risultati->squad_magg_num_goal);
// free all allocated memory then return
for (int i = 0; i < indice;i++){
free(partite[i]);
}
free(partite);
free(risultati);
fclose(fp);
return 0;
}
I was trying to run your code but couldnt get it to parse data from the file properly so i made a quick parser for you (This is in the code above already):
partita *newGame(char *data){
partita *partite = malloc(sizeof(partita));
char* temp;
// Get Home Team
temp = strchr(data, ',') -1;
temp[0] = '\0';
strcpy(partite->squadraCasa, data + 2);
data = temp+1;
// Get Away Team
temp = strchr(data+1, ',') -1;
temp[0] = '\0';
strcpy(partite->squadraOspite, data + 2);
data = temp + 1;
// Get Home Score
temp = strchr(data + 1, ',');
temp[0] = '\0';
partite->golSquadraCasa = atoi(data + 1);
data = temp + 1;
// Get Away Score
temp = strchr(data, '}');
temp[0] = '\0';
partite->golSquadraOspite = atoi(data);
// Return game
return partite;
}
You can always try to use something similar to this to parse strings or lines that you bring in as I find it is more efficient to just code something that you know works to the specification you want.
Let me know if there is some problem with the code or would like to know more about the functionality of this. I tried to keep as much of this in Italian.
Cheers

Manipulating structs with a void function in C

so I've been set a task of creating a faux string struct and implementing all the usual string functions on my faux string struct. I'm stuck on the tests of my strcat implementation called append, with the first test failing (segfault) being the 5th line. My function for creating new structs should be OK because it passed all the tests, but I've included it just incase.
I've already been able to successfully implement length, get, set and copy functions for my faux string structs.
The struct:
struct text {
int capacity;
char *content;
};
typedef struct text text;
My function for creating new structs:
text *newText(char *s) {
printf("new Text from %s\n", s);
int sizeNeeded = (strlen(s)+1);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
text *out = malloc(sizeGot);
char *c = malloc(sizeGot);
strcpy(c, s);
out->content = c;
out->capacity = (sizeGot);
printf("the capacity is %d\n", sizeGot);
return out;
free(c);
}
My append function:
void append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %d\n", t1->content, *t2->content);
int sizeNeeded = (t1->capacity + t2->capacity);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
char *stringy = calloc(sizeGot, 32);
stringy = strcat(t1->content, t2->content);
free(t1);
t1 = newText(stringy);
}
and finally the tests:
void testAppend() {
text *t = newText("car");
text *t2 = newText("pet");
append(t, t2);
assert(like(t, "carpet"));
assert(t->capacity == 24);
text *t3 = newText("789012345678901234");
append(t, t3);
assert(like(t, "carpet789012345678901234"));
assert(t->capacity == 48);
freeText(t);
freeText(t2);
freeText(t3);
}
You are allocating memory in the wrong way. You could fix this by using a flexible array member like this:
typedef struct {
int capacity;
char content[];
} text;
text *out = malloc(sizeof(text) + sizeof(something));
strcpy(out->content, str);
...
And obviously code such as this is nonsense:
return out;
free(c);
}
Enable compiler warnings and listen to them.
Och, some errors you have:
Inside text_new you allocate memory for text *out using text *out = malloc(sizeGot); when sizeGot = 24 is a constant value. You should allocate sizeof(*out) or sizeof(text) bytes of memory for it.
I don't know what for int sizeGot = 24; while (sizeNeeded > sizeGot) the loop inside text_new and append is for. I guess the intention is to do allocations in power of 24. Also it mostly looks like the same code is in both functions, it does look like code duplication, which is a bad thing.
Inside append You pass a pointer to t1, not a double pointer, so if you modify the t1 pointer itself the modification will not be visible outside of function scope. t1 = newText(stringy); is just pointless and leaks memory. You could void append(text **t1, text *t2) and then *t1 = newText(stringy). But you can use a way better approach using realloc - I would expect append to "append" the string, not to create a new object. So first resize the buffer using realloc then strcat(&t1->content[oldcapacity - 1], string_to_copy_into_t1).
int sizeNeeded = (t1->capacity + t2->capacity); is off. You allocate capacity in power of 24, which does not really interact with string length. You need to have strlen(t1->content) + strlen(t2->content) + 1 bytes for both strings and the null terminator.
Try this:
size_t text_newsize(size_t sizeNeeded)
{
// I think this is just `return 24 << (sizeNeeded / 24);`, but not sure
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot *= 2;
}
return sizeGot;
}
text *newText(char *s) {
printf("new Text from %s\n", s);
if (s == NULL) return NULL;
int sizeNeeded = strlen(s) + 1;
int sizeGot = text_newsize(sizeNeeded);
text *out = malloc(sizeof(*out));
if (out == NULL) {
return NULL;
}
out->content = malloc(sizeGot);
if (out->content == NULL) {
free(out);
return NULL;
}
strcpy(out->content, s);
out->capacity = sizeGot;
printf("the capacity is %d\n", sizeGot);
return out;
}
and this:
int append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %s\n", t1->content, t2->content);
int sizeNeeded = strlen(t1->content) + strlen(t2->content) + 1;
if (t1->capacity < sizeNeeded) {
// this could a text_resize(text*, size_t) function
int sizeGot = text_newsize(sizeNeeded);
void *tmp = realloc(t1->content, sizeGot);
if (tmp == NULL) return -ENOMEM;
t1->content = tmp;
t1->capacity = sizeGot;
}
strcat(t1->content, t2->content);
return 0;
}
Some remarks:
Try to handle errors in your library. If you have a function like void append(text *t1, text *t2) let it be int append(text *t1, text *t2) and return 0 on success and negative number on *alloc errors.
Store the size of everything using size_t type. It's defined in stddef.h and should be used to represent a size of an object. strlen returns size_t and sizeof also returns size_t.
I like to put everything inside a single "namespace", I do that by prepending the functions with a string like text_.
I got some free time and decided to implement your library. Below is the code with a simple text object storing strings, I use 24 magic number as allocation chunk size.
// text.h file
#ifndef TEXT_H_
#define TEXT_H_
#include <stddef.h>
#include <stdbool.h>
struct text;
typedef struct text text;
text *text_new(const char content[]);
void text_free(text *t);
int text_resize(text *t, size_t newsize);
int text_append(text *to, const text *from);
int text_append_mem(text *to, const void *from, size_t from_len);
const char *text_get(const text *t);
int text_append_str(text *to, const char *from);
char *text_get_nonconst(text *t);
size_t text_getCapacity(const text *t);
bool text_equal(const text *t1, const text *t2);
#endif // TEXT_H_
// text.c file
//#include "text.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
struct text {
size_t capacity;
char *content;
};
text *text_new(const char content[])
{
text * const t = malloc(sizeof(*t));
if (t == NULL) goto MALLOC_ERR;
const struct text zero = {
.capacity = 0,
.content = NULL,
};
*t = zero;
if (content != NULL) {
const int ret = text_append_str(t, content);
if (ret) {
goto TEXT_APPEND_ERR;
}
}
return t;
TEXT_APPEND_ERR:
free(t);
MALLOC_ERR:
return NULL;
}
void text_free(text *t)
{
assert(t != NULL);
free(t->content);
free(t);
}
int text_resize(text *t, size_t newcapacity)
{
// printf("%s %d -> %d\n", __func__, t->capacity, newcapacity);
// we resize in chunks
const size_t chunksize = 24;
// clap the capacity into multiple of 24
newcapacity = (newcapacity + chunksize - 1) / chunksize * chunksize;
void * const tmp = realloc(t->content, newcapacity);
if (tmp == NULL) return -ENOMEM;
t->content = tmp;
t->capacity = newcapacity;
return 0;
}
int text_append_mem(text *to, const void *from, size_t from_len)
{
if (to == NULL || from == NULL) return -EINVAL;
if (from_len == 0) return 0;
const size_t oldcapacity = to->capacity == 0 ? 0 : strlen(to->content);
const size_t newcapacity = oldcapacity + from_len + 1;
int ret = text_resize(to, newcapacity);
if (ret) return ret;
memcpy(&to->content[newcapacity - from_len - 1], from, from_len);
to->content[newcapacity - 1] = '\0';
return 0;
}
int text_append_str(text *to, const char *from)
{
if (to == NULL || from == NULL) return -EINVAL;
return text_append_mem(to, from, strlen(from));
}
int text_append(text *to, const text *from)
{
if (to == NULL || from == NULL) return -EINVAL;
if (text_getCapacity(from) == 0) return 0;
return text_append_str(to, text_get(from));
}
const char *text_get(const text *t)
{
return t->content;
}
const size_t text_strlen(const text *t)
{
return t->capacity == 0 ? 0 : strlen(t->content);
}
size_t text_getCapacity(const text *t)
{
return t->capacity;
}
bool text_equal_str(const text *t, const char *str)
{
assert(t != NULL);
if (str == NULL && t->capacity == 0) return true;
const size_t strlength = strlen(str);
const size_t t_strlen = text_strlen(t);
if (t_strlen != strlength) return false;
if (memcmp(text_get(t), str, strlength) != 0) return false;
return true;
}
// main.c file
#include <stdio.h>
int text_testAppend(void) {
text *t = text_new("car");
if (t == NULL) return -1;
text *t2 = text_new("pet");
if (t2 == NULL) return -1;
if (text_append(t, t2)) return -1;
assert(text_equal_str(t, "carpet"));
assert(text_getCapacity(t) == 24);
text *t3 = text_new("789012345678901234");
if (t3 == NULL) return -1;
if (text_append(t, t3)) return -1;
assert(text_equal_str(t, "carpet789012345678901234"));
assert(text_getCapacity(t) == 48);
text_free(t);
text_free(t2);
text_free(t3);
return 0;
}
int main()
{
text *t1 = text_new("abc");
text_append_str(t1, "def");
printf("%s\n", text_get(t1));
text_free(t1);
printf("text_testAppend = %d\n", text_testAppend());
return 0;
}

Reading string from array of pointers

How can I read each individual character from a string that is accessed through an array of pointers? In the below code I currently have generated an array of pointers to strings called, symCodes, in my makeCodes function. I want to read the strings 8 characters at a time, I thought about concatenating each string together, then looping through that char by char but the strings in symCodes could be up to 255 characters each, so I feel like that could possibly be too much all to handle at once. Instead, I thought I could read each character from the strings, character by character.
I've tried scanf or just looping through and always end up with seg faults. At the end of headerEncode(), it's near the bottom. I malloc enough memory for each individual string, I try to loop through the array of pointers and print out each individual character but am ending up with a seg fault.
Any suggestions of a different way to read an array of pointers to strings, character by character, up to n amount of characters is appreciated.
EDIT 1: I've updated the program to no longer output warnings when using the -Wall and -W flags. I'm no longer getting a seg fault(yay!) but I'm still unsure of how to go about my question, how can I read an array of pointers to strings, character by character, up to n amount of characters?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "huffman.h"
#define FAIL 0
#define SUCCESS 1
/* global 1 day arrays that hold chars and their freqs from file */
unsigned long globalFreqs[256] = {0};
unsigned char globalUsedCh[256] = {0};
char globalCodes[256] = {0};
unsigned char globalUniqueSymbols;
unsigned long totalCount = 0;
typedef struct HuffmanTreeNode* HTNode;
struct HuffmanTreeNode* globalSortedLL;
/*
struct has the input letter, the letters frequency, and the left and irght childs
*/
struct HuffmanTreeNode
{
char symbol;
unsigned long freq;
char *code;
struct HuffmanTreeNode *left, *right;
struct HuffmanTreeNode* next;
};
/* does it make sense to have a struct for the entire huffman tree to see its size? */
struct HuffmanTree
{
unsigned size;
};
/*generate new node with given symbol and freq */
struct HuffmanTreeNode* newNode(char symbol, int freq)
{
struct HuffmanTreeNode* newNode = malloc(sizeof(struct HuffmanTreeNode));
newNode->symbol = symbol;
newNode->freq = freq;
newNode->left = newNode->right = NULL;
return newNode;
}
/*current work in progress, i believe this is the way to insert it for a BST
/* will change for HuffmanTreenode once working
/*
*/
struct HuffmanTreeNode* insert(struct HuffmanTreeNode* node, struct HuffmanTreeNode* htnNew)
{
struct HuffmanTreeNode* currentNode = node;
if(currentNode == NULL || compareTwoNodes(htnNew, currentNode))
{
htnNew->next = currentNode;
return htnNew;
}
else
{
while(currentNode->next != NULL && compareTwoNodes(currentNode->next, htnNew))
{
currentNode = currentNode->next;
}
htnNew->next = currentNode->next;
currentNode->next = htnNew;
return node;
}
}
int compareTwoNodes(struct HuffmanTreeNode* a, struct HuffmanTreeNode* b)
{
if(b->freq < a->freq)
{
return 0;
}
if(a->freq == b->freq)
{
if(a->symbol > b->symbol)
return 1;
return 0;
}
if(b->freq > a->freq)
return 1;
}
struct HuffmanTreeNode* popNode(struct HuffmanTreeNode** head)
{
struct HuffmanTreeNode* node = *head;
*head = (*head)->next;
return node;
}
/*convert output to bytes from bits*/
/*use binary fileio to output */
/*put c for individual character byte*/
/*fwrite each individual byte for frequency of symbol(look at fileio slides) */
/*
#function:
#param:
#return:
*/
int listLength(struct HuffmanTreeNode* node)
{
struct HuffmanTreeNode* current = node;
int length = 0;
while(current != NULL)
{
length++;
current = current->next;
}
return length;
}
/*
#function:
#param:
#return:
*/
void printList(struct HuffmanTreeNode* node)
{
struct HuffmanTreeNode* currentNode = node;
while(currentNode != NULL)
{
if(currentNode->symbol <= ' ' || currentNode->symbol > '~')
printf("=%d", currentNode->symbol);
else
printf("%c", currentNode->symbol);
printf("%lu ", currentNode->freq);
currentNode = currentNode->next;
}
printf("\n");
}
/*
#function:
#param:
#return:
*/
void buildSortedList()
{
int i;
for(i = 0; i < 256; i++)
{
if(!globalFreqs[i] == 0)
{
globalSortedLL = insert(globalSortedLL, newNode(i, globalFreqs[i]));
}
}
printf("Sorted freqs: ");
printList(globalSortedLL);
printf("listL: %d\n", listLength(globalSortedLL));
}
/*
#function: isLeaf()
will test to see if the current node is a leaf or not
#param:
#return
*/
int isLeaf(struct HuffmanTreeNode* node)
{
if((node->left == NULL) && (node->right == NULL))
return SUCCESS;
else
return FAIL;
}
/*where I plan to build the actual huffmantree */
/*
#function:
#param:
#return:
*/
struct HuffmanTreeNode* buildHuffmanTree(struct HuffmanTreeNode* node)
{
int top = 0;
struct HuffmanTreeNode *left, *right, *topNode, *huffmanTree;
struct HuffmanTreeNode* head = node;
struct HuffmanTreeNode *newChildNode, *firstNode, *secondNode;
while(head->next != NULL)
{
/*grab first two items from linkedL, and remove two items*/
firstNode = popNode(&head);
secondNode = popNode(&head);
/*combine sums, use higher symbol, create new node*/
newChildNode = newNode(secondNode->symbol, (firstNode->freq + secondNode->freq));
newChildNode->left = firstNode;
newChildNode->right = secondNode;
/*insert new node, decrement total symbols in use */
head = insert(head, newChildNode);
}
return head;
}
void printTable(char *codesArray[])
{
int i;
printf("Symbol\tFreq\tCode\n");
for(i = 0; i < 256; i++)
{
if(globalFreqs[i] != 0)
{
if(i <= ' ' || i > '~')
{
printf("=%d\t%lu\t%s\n", i, globalFreqs[i], codesArray[i]);
}
else
{
printf("%c\t%lu\t%s\n", i, globalFreqs[i], codesArray[i]);
}
}
}
printf("Total chars = %lu\n", totalCount);
}
void makeCodes(
struct HuffmanTreeNode *node, /* Pointer to some tree node */
char *code, /* The *current* code in progress */
char *symCodes[256], /* The array to hold the codes for all the symbols */
int depth) /* How deep in the tree we are (code length) */
{
char *copiedCode;
int i = 0;
if(isLeaf(node))
{
code[depth] = '\0';
symCodes[node->symbol] = code;
return;
}
copiedCode = malloc(255*sizeof(char));
memcpy(copiedCode, code, 255*sizeof(char));
code[depth] = '0';
copiedCode[depth] = '1';
makeCodes(node->left, code, symCodes, depth+1);
makeCodes(node->right, copiedCode, symCodes, depth+1);
}
/*
#function: getFileFreq()
gets the frequencies of each character in the given
file from the command line, this function will also
create two global 1d arrays, one for the currently
used characters in the file, and then one with those
characters frequencies, the two arrays will line up
parallel
#param: FILE* in, FILE* out,
the current file being processed
#return: void
*/
void getFileFreq(FILE* in, FILE* out)
{
unsigned long freqs[256] = {0};
int i, t, fileCh;
while((fileCh = fgetc(in)) != EOF)
{
freqs[fileCh]++;
totalCount++;
}
for(i = 0; i < 256; i++)
{
if(freqs[i] != 0)
{
globalUsedCh[i] = i;
globalFreqs[i] = freqs[i];
if(i <= ' ' || i > '~')
{
globalUniqueSymbols++;
}
else
{
globalUniqueSymbols++;
}
}
}
/* below code until total count is for debugging purposes */
printf("Used Ch: ");
for(t = 0; t < 256; t++)
{
if(globalUsedCh[t] != 0)
{
if(t <= ' ' || t > '~')
{
printf("%d ", globalUsedCh[t]);
}
else
printf("%c ", globalUsedCh[t]);
}
}
printf("\n");
printf("Freq Ch: ");
for(t = 0; t < 256; t++)
{
if(globalFreqs[t] != 0)
{
printf("%lu ", globalFreqs[t]);
}
}
printf("\n");
/* end of code for debugging/vizualazation of arrays*/
printf("Total Count %lu\n", totalCount);
printf("globalArrayLength: %d\n", globalUniqueSymbols);
}
void headerEncode(FILE* in, FILE* out, char *symCodes[256])
{
char c;
int i, ch, t, q, b, z;
char *a;
char *fileIn;
unsigned char *uniqueSymbols;
unsigned char *byteStream;
unsigned char *tooManySym = 0;
unsigned long totalEncodedSym;
*uniqueSymbols = globalUniqueSymbols;
totalEncodedSym = ftell(in);
rewind(in);
fileIn = malloc((totalEncodedSym+1)*sizeof(char));
fread(fileIn, totalEncodedSym, 1, in);
if(globalUniqueSymbols == 256)
{
fwrite(tooManySym, 1, sizeof(char), out);
}
else
{
fwrite(uniqueSymbols, 1, sizeof(uniqueSymbols)-7, out);
}
for(i = 0; i < 256; i++)
{
if(globalFreqs[i] != 0)
{
fwrite(globalUsedCh+i, 1, sizeof(char), out);
fwrite(globalFreqs+i, 8, sizeof(char), out);
}
}
for(t = 0; t < totalEncodedSym; t++)
{
fwrite(symCodes[fileIn[t]], 8, sizeof(char), out);
}
for(q = 0; q < totalEncodedSym; q++)
{
symCodes[q] = malloc(255*sizeof(char));
a = symCodes[q];
while(*a != '\0')
printf("%c\n", *(a++));
}
printf("Total encoded symbols: %lu\n", totalEncodedSym);
printf("%s\n", fileIn);
}
void encodeFile(FILE* in, FILE* out)
{
int top = 0;
int i;
char *code;
char *symCodes[256] = {0};
int depth = 0;
code = malloc(255*sizeof(char));
getFileFreq(in, out);
buildSortedList();
makeCodes(buildHuffmanTree(globalSortedLL), code, symCodes, depth);
printTable(symCodes);
headerEncode(in, out, symCodes);
free(code);
}
/*
void decodeFile(FILE* in, FILE* out)
{
}*/
There are many problems in your code:
[major] function compareTwoNodes does not always return a value. The compiler can detect such problems if instructed to output more warnings.
[major] the member symbol in the HuffmanTreeNode should have type int. Type char is problematic as an index value because it can be signed or unsigned depending on compiler configuration and platform specificities. You assume that char has values from 0 to 255, which is incorrect for most platforms where char actually has a range of -128 .. 127. Use unsigned char or int but cast the char values to unsigned char to ensure proper promotion.
[major] comparison if (globalUniqueSymbols == 256) is always false because globalUniqueSymbols is an unsigned char. The maximum number of possible byte values is indeed 256 for 8-bit bytes, but it does not fit in an unsigned char, make globalUniqueSymbols an int.
[major] *uniqueSymbols = globalUniqueSymbols; in function headerEncode stores globalUniqueSymbols into an uninitialized pointer, definitely undefined behavior, probable segmentation fault.
[major] sizeof(uniqueSymbols) is the size of a pointer, not the size of the array not the size of the type. Instead of hacking it as sizeof(uniqueSymbols)-7, fputc(globalUniqueSymbols, out);
[major] fwrite(tooManySym, 1, sizeof(char), out); is incorrect too, since tooManySym is initialized to 0, ie: it is a NULL pointer. You need a special value to tell that all bytes values are used in the source stream, use 0 for that and write it with fputc(0, out);.
You have nested C style comments before function insert, this is not a bug but error prone and considered bad style.
function newNode should take type unsigned long for freq for consistency.
function buildHuffmanTree has unused local variables: right, top and topNode.
variable i is unused in function makeCodes.
many unused variables in headerEncode: byteStream, c, ch, b...
totalEncodedSym is an unsigned long, use an index of the proper type in the loops where you stop at totalEncodedSym.
unused variables un encodeFile: i, top...
Most of these can be detected by the compiler with the proper warning level: gcc -Wall -W or clang -Weverything...
There are probably also errors in the program logic, but you cannot see these until you fix the major problems above.

Parsing a read in file and storing it in a binary tree

I am getting a segfault when I try to insert my node into the binary tree. I run the program with gdb and here is what I find out about the segfault, but I dont really know what to change in my insert and create function. Thanks for the help in advance.
Program received signal SIGSEGV, Segmentation fault.
__strcmp_sse42 () at ../sysdeps/x86_64/multiarch/strcmp.S:260
260 movdqu (%rsi), %xmm2
(gdb) where
#0 __strcmp_sse42 () at ../sysdeps/x86_64/multiarch/strcmp.S:260
#1 0x0000000000400a42 in insert_into_commands_tree (node=0x7fffffffe0b0,
data=0x602270) at lab8.c:116
#2 0x00000000004009d7 in create_commands_tree (commands=0x7fffffffe0b0,
file=0x7fffffffe4a1 "commands.dat") at lab8.c:104
#3 0x0000000000400835 in main (argc=2, argv=0x7fffffffe1b8) at lab8.c:48
What my program does is reads in a text file and parses the strings, then it stores them into a binary tree. Then the user types in a command and I search the tree and see if the command is listed in the tree. I am going to post my full code so you all can see it, then post the file I read in and the sample output, and hopefully someone can help me with this segfault error. Thanks a lot in advance. My teacher provided us with the main and tokenizer function too.
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#define COMMAND_NAME_LEN 50
#define MAX_SPLIT_SIZE 50
#define MAX_BUFF_SIZE 50
typedef struct Command_ {
char name[COMMAND_NAME_LEN];
int expected_param_count;
struct Command_ *left;
struct Command_ *right;
}Command;
typedef struct StringArray_ {
char **strings;
int size;
}StringArray;
StringArray* tokenizer (char *string, const char* delimiters);
void free_string_array(StringArray *sr);
void create_commands_tree(Command **commands, const char *file);
void insert_into_commands_tree(Command** node, char** data);
Command* get_command(Command *node, const char *command);
Command* create_command(char **data);
void destroy_commands_tree(Command* node);
void display_commands(Command *node);
int main (int argc, char *argv[]) {
if (argc < 2) {
printf("%s is missing commands.dat\n", argv[0]);
return 0;
}
Command* options = NULL;
create_commands_tree(&options,argv[1]);
int checking = 1;
char input_buffer[MAX_BUFF_SIZE];
do {
printf("Command: ");
fgets(input_buffer,MAX_BUFF_SIZE,stdin);
StringArray* parsed_input = tokenizer(input_buffer," \n");
Command* c = get_command(options,parsed_input->strings[0]);
if( c && parsed_input->size == c->expected_param_count) {
if (strcmp(c->name, "quit") == 0){
checking = 0;
}
printf("Valid command used\n");
}
else {
printf("Invalid command, please try again\n");
}
free_string_array(parsed_input);
}while (checking);
destroy_commands_tree(options);
}
void create_commands_tree(Command **commands, const char *file) {
FILE *input;
input = fopen(file, "r");
char strings[100];
StringArray *temp2;
while(fgets(strings,100,input)){
temp2 = tokenizer(strings, "\n");
insert_into_commands_tree(commands,temp2->strings);
}
}
void insert_into_commands_tree(Command** node, char** data) {
if(*node == NULL){
*node = create_command(data);
}
else if( *node != NULL){
if(strcmp(data[0],(*node)->name) < 0)
insert_into_commands_tree(&(*node)->left,data);
else if(strcmp(data[0], (*node)->name) > 0)
insert_into_commands_tree(&(*node)->right,data);
}
}
Command* create_command(char **data) {
Command* new_;
new_ = (Command*)malloc(sizeof(Command));
strncpy(new_->name, data[0], COMMAND_NAME_LEN);
new_->expected_param_count = atoi(data[1]);
new_->right = NULL;
new_->left = NULL;
return new_;
}
Command* get_command(Command *node, const char *command) {
Command *temp = node;
int compare;
if(temp){
compare = strcmp(node->name, command);
if(compare == 0){
return temp;
}
else if(compare < 0){
return (get_command(node->right, command));
}
else{
if(compare > 0){
return (get_command(node->left, command));
}}
}
return temp;
}
void destroy_commands_tree(Command* node) {
if( node == NULL){
return;
}
destroy_commands_tree(node->left);
destroy_commands_tree(node->right);
free(node);
}
void display_commands(Command *node) {
printf("\npickup <item>");
printf("\nhelp ");
printf("\nquit ");
printf("\nload <file>\n\n");
}
StringArray* tokenizer (char *string, const char* delimiters){
StringArray* sr = malloc(sizeof(StringArray));
sr->strings = malloc(MAX_SPLIT_SIZE * sizeof(char *));
size_t len;
char* hold;
(sr->strings)[0] = malloc(MAX_BUFF_SIZE * sizeof(char));
hold = strtok(string, delimiters);
int i;
for(i = 1; i < MAX_SPLIT_SIZE; i++){
hold = strtok(NULL, delimiters);
if(hold == NULL){
sr->size = i + 1;
break;
}
(sr->strings)[i] = malloc(MAX_BUFF_SIZE * sizeof(char));
strcpy((sr->strings)[i], hold);
}
return sr;
}
void free_string_array(StringArray *sr) {
int i;
for(i = 0; i < sr->size; ++i){
free(sr->strings[i]);
}
free(sr->strings);
free(sr);
}
Here is the sample output that was given:
]$ ./a.out commands.dat 
Command: pickup 
Invalid command, please try again 
Command: pickup ball 
Valid command used 
Command: quit 1 
Invalid command, please try again 
Command: load 
Invalid command, please try again 
Command: load bak.sav 
Valid command used 
Command: help
Valid command used
Command: help 2 
Invalid command, please try again 
Command: quit 
Valid command used 
And the file that we read in is as follows:
pickup,2
help,1
quit,1
load,2
There are several issues with this code:
In function insert_into_commands_tree
Like said in a comment above, the condition should be if (*node == NULL). This is probably what caused a segfault.
Furthermore, the if ... else if structure is a bit redundant here, since it is a true/false condition. You can simply use:
void insert_into_commands_tree(Command** node, char** data) {
if(*node == NULL) {
*node = create_command(data);
}
else {
if(strcmp(data[0],(*node)->name) < 0)
insert_into_commands_tree(&(*node)->left,data);
else if(strcmp(data[0], (*node)->name) > 0)
insert_into_commands_tree(&(*node)->right,data);
}
}
(but it's just a small detail)
In function create_commands_tree
What you do here is read a line of the input file with fgets, and separate this line with the toker "\n", which is useless (since fgets already separates the lines). I think you meant:
temp2 = tokenizer(strings, ",");
In function tokenizer
The output of this function is not what it should be. The main issue here is that you don't copy the first token into your StringArray. Below is a valid (and a bit simplified) tokenizer function:
StringArray* tokenizer (char *string, const char* delimiters)
{
StringArray* sr = malloc(sizeof(StringArray));
sr->strings = malloc(MAX_SPLIT_SIZE * sizeof(char *));
char* hold;
int i = 0;
hold = strtok(string, delimiters);
while ((hold != NULL) && (i < MAX_SPLIT_SIZE))
{
(sr->strings)[i] = malloc(MAX_BUFF_SIZE * sizeof(char));
strncpy((sr->strings)[i], hold, MAX_SPLIT_SIZE);
i++;
hold = strtok(NULL, delimiters);
}
sr->size = i;
return sr;
}
With these modifications, your code seems to do what you expect.

Resources