Dynamically expanding C code without using realloc - c

I need to put the names separated by commas from the text into struct that expands dynamically, but I am prohibited from using realloc ().I'm getting a core dumped error in this code. What is the error in this code?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct movie{
double budget;
int genre;
char* name;
double score;
int year;
};
void recorder_function(struct movie *movies){
FILE*fp;
fp=fopen("Movies.txt","r");
struct movie *p;
int i,n=0;
char line[1000];
char temp_budget[50];
char temp_name[50];
char temp_genre[50];
while (!feof(fp)) {
fgets(line,1000,fp);
sscanf(line,"%50[^,],%50[^,],%50[^,]",temp_budget,temp_genre,temp_name);
//I open the fields in this section
movies=(struct movie *)calloc(n+1,sizeof(struct movie));
p=(struct movie *)calloc(n+1,sizeof(struct movie));
p[n].name=(char*)malloc(sizeof(char)*(strlen(temp_name)+1));
movies[n].name=(char*)malloc(sizeof(char)*(strlen(temp_name)+1));
for(i=0;i<n;i++)
movies[i]=p[i];
strcpy(movies[n].name,temp_name);
free(p);
p=movies;
n++;
}
for(i=0;i<n;i++)
printf("%s\n",movies[i].name);
}
int main(){
int choice;
struct movie *movies;
recorder_function(movies);
}

It is a bad idea to overwrite the pointer movie by newly allocated clean buffer.
Instead of that, you should
Allocate new buffer only for p.
Put the new element to p[n]
Put existing elements movie[0], ... , movie[n-1] to p[0], ... , p[n-1]
Free the old buffer movie
Assign the new buffer p to movie
Don't forget to initialize movie not to cause troubles at the first freeing.
Also while (!feof(fp)) is wrong and you should check if readings are successful after trying to read and before using what are read.
One more important point is that you should make sure that fopen() succeeded. Passing NULL, which fopen() returns on failure`, to other file manipulation functions may cause troubles.
Another point is that your arrays used for sscanf() outputs should have one more elements for the terminating null-character.
Yet another point is that casting results of malloc() family is considered as a bad practice.
Try this:
void recorder_function(struct movie *movies){
FILE*fp;
fp=fopen("Movies.txt","r");
if(fp==NULL){
fputs("file open error\n", stderr);
return;
}
struct movie *p;
int i,n=0;
char line[1000];
char temp_budget[51];
char temp_name[51];
char temp_genre[51];
movies=NULL;
while (fgets(line,1000,fp)) {
sscanf(line,"%50[^,],%50[^,],%50[^,]",temp_budget,temp_genre,temp_name);
//I open the fields in this section
p=calloc(n+1,sizeof(struct movie));
p[n].name=malloc(sizeof(char)*(strlen(temp_name)+1));
strcpy(p[n].name,temp_name);
for(i=0;i<n;i++){
p[i]=movies[i];
}
free(movies);
movies=p;
n++;
}
for(i=0;i<n;i++){
printf("%s\n",movies[i].name);
}
}
The next stage will be fixing the weird usage of the argument movie. Arguments in C are copies of what are passed, and modifications of arguments in callee functions won't affect what are passed in caller. Your choice is:
Remove the argument movies and convert that to a local variable.
Have recorder_function take a pointer to struct movie* (struct movie**) and have it use the pointer to modify what caller specifies. (You also have to change the statement to call the function to pass a pointer in this case)

Related

Passing string to struct in C [duplicate]

This question already has answers here:
Crash or "segmentation fault" when data is copied/scanned/read to an uninitialized pointer
(5 answers)
Closed 4 years ago.
I am complete beginner in C and need help with saving strings in structs.
(I tried multiple ways to have it done but program prints nothing or i got (0xC0000005) or program shuts down.. (I tried using pointer as a parameter but effect is the same-or using scanf straightly for text.line and etc ). I would be glad if somebody explained me simply how it should be done - I got feeling that i still dont fully understand idea of pointers and thats the problem ;q.
typedef struct label{
char *line;
}label;
void save_line(label text){
printf("Write your name\n");
char *helper=malloc(30 * sizeof *helper);
scanf("%s", helper);
strcpy(text.line, helper);
}
void main(){
label text;
save_line(text);
printf("%s", text.line);
}
When you pass a parameter to a function in C it creates a copy of it, so the text variable you have in main is not the same as the one you have in save_line.
You need to pass a pointer the text to save_line function, like so:
void save_line(label *text) {
printf("Write your name\n");
char *helper=malloc(30 * sizeof *helper);
scanf("%s", helper);
text->line = helper;
}
And in main:
void main(){
label text;
save_line(&text);
printf("%s", text.line);
}
There is no space to allocate your string in line, because it is just a no initialized pointer, so you will have to reserve some memory for it. Furthermore, your function is allocating memory which is not being deallocated (and regarding helper, you wouldn't need two different bunchs of memory for the same string).
#include<stdlib.h>
#include<stdio.h>
const uint8_t MAX_NAME_SIZE = 20;
typedef struct label{
char *line;
}label;
void save_line(label text){
printf("Write your name (max len %u chrs):\n",(MAX_NAME_SIZE-1));
scanf("%s", text.line);
}
void main(){
label text;
text.line = malloc(MAX_NAME_SIZE);
save_line(text);
printf("%s", text.line);
free(text.line);
}
I prefer to allocate and deallocate memory in the same scope, to avoid memory leaks (inside the same function, inside the main, etc), so in order to keep your structure, for the example the memory is being managed in the main. You should think about your design, and analyse the scope that label text; should have.
Your code is wrong at many levels:
You probably want something like this:
#include <stdio.h>
#include <stdlib.h>
typedef struct label {
char *line;
}label;
void save_line(label *text) { // use label *ttext instead of label text
printf("Write your name\n");
text->line = malloc(30 * sizeof *text->line); // no need for strcpy here anyway
scanf("%s", text->line);
}
int main() { // main should return int *
label text;
save_line(&text); // pass the pointer to text, not just text
printf("%s", text.line);
free(text.line); // free allocated memory
}
There is still room for improvement though, e.g. scanf("%s"), ... is dangerous because if the user types too many characters, you'll get a buffer overflow.
*read this: What should main() return in C and C++?
strcpy only copies the thing pointed to by the source to the destination. It does not allocate space in the destination for the stuff being copied.
If your platform has strdup you can use that instead:
text.line = strdup(helper);
If not, allocate some space in text.line before you strcpy
text.line = malloc(strlen(helper) + 1); // +1 for the null byte at the end
strcpy(text.line, helper);

Repeating strings but not ints when reading from files

I've written a function that uses fscanf("%s &d &d &d") to read from a file. For some reason, when I display the output, all the integer values are correct, but the strings are all the same. food_stuff is a struct held in an array.
*EDIT
I've tried using a for to add each element from one array to the other, and I've tried strcpy.
Here's the function.
int readFromFile()
{
char*name;
int type;
int cals;
int price;
FILE * food_file;
food_file = fopen ("food_file.txt", "r");
while(fscanf(food_file, "%s %d %d %d", name, &type, &cals, &price)!=EOF)
{
food_stuff fs;
fs.name = name;
fs.type = type;
fs.calories = cals;
fs.price = price;
fflush(stdin);
addItem(fs);
}
}
As a commenter already has pointed out, you need to allocate memory for your variable name. Because the variable is temporary, you could just declare a local char array on the stack instead of allocationg memory on the heap:
char name[40];
Your problem is that the name field in your structure is probably also only a pointer to char. That pointer points to name for all footstuff structs you generate, which is the same for all instances, only with different contents. (What's worse: That buffer won't exist any more if you leave readFromFile, invalidating the name field for every foodstuff.)
One possible solution is to make the name field also a buffer, e.g:
typedef struct food_stuff food_stuff;
struct food_stuff {
char name[40];
int type;
int cals;
int price;
};
When you assign the read data to the struct, don't copy the pointer, but copy the contents with strcpy. (You need to include <string.h> for that):
strcpy(fs.name, name);
This solution assumes that all names are less than 39 characters long, which is not a safe assumption for real data. You should specify a width on the %s format in your call to fscanf.
Another solution is leave the struct definition as I assume it is now with char *name and to allocate memory for each new struct. You still have to copy the contents, though:
fs.name = malloc(strlen(name) + 1);
// error check omitted
strcpy(fs.name, name);
This solution requires that you free the extra memory allocated when you free the foodstuff structs.

there is a runtime error in this file handling code

This is a simple code to store a person's name and number in a file.the problem occurs when i also want to include the person's contact number.the error occurs after the contact number is scanned.
#include <stdio.h>
#include <stdlib.h>
main()
{
FILE *fp;//file pointer
char *name,*number;
char filename[]="testfile.txt";//file to be created
fp=fopen(filename,"w");
if(fp==NULL)
{
printf("\nerror\n");
exit(1);
}
fprintf(stdout,"Please enter a name:\t");
fscanf(stdin,"%s",name);
fprintf(fp,"%s",name);
fprintf(stdout,"Enter contact number:\t");
fscanf(stdin,"%s",number);
fprintf(fp,"%s",number);
fclose(fp);
}
you did not malloc() the memory for name and number!
Here is an extract of your code:
char *name;
fscanf(stdin,"%s",name);
name is a pointer to a char (or the first char of a string) but you didn't initialized it's value so it points anywhere.
The second line with fscanf read a word and write it in memory at the address pointed by name. So, basically, fscanf will try to write somewhere it will probably won't be able to write.
There are 2 solutions:
either you change char *name to char name[MAXNAME] (where MAXNAME is a constant value)
either you do a malloc: char *name = malloc(MAXNAME)
The same for number.
Allocate memory for pointers name and number
name = malloc(sizeof(char) * num_elements);
number = malloc(sizeof(char) * num_elements);

typedef memory allocation

VARIABLES AREN'T SET IN STONE YET! Excuse if if no indention. I am new to this site. Anyway, I have a text document of a list of games in five different categories, and I need to some help with memory allocation VIA typedef. How would one do it? So far, this is what I have:
/*
Example of text document
2012 DotA PC 0.00 10
2011 Gran Turismo 5 PS3 60.00 12
list continues in similar fashion...
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//function prototype here
char **readFile(char *file);
char *allocateString(char temp[]);
typedef struct
{
int year;
char name[100];
char system[10];
float price;
int players;
}game;
int main(void)
{
char **list;
system("pause");
return 0;
}
//function defined here
char **readFile(char *file) //reads file and and allocates
{
FILE* fpIn;
int i, total=0;
fpIn = fopen("list.txt", "r");
if (!fpIn)
{
printf("File does not exist");
exit(101);
}
/*
allocate memory by row here VIA for loop with the total++ to keep track of the
number of games
*/
/*
allocate memory individually for each item VIA "allocateString by using going
to set list[i] = allocateStrng(tmpList) using for loop the for loop will have
for (i=0; i<total; i++)
*/
return;
}
//allocateString here
char *allocateString(char temp[]);
{
char *s;
s = (char*)calloc(strlen(temp+1), sizeof(char)));
strcpy(s, temp);
return s;
}
Usually you'd allocate a decent amount of memory up front, detect situations where that amount is not enough, and enlarge the allocation in those cases using realloc (or malloc followed by memcpy and free). This advice holds for both the buffer into which you read the current line (to be passed as temp to allocateString) and the array to hold the sequence of all lines.
You can detect an insufficient buffer size for the line buffer when after calling fgets(buf, bufsize, fpIn) the strlen(buf) == bufsize - 1 but still buf[bufsize - 2] != '\n'. In other words, when reading filled the whole buffer, but still didn't reach a newline. In that case, the next read will continue the current line. You might want an inner loop to extend the buffer and read again for as long as it takes.
Note that your allocateString pretty much duplicates strdup, so you might want to use that instead.
The links in the above text mainly come from the manual of the GNU C library. cppreference.com is another good source of C function documentation. As are the Linux man pages.
s = (char*)calloc(strlen(temp+1), sizeof(char)));
//the name of the array is a pointer, so you are doing pointer arithmetic.
//I think you want strlen(*temp+1, sizeof(char)));
// or strlen(temmp[1]) it isn't clear if this is a pointer to a string or an array
// of strings
//you need the length of the string *temp is the content which temp points to
//strcpy(s, temp);

Copy a string into a char array

Hello i want to copy the input of a user into an array of char which is defined in a struct. Sincerly i have no idea how to do this.
#include <stdio.h>
#include <stdlib.h>
int main ()
{
struct employee
{
char firstname[11];
char lastname[11];
char number[11];
int salary;
}
int i;
int nemps;
const int maxemps = 5;
struct employee* emps[maxemps];
printf("How many employees do you want to store?(max:5): ");
scanf("%d", nemps);
for (i = 0; i < nemps; i++)
{
printf("Employee 1./nLast name:");
scanf("....")// Read the string and copy it into char lastname[]
}
}
First off struct employee* emps[maxemps]; creates an array of pointers to struct employee with array size maxemps. You haven't actually set aside any space in memory for the actual structs, just the pointers that will point to them. To dynamically allocate space on the heap for your structs so you can use them in a meaningful way, you'll need to loop over a call to malloc() like so:
for (i = 0; i < maxemps; i++) {
emps[i] = malloc(sizeof(struct employee));
}
You'll also want a similar loop at the end of your program which will free() each pointer.
Next, when you are getting input from a user you really want to be using fgets() over scanf() because fgets() allows you to specify the number of characters to read in so you can prevent overflows on your destination buffer.
Update
If you want to work with a single struct employee without the use of pointers this is accomplished by declaring one or more struct employee on the stack and then using the . member access operator as follows:
struct employee emp;
fgets(emp.lastname, sizeof(emp.lastname), stdin);
Update2
I found a number of errors in your code. Please see this link for a working sample with comments.
You only need:
scanf("%10s", (emps[i])->last_name);
Here "%10s" denotes a string with a maximum length of 10, and it will load the string to last_name.
In C the string is represented as a char array, with '\0' as the end.
Using scanf here is vulnerable to buffer attacks if the user-input is longer than 10: http://en.wikipedia.org/wiki/Scanf#Security, so you need to assign a maximum length to the format.

Resources