How can I free memory used by malloc() outside a function? - c

I am trying to free the memory allocated by my getSongInfo function, I have tried using a pointer to the function call but I get an error "cannt assign int to type int*" error. Any help would be great as the current way I have seems like it may work, but I might be completely wrong. Thanks!
Original Attempt:
int *memPtr = NULL
memPtr = getSongInfo(&fillPtr[arrayCounter], tempArtist[counter], tempSong[counter]);
Gives error!
Current Attempt:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#pragma warning(disable:4996)
int getSongInfo(struct songInfo *pFillInfo, char *artistName, char *songName);
void printSongInfo(struct songInfo songList[10]);
struct songInfo {
char *songArtist;
char *songTitle;
};
int main(void)
{
struct songInfo *fillPtr;
struct songInfo songList[10];
fillPtr = &songList[0];
char tempArtist[10][30];
char tempSong[10][30];
int *memPtr = NULL;
int i = 0;
int counter = 0;
int arrayCounter = 0;
while (counter != 10)
{
printf("Please enter the artist name: ");
fgets(tempArtist[counter], sizeof(tempArtist[counter]), stdin);
tempArtist[counter][strcspn(tempArtist[counter], "\n")] = 0;
printf("Please enter the song name: ");
fgets(tempSong[counter], sizeof(tempSong[counter]), stdin);
tempSong[counter][strcspn(tempSong[counter], "\n")] = 0;
getSongInfo(&fillPtr[arrayCounter], tempArtist[counter], tempSong[counter]);
printf("Song and Artist Captured! \n");
counter++;
arrayCounter++;
}
printSongInfo(fillPtr);
free(fillPtr->songArtist);
free(fillPtr->songTitle);
}
int getSongInfo(struct songInfo *pFillInfo, char *artistName, char *songName)
{
pFillInfo->songArtist = (char*)malloc(strlen(artistName) + 1);
pFillInfo->songTitle = (char*)malloc(strlen(songName) + 1);
strcpy(pFillInfo->songArtist, artistName);
strcpy(pFillInfo->songTitle, songName);
return 1;
}
void printSongInfo(struct songInfo songList[10])
{
int counter = 0;
while (counter != 10)
{
printf("%-35s %-35s\n", songList[counter].songArtist, songList[counter].songTitle);
counter++;
}
}

Your getSongInfo function does not return a pointer, so attempting to put the return value into a variable and then free it is pointless. The pointers in question are inside the struct songInfo, specifically, the fillPtr variable (which is actually redundant, since songList and fillPtr point to the same location).
In addition, please be aware that strcspn will not always return a valid index. If it does not find a match, it will return the length of the first argument.
I think this is more like what you are trying to do:
int main(void)
{
const int numSongs = 10;
struct songInfo songList[numSongs];
char tempArtist[30];
char tempSong[30];
int i;
int newline_idx;
for (i = 0; i < numSongs; ++i)
{
printf("Please enter the artist name: ");
fgets(tempArtist, sizeof(tempArtist), stdin);
newline_idx = strcspn(tempArtist, "\n");
if (newline_idx < sizeof(tempArtist))
tempArtist[newline_idx] = 0;
printf("Please enter the song name: ");
fgets(tempSong, sizeof(tempSong), stdin);
newline_idx = strcspn(tempSong, "\n");
if (newline_idx < sizeof(tempSong))
tempSong[newline_idx] = 0;
getSongInfo(&songList[i], tempArtist, tempSong);
printf("Song and Artist Captured! \n");
}
for (i = 0; i < numSongs; ++i)
{
free(songList[i].songArtist);
free(songList[i].songTitle);
}
}
You might consider separating the code for free()ing each struct into its own function.
You might also consider heeding that compiler warning instead of ignoring it, as Bodo commented. Careless handling of strings from stdin is dangerous.

Related

Memory allocation of an array of struct in a C function

I am trying to pass the pointer to a struct to a function to create an array of the struct there. The overall idea is:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct items
{
int number;
char *name;
char *description;
} ITEMS;
int process(ITEMS **items)
{
int i = 0, something = 200;
// for loop is for the representation.
// The actual data come from MySQL row loop
for (int j = 100; j < something; j++)
{
// Growing the array of struct
items = realloc(items, (i + 1) * sizeof(*items));
// Adding items here
items[i]->number = j;
strcpy(items[i]->name, "Some name"); // it comes from a variable
strcpy(items[i]->description, "Some text");
i++;
}
return i;
}
int main()
{
ITEMS *items;
int num_items = process(&items);
for (int i = 0; i < num_items; i++)
{
printf("%d - %s - %s\n", items[i].number, items[i].name,
items[i].description);
}
return 0;
}
but I am struggling with reallocating the memory for the struct (and with the pointers too).
The pointer to pointer must be de-referenced in the function so the allocation is visible in the calling function.
strdup is use to allocate memory to the pointers in the structure.
It is better to use a temporary variable for the reallocation.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct items
{
int number;
char *name;
char *description;
} ITEMS;
int process(ITEMS **items)
{
int i = 0;
// Growing the array of struct
*items = realloc(*items, (i + 1) * sizeof(**items));
// repeating this step in a loop of adding items
// Adding items here
(*items)[i].number = 72;
(*items)[i].name = strdup( "Some name"); // it comes from a variable
(*items)[i].description = strdup ("Some text");
i++;
*items = realloc(*items, (i + 1) * sizeof(**items));
// another item
(*items)[i].number = 88;
(*items)[i].name = strdup( "Some name"); // it comes from a variable
(*items)[i].description = strdup ("Some text");
i++;
return i;
}
int main()
{
ITEMS *items = NULL;
int num_items = process(&items);
for (int i = 0; i < num_items; i++)
{
printf("%d - %s - %s\n", items[i].number, items[i].name, items[i].description);
}
return 0;
}
With better error detection
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct items
{
int number;
char *name;
char *description;
} ITEMS;
int process(ITEMS **items)
{
ITEMS *temp = NULL;
int i = 0;
// Growing the array of struct
if ( NULL == ( temp = realloc(*items, (i + 1) * sizeof(**items)))) {
fprintf ( stderr, "realloc problem\n");
return i;
}
*items = temp;
// repeating this step in a loop of adding items
// Adding items here
(*items)[i].number = 72;
(*items)[i].name = strdup( "Some name"); // it comes from a variable
(*items)[i].description = strdup ("Some text");
i++;
if ( NULL == ( temp = realloc(*items, (i + 1) * sizeof(**items)))) {
fprintf ( stderr, "realloc problem\n");
return i;
}
*items = temp;
// another item
(*items)[i].number = 88;
(*items)[i].name = strdup( "Some name"); // it comes from a variable
(*items)[i].description = strdup ("Some text");
i++;
return i;
}
int main()
{
ITEMS *items = NULL;
int num_items = process(&items);
for (int i = 0; i < num_items; i++)
{
printf("%d - %s - %s\n", items[i].number, items[i].name, items[i].description);
}
return 0;
}

Why does printing from this struct give a segmentation fault?

I'm trying to create an array of Product structs and then print the name and code of each Product in the array, but I keep getting a segmentation fault. I have tried to insert each value without a loop and then printing, and it works, but I'd like to automate it. The function fill_products fills the products array according to the user's input, and the select_products prints each name-code pair for the entire array.
This is my code:
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int code;
char *name;
float price;
} Product;
void select_products(Product *products, int len)
{
int i;
printf("%-30s%s\n", "Name", "Code");
for (i = 0; i < len; i++)
{
printf("%-30s%d\n", products[i].name, products[i].code);
}
return;
}
void fill_products(Product *products, int len)
{
int i, code;
char *name;
float price;
for (i = 0; i < len; i++)
{
printf("Insert product name (%d / %d): ", i + 1, len);
scanf("%s", &name);
printf("Insert product price (%d / %d): ", i + 1, len);
scanf("%f", &price);
products[i].code = i;
products[i].name = name;
products[i].price = price;
}
return;
}
int is_alloc(Product *products)
{
if (products == NULL)
{
printf("Error: memory allocation unsuccessful.\n");
}
return products != NULL;
}
int main(void)
{
int len, n_bytes;
Product *products;
printf("Insert length of array: ");
scanf("%d", &len);
n_bytes = sizeof *products * len;
products = malloc(n_bytes);
if(!is_alloc(products))
{
exit(0);
}
fill_products(products, len);
select_products(products, len);
free(products);
return 0;
}
I keep getting a segmentation fault.
Please enable compiler warnings, and pay attention to them.
This code:
char *name;
...
scanf("%s", &name);
is bogus and doesn't do at all what you intend.
You must either allocate space for name separately (and then not forget to free() it), or make that space available in the Product structure like so:
typedef struct
{
int code;
char name[100];
float price;
} Product;
(this assumes there is a reasonable limit on name length).

comparing two string using a pointer and strcmp

i'm trying to solve this problem but it's not working i think the statement inside 'if' is wrong and i don't know if i can put a pointer inside strcmp like this!!
#include <string.h>
#include <studio.h>
struct PersonCar {
char pname[20];
char cModel[20];
float price;
};
struct PersonCar pc[4];
float calculatePrice(struct PersonCar *p, char *name) {
p = malloc(sizeof(pc));
float s = 0;
for (int i = 0; i < 4; i++) {
if ((strcmp((p[i].pname), name)) == 0) //(p+i)->pname
s += (p + i)->price; //(p+i)->price; }
return s;
}
int main() {
// entering the element of the array from the user
char A[20];
printf("Enter a name : ");
fgets(A, sizeof(A), stdin);
printf("the total price of the registered cars for %s =%f\n", A,
calculatePrice(&pc, A));
}
First you need some data (I have included some statically initialized data in my version)
Second, you need to eliminate the malloc() statement at the beginning of the function, that modifies the passed pointer to the data and makes it to point to an uninitialized data, that is very unprobable that finds any register that matches.
You h ad better to know the size of the data array, and pass the number of entries on it.
You need to eliminate the last '\n' from the array read by fgets(). If you don't, it is comparing "smith" against "smith\n", which will never be equal. I suggest one way below, but you have to be careful and read the man page of strtok, as it modifies the source string, this can be not what you want (while in this case is preciselly what we want)
To illustrate, I have written this sample code (but a full program you can compile and execute) to see the logic.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct PersonCar {
char *pname;
char *cModel;
float price;
};
/* initialized data */
struct PersonCar pc[] = {
{ .pname = "smith",
.cModel = "foo",
.price = 10.50F,
}, {
.pname = "montgomery",
.cModel = "bar",
.price = 20.50F,
}, {
.pname = "mckormack",
.cModel = "blah",
.price = 35.50F,
}, {
.pname = "smith",
.cModel = "pong",
.price = 55.50F,
}, {
.pname = "phelps",
.cModel = "ping",
.price = 75.50F,
},
};
size_t pc_count = sizeof pc / sizeof pc[0];
float calculatePrice(
struct PersonCar *p,
size_t p_count,
char *name)
{
float total = 0.0;
for (int i = 0; i < p_count; i++) {
if (strcmp(p[i].pname, name) == 0)
total += p[i].price; /* found */
}
/* not found */
return total;
}
int main()
{
// entering the element of the array from the user
char A[80];
printf("Enter a name : ");
fgets(A, sizeof(A), stdin);
char *p = strtok(A, "\n"); /* chop the last \n if present */
printf("the total price of the registered cars for %s =%f\n",
p, calculatePrice(pc, pc_count, p));
}
I think you're looking for something like:
#include <string.h>
#include <stdio.h>
struct PersonCar {
char pname[20];
char cModel[20];
float price;
};
struct PersonCar pc[4] = {
{"abc", "", 1.0},
{"def", "", 2.0},
{"abc", "", 3.0},
{"jkl", "", 4.0},
};
float
calculatePrice(struct PersonCar *p, char *name)
{
float s = 0;
for( struct PersonCar *e = p + 4; p < e; p++ ){
if( strcmp((p->pname), name) == 0 ){
s += p->price;
}
}
return s;
}
int
main(void)
{
char A[20];
printf("Enter a name : ");
fgets(A, sizeof(A), stdin);
printf("the total price of the registered cars for %s =%f\n", A,
calculatePrice(pc, A));
}
One glaring issue here is that you're not dealing with the newline in the input, but since I don't know how you're actually initializing the data it's not clear how you want to deal with it.

Double pointer with array in another function

I have to create a program that has an array of costumers (structs that contain name, code and documentation) and functions to insert, remove and list all of them in order of code. I'm not understanding what I should do. Please note that the parameters for insertCostumer, removeCostumer and listCostumer cannot be changed.
Piece of code 01:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#define MAX_REG 10
typedef struct _costumer {
int code;
char name[50];
char documentation[20];
} costumer;
Piece of code 02:
int main(int argc, char** argv) {
costumer *costumers[MAX_REG];
costumer **p_costumer;
p_costumer = &costumers[0];
int count = 0;
memset(costumers, 0, sizeof(costumers));
//Some code to check what to do using a switch
case '1': insertCostumer(p_costumer, &count); getch(); break;
case '2': removeCostumer(p_costumer, &count); getch(); break;
case '3': listCostumers(p_costumer, &count); getch(); break;
//Some code
return (EXIT_SUCCESS);
}
Piece of code 03:
void insertCostumer(costumer **p_costumer, int *count){
char aux[50];
char aux2[20];
if(*count < MAX_REG) {
*p_costumer = (costumer *) malloc(sizeof(costumer));
printf("\nInsert the code: ");
gets(aux);
(*p_costumer)->code = atoi(aux);
printf("Insert the name: ");
gets(aux);
strcpy((*p_costumer)->name, aux);
printf("Insert the documentation: ");
gets(aux2);
strcpy((*p_costumer)->documentation, aux2);
(*count)++;
p_costumer = &*p_costumer[*count];
} else {
printf("List full! Remove a costumer first!\n");
}
}
void removeCostumer(costumer **p_costumer, int *count){
char aux3[50];
int cod;
printf("\nInsert the code of the costumer to be removed: ");
gets(aux3);
cod = atoi(aux3);
for(int i = 0; i < *count; i++) {
if(p_costumer[i]->code == cod) {
strcpy(p_costumer[i]->name, NULL);
p_costumer[i]->code = 0;
strcpy(p_costumer[i]->documentation, NULL);
}
}
}
void listCostumers(costumer **p_costumer, int *count){
for(int i = 0; i < *count; i++) {
printf("Code: %d | Name: %s | Documentation: %s\n", p_costumer[i]->code, p_costumer[i]->name, p_costumer[i]->documentation);
}
}
I don't know what I'm doing wrong; nothing is working, honestly. I was trying to first insert, list and remove to try and make the sorting part later, but I can't even get this part done. When I list, only the last costumer added is listed, for example.
Can someone help me?
Okay, I had to refactor a considerable amount of your code, so I don't have a blow by blow description of the changes.
You'll just have to study it a bit.
Note that even if you're passed a double pointer as an argument, doesn't mean you have to use it as a double in the body of the functions. Note, in particular, what I did for the count (e.g. int count = *p_count; and *p_count = count;)
But, it should be noted that the list is one of pointers to structs and not merely a pointer to an array of structs (i.e. there is an extra level of indirection). This makes things a bit faster.
Note that, bug fixes aside, the key is the "slide" operation in the remove function.
Because we're "sliding" pointers, this is faster/more efficient with the pointer array. Study this [concept] well.
Never use gets--always use fgets
I've deliberately left off comments. This will allow you to add them as you analyze the code. I've found that this can be a powerful technique for understanding a [foreign] code base.
Anyway, here's the code. I've done some rudimentary testing and it seems to work:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <conio.h>
#define MAX_REG 10
char aux[1000];
typedef struct _costumer {
int code;
char name[50];
char documentation[20];
} costumer;
void
lineget(char *buf,size_t buflen)
{
char *cp;
cp = fgets(buf,buflen,stdin);
if (cp != NULL) {
cp = strrchr(buf,'\n');
if (cp != NULL)
*cp = 0;
}
}
void
insertCostumer(costumer **p_costumer, int *p_count)
{
costumer *add;
int count = *p_count;
char aux2[20];
if (count < MAX_REG) {
add = malloc(sizeof(costumer));
printf("\nInsert the code: ");
lineget(aux,sizeof(aux));
add->code = atoi(aux);
printf("Insert the name: ");
lineget(add->name,sizeof(add->name));
printf("Insert the documentation: ");
lineget(add->documentation,sizeof(add->documentation));
p_costumer[count] = add;
++count;
}
else {
printf("List full! Remove a costumer first!\n");
}
*p_count = count;
}
void
removeCostumer(costumer **p_costumer, int *p_count)
{
int count = *p_count;
int cod;
int i;
costumer *cur;
printf("\nInsert the code of the costumer to be removed: ");
fgets(aux,sizeof(aux),stdin);
cod = atoi(aux);
int slide = 0;
for (i = 0; i < count; i++) {
cur = p_costumer[i];
if (cur->code == cod) {
slide = 1;
break;
}
}
if (slide) {
free(cur);
--count;
for (; i < count; ++i)
p_costumer[i] = p_costumer[i + 1];
p_costumer[count] = NULL;
}
*p_count = count;
}
void
listCostumers(costumer **p_costumer, int *p_count)
{
costumer *cur;
int count = *p_count;
for (int i = 0; i < count; ++i, ++cur) {
cur = p_costumer[i];
printf("Code: %d | Name: %s | Documentation: %s\n",
cur->code, cur->name, cur->documentation);
}
}
int
main(int argc, char **argv)
{
costumer *costumers[MAX_REG];
costumer **p_costumer;
char buf[100];
p_costumer = &costumers[0];
int count = 0;
memset(costumers, 0, sizeof(costumers));
setbuf(stdout,NULL);
//Some code to check what to do using a switch
while (1) {
printf("operation to perform (1=insert, 2=remove, 3=print): ");
char *cp = fgets(buf,sizeof(buf),stdin);
if (cp == NULL)
break;
switch (cp[0]) {
case '1':
insertCostumer(p_costumer, &count);
break;
case '2':
removeCostumer(p_costumer, &count);
break;
case '3':
listCostumers(p_costumer, &count);
break;
}
}
return (EXIT_SUCCESS);
}

How to print string into an array after used the isdigit?

I'm very new to isdigit and the string. I really need help from you guys. I want to print string into an array 'food[f]'.But can you guys help me to check where is my problem ? Here is my code.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main()
{
char foo[20];
float price;
int number=1;
int f=0,i=0;
char *food[f];
adding_food :
food[f] = (char*)malloc(25);
printf("Adding food into Menu (0 to Main Menu): ");
scanf("%s", foo);
{
if(isdigit(foo[0])== 0)
{
foo[i] = *food[f]; //something wrong here
printf("Enter price (RM) : ");
scanf("%f",&price);
printf("\n%-16d%-19s%6.2f\n\n",number,foo,price);
printf("\n%-16d%-19s%6.2f\n\n",number,food[f],price);
number++;
i++;
f++;
goto adding_food;
}
else
return 0;
}
}
I would like my output be like this
Adding food into Menu (0 to Main Menu) : Cake
Enter price (RM) : 10
1 Cake 10 //foo[0]
1 Cake 10 //food[0]
There are several mistakes in your code. Example:
char *food[f];
is similar to
char *food[0];
as f is zero. That makes no sense.
Also the use of goto is not considered good style.
So let me show you another approach. Something like:
// Make a type that can hold both a name and a price
struct item
{
char name[25];
float price;
};
#define MAX_ITEMS 100
struct item* addItems(int* n)
{
*n = 0;
struct item* items = malloc(MAX_ITEMS * sizeof *items);
if (items == NULL) return NULL;
while (*n != MAX_ITEMS)
{
scanf("%24s", items[*n].name);
if (items[*n].name[0] == '0') break;
scanf("%f", &items[*n].price);
*n += 1;
}
return items;
}
void print_all(struct item* my_items, int num_items)
{
for (int i = 0; i < num_items; ++i)
printf("%s %f\n", my_items[i].name, my_items[i].price);
}
int main()
{
int num_items;
struct item* my_items = addItems(&num_items);
print_all(my_items, num_items);
free(my_items);
}

Resources