C program dynamic memory allocation error, string value issue - c

I have tried multiple methods for a long time but it didn't work, the output automatically updates all previously entered values for the name.
The find method is not required to be checked, but the first display itself provides invalid output:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int phone;
char *name;
} phonecontact;
phonecontact *create(phonecontact *p, int phone, char name[], int i) {
(p+i)->name = (char *)calloc(10, sizeof(char));
(p+i)->name = name;
(p+i)->phone = phone;
printf("\n%s\n", (p+i)->name);
return p;
}
void display(phonecontact *p, int i) {
printf("Name : %s , Phone number : %d\n", p[i].name, p[i].phone);
}
int find(phonecontact *p, char name[], int *num) {
int phone, i = 0;
for (i = 0; i < *num; i++) {
if (strcmp(p[i].name, name) == 1)
display(p, i);
return 1;
}
p = (phonecontact *)realloc(p, ++(*num) * sizeof(phonecontact));
printf("Enter phone number for entered person : ");
scanf("%d", &phone);
p = create(p, phone, name, i + 1);
return 0;
}
int main() {
int num, phone;
char name[30];
printf("Enter number of contacts : ");
scanf("%d", &num);
phonecontact *p;
p = (phonecontact *)calloc(num, sizeof(phonecontact));
for (int i = 0; i < num; i++) {
printf("Enter name : ");
scanf("%s", name);
printf("Enter phone number : ");
scanf("%d", &phone);
p = create(p, phone, name, i);
}
printf("\n%s\n", (p)->name);
printf("\n%s\n", (p + 1)->name);
printf("\n%s\n", (p + 2)->name);
for (int i = 0; i < num; i++) {
display(p, i);
}
printf("Enter name of contact : ");
scanf("%s", &name);
find(p,name, &num);
}

There are many problems in the code:
For each contact to have its own name string, you must allocate a copy of the create() string argument:
phonecontact *create(phonecontact *p, int phone, const char *name, int i) {
p[i].name = strdup(name);
p[i].phone = phone;
printf("\n%s\n", p[i].name);
return p;
}
the function strcmp() returns 0 when the strings compare equal, a negative value if the first argument compares less than the second in lexicographical order and a value greater than zero otherwise.
You should use if (strcmp(p[i].name, name) == 0) and return 1 only if the strings compare equal.
It is confusing for a function named find() to append a new entry in the array.
The array reallocated by find() is not passed back to the caller, and the previous pointer may have become invalid, causing undefined behavior.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int phone;
char *name;
} phonecontact;
int flush_input(void) {
int c;
while ((c = getchar()) != EOF && c != '\n')
continue;
return c;
}
void init_contact(phonecontact *p, int phone, const char *name) {
p->name = strdup(name);
p->phone = phone;
}
void display_contact(const phonecontact *p) {
printf("Name: %s, Phone number: %d\n", p->name, p->phone);
}
int add_contact(phonecontact **pp, int *pnum, const char *name) {
int phone;
int num = *pnum;
phonecontact *p = *pp;
for (int i = 0; i < num; i++) {
if (strcmp(p[i].name, name) == 1) {
display_contact(&p[i]);
return 1;
}
}
/* reallocate the contact array */
p = (phonecontact *)realloc(p, (num + 1) * sizeof(*p));
if (p == NULL) {
printf("cannot reallocate contact array\n");
return -1;
}
printf("Enter phone number for entered person: ");
while (scanf("%d", &phone) != 1) {
printf("Invalid input. Try again: ");
if (flush_input() == EOF)
return -1;
}
init_contact(&p[num], phone, name);
*pnum = num + 1;
*pp = p;
return 0;
}
int main() {
int num, phone;
char name[30];
printf("Enter number of contacts : ");
if (scanf("%d", &num) != 1 || num < 0) {
fprintf(stderr, "invalid input\n");
return 1;
}
phonecontact *p = calloc(num, sizeof(*p));
if (p == NULL) {
fprintf(stderr, "memory allocation error\n");
return 1;
}
for (int i = 0; i < num; i++) {
printf("Enter name: ");
if (scanf(" %29[^\n]", name) != 1) {
fprintf(stderr, "invalid input\n");
return 1;
}
printf("Enter phone number: ");
if (scanf("%d", &phone) != 1)
fprintf(stderr, "invalid input\n");
return 1;
}
init_contact(&p[i], phone, name);
}
for (int i = 0; i < num; i++) {
display_contact(&p[i]);
}
printf("Enter name of contact: ");
if (scanf(" %29[^\n]", &name) == 1)
add_contact(&p, *num, name);
return 0;
}

Related

Sort struct by int value

I got an assignment to make program that asks for a number of teams, their names, wins and losses. I have got two problems with the code.
How do I get the team names with spaces in them correctly?
And how to sort the teams by total value (total=wins-losses) before printing?
#include <stdio.h>
#include <string.h>
#define NAME_LENGTH 25
typedef struct {
char name[NAME_LENGTH];
int wins;
int losses;
int total;
} Total;
Total readTeamInfo(void);
void printInfo(Total tt, int);
int main(void) {
int i, teams;
printf("Number of teams > ");
scanf("%d", &teams);
Total data[teams];
for (i = 0; i < teams; i++) {
data[i] = readTeamInfo();
data[i].total = data[i].wins - data[i].losses;
}
for (i = 0; i < teams; i++) {
printInfo(data[i], i);
}
return (0);
}
Total readTeamInfo(void) {
Total tt;
printf("Team name > ");
scanf("%s", tt.name);
printf("Wins > ");
scanf("%d", &tt.wins);
printf("Losses > ");
scanf("%d", &tt.losses);
return (tt);
}
void printInfo(Total tt, int i) {
printf("Team #%d %s: %d wins and %d loses\n", i + 1, tt.name, tt.wins, tt.losses);
}
To read the team name, you can use scanf() with a " %24[^\n]" format. This format specifies a class of characters, namely all characters different from '\n' and the maximum number of characters to store into the destination array before the null terminator. The number 24 is hardcoded here and there is no simple way to specify this number as an expression involving NAME_LENGTH. An alternative solution is to use fgets() to read a full line and strip the newline.
To sort the array of structures, you should use qsort() with a custom comparison function.
Also check the return value of scanf() to detect invalid or missing input and exit with an error message.
It is quite confusing to name the team structure Total. You should use meaningful names such as Team.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NAME_LENGTH 25
typedef struct {
char name[NAME_LENGTH];
int wins;
int losses;
int total;
} Team;
Team readTeamInfo(void);
void printInfo(Team tt, int);
int compare_teams(const void *p1, const void *p2) {
const Team *t1 = p1;
const Team *t2 = p2;
return (t1->total > t2->total) - (t1->total < t2->total);
}
int main() {
int i, teams;
printf("Number of teams > ");
if (scanf("%d", &teams) != 1 || teams <= 0) {
fprintf(stderr, "invalid input\n");
exit(1);
}
Team data[teams];
for (i = 0; i < teams; i++) {
data[i] = readTeamInfo();
data[i].total = data[i].wins - data[i].losses;
}
qsort(data, teams, sizeof(*data), compare_teams);
for (i = 0; i < teams; i++) {
printInfo(data[i], i);
}
return 0;
}
Team readTeamInfo(void) {
Team tt;
printf("Team name > ");
if (scanf(" %24[^\n]", tt.name) != 1) {
fprintf(stderr, "invalid input\n");
exit(1);
}
printf("Wins > ");
if (scanf("%d", &tt.wins) != 1) {
fprintf(stderr, "invalid input\n");
exit(1);
}
printf("Losses > ");
if (scanf("%d", &tt.losses) != 1) {
fprintf(stderr, "invalid input\n");
exit(1);
}
return tt;
}
void printInfo(Team tt, int i) {
printf("Team #%d %s: %d wins and %d loses\n",
i + 1, tt.name, tt.wins, tt.losses);
}

swap strings function in c with pointers

When I try to swap between strings in the function Update_student it doesn't make it. Why?
#include <stdio.h>
#include <string.h>
#define SIZE 1
struct student {
int id_number;
char name[50];
char sex[6];
int quiz_score[2];
int total_score;
};
void Add_Student_Records(struct student *pupil) {
printf("ID:");
scanf("%d", &pupil->id_number);
printf("Name: ");
scanf("%s", &pupil->name);
printf("Sex :");
scanf("%s", &pupil->sex);
for (int i = 0; i < 2; i++) {
printf("Quit score %d:", i + 1);
scanf("%d", &pupil->quiz_score[i]);
}
pupil->total_score = pupil->quiz_score[0] + pupil->quiz_score[1];
return;
}
void Add_Students(struct student *students) {
for (int i = 0; i < SIZE; i++) {
printf("Student %d:\n", i + 1);
Add_Student_Records(students);
}
return;
}
void Print_Students(struct student *students) {
for (int i = 0; i < SIZE; i++) {
printf("Student %d details: \n", i + 1);
printf("ID:%d\n", students->id_number);
printf("Name:%s\n", students->name);
printf("Sex:%s\n", students->sex);
for (int i = 0; i < 2; i++) {
printf("Quit score %d:\n", students->quiz_score[i]);
}
printf("Total score: %d\n", students->total_score);
students++;
}
return;
}
void Replace_Strings(char **old_string, char **new_string) {
*old_string = *new_string;
return;
}
void Update_Student(struct student *students) {
int i = 0;
char name[50], new_name[50], cur_name[50];
printf("You can update name, and scores.\n");
printf(" current Name: ");
scanf("%s", name);
printf("new name: ");
scanf("%s", &new_name);
while (i < SIZE) {
strcpy(cur_name, students->name);
if (strcmp(cur_name, name) == 0) {
char *ptr_old_stud_name = students->name;
char *ptr_new_stud_name = new_name;
Replace_Strings(&ptr_old_stud_name, &ptr_new_stud_name);
}
i++;
students++;
}
return;
}
int main() {
struct student students[SIZE];
char ch;
/*1.Add student, 2. Print student*/
printf("1.Add student\n2.Print students\n3.Update student\n");
scanf("%c", &ch);
while (ch != 'E') {
if (ch == '1') {
Add_Students(&students[0]);
}
else if (ch == '2') {
Print_Students(&students[0]);
}
else if (ch =='3') {
Update_Student(&students[0]);
}
printf("Another operation:\t");
scanf("%c", &ch);
}
}
Replace_Strings(&ptr_old_stud_name,&ptr_new_stud_name); passes the addresses of ptr_old_stud_name and ptr_new_stud_name to ReplaceStrings.
ptr_old_stud_name and ptr_new_stud_name are local variables. The first is a pointer that has been set to point to students->name. The second is a pointer that has been set to point to new_name.
Replace_Strings changes the first thing it is passed a pointer to to the second thing it is passed a pointer to. So it changes ptr_old_stud_name to have the value of ptr_new_stud_name.
The result is that the local variable ptr_old_stud_name has a new value. This does not change the thing it points to, students->name.
More specifically, ptr_old_stud_name was pointing to the first character of students->name. students->name is an array, and it cannot be altered by changing pointers to it, and its address cannot be changed. To change its contents, you must copy new values into the bytes within it, which you could do by using strcpy to copy bytes into it from new_name.
Your function Update_student is confusing, you should just iterate through the array of students and compare the student's name with cur_name and replace the name when there is a match.
You should also pass the number of students to handle as an argument.
Here is a modified version:
void Update_Student(struct student *students, int count) {
char cur_name[50], new_name[50];
printf("You can update name, and scores.\n");
printf(" current Name: ");
scanf("%49s", cur_name);
printf("new name: ");
scanf("%49s", new_name);
for (int i = 0; i < count; i++) {
if (strcmp(cur_name, students[i].name) == 0) {
strcpy(students[i].name, new_name);
}
}
}
Call from main as Update_Student(students, SIZE);
Note also that you should ignore whitespace when reading the commands by adding a space before the %c:
scanf(" %c", &ch);
Here is a modified version with for multiple students:
#include <stdio.h>
#include <string.h>
#define SIZE 10
struct student {
int id_number;
char name[50];
char sex[6];
int quiz_score[2];
int total_score;
};
int Add_Student_Records(struct student *pupil) {
printf("ID:");
if (scanf("%d", &pupil->id_number) != 1)
return 0;
printf("Name: ");
if (scanf("%49s", pupil->name) != 1)
return 0;
printf("Sex :");
if (scanf("%1s", pupil->sex) != 1)
return 0;
for (int i = 0; i < 2; i++) {
printf("Quiz score %d:", i + 1);
if (scanf("%d", &pupil->quiz_score[i]) != 1)
return 0;
}
pupil->total_score = pupil->quiz_score[0] + pupil->quiz_score[1];
return 1;
}
int Add_Students(struct student *students, int count) {
int i;
for (int i = 0; i < count; i++) {
printf("Student %d:\n", i + 1);
if (Add_Student_Records(students + i) == 0)
break;
}
return i;
}
void Print_Students(struct student *students, int count) {
for (int i = 0; i < count; i++) {
printf("Student %d details: \n", i + 1);
printf("ID:%d\n", students->id_number);
printf("Name:%s\n", students->name);
printf("Sex:%s\n", students->sex);
for (int i = 0; i < 2; i++) {
printf("Quit score %d:\n", students->quiz_score[i]);
}
printf("Total score: %d\n", students->total_score);
students++;
}
}
void Update_Student(struct student *students, int count) {
char cur_name[50], new_name[50];
printf("You can update name, and scores.\n");
printf(" current Name: ");
if (scanf("%49s", cur_name) != 1)
return;
printf("new name: ");
if (scanf("%49s", new_name) != 1)
return;
for (int i = 0; i < count; i++) {
if (strcmp(cur_name, students[i].name) == 0) {
strcpy(students[i].name, new_name);
}
}
}
int main() {
struct student students[SIZE];
int n = 0;
char ch = 'E';
/* print the menu */
printf("1. Add student\n"
"2. Print students\n"
"3. Update student\n");
scanf(" %c", &ch);
while (ch != 'E') {
if (ch == '1') {
if (n == SIZE) {
printf("student array is full\n");
} else {
/* add more students */
n += Add_Students(&students[n], SIZE - n);
}
} else
if (ch == '2') {
Print_Students(students, n);
} else
if (ch =='3') {
Update_Student(students, n);
}
scanf("%*[^\n]"); // consume the rest of the pending input line
printf("Another operation:\t");
if (scanf(" %c", &ch) != 1)
break;
}
return 0;
}

Trying to understand the problems in my c code

I created a code with 4 functions:
Data_duplication that checks if the name is already exists.
Init that the function initializes through input from the user a set of names and a set of scores. Both arrays of the same size which called size. Whenever data is collected for arrays, they must be valid. If the user typed an invalid name, an error message should be printed and an alternate statistic requested. A valid score is complete between 0 and 100. A valid name meets the following conditions:
Begins with a large Latin letter.
All but the first characters are lowercase Latin characters.
Not already in the array. (The array must not contain the same name twice.)
Find function that gets as parameters, an array of names, an array of grades, and the size of these arrays. In addition, it receives as a student name parameter. The function finds the student's position in the array of names, and returns its grade. If the student does not appear in the set, 1- will be returned.
FreeAll that frees all the memory from the arrays.
PROBLEM:
When i enter for example that there are 3 students it is asking from me to write a fourth student and then it writes a grade that I don't know from where and gets out of the program.
Code:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#define SIZE 20
int Data_duplication(char* temp, char** names, int line);
void Init(char*** names, int** grades, int* size);
int Find(char** names, int* grades, int size, char* name);
void FreeAll(char*** names, int** grades, int size);
int main()
{
char** Names = NULL;
int* Grades = NULL;
int size, grade;
char name[SIZE] = { 0 };
Init(&Names, &Grades, &size);
printf("Enter a student name\n");
scanf("%s", name);
grade = Find(Names, Grades, size, name);
printf("%d", grade);
FreeAll(&Names, &Grades, size);
return 0;
}
void Init(char*** names, int** grades, int* size)
{
int i, j, flag;
int strlengh;
char temp[SIZE] = { 0 };
printf("Enter number of students\n");
scanf("%d", size);
*names = (char**)malloc((*size) * sizeof(char*));
if (!(*names))
{
printf("Error");
return;
}
*grades = (int*)malloc((*size) * sizeof(int));
if (!*grades)
{
printf("Error");
return;
}
for (i = 0; i < *size; i++)
{
printf("Enter a name\n");
scanf("%s", temp);
strlengh = strlen(temp);
do
{
flag = 1;
if (strlen(temp) > 20)//if it longer then it should be
flag = 0;
if (temp[0] > 'Z' || temp[0] < 'A') //start with capital letter
flag = 0;
for (j = 1; temp[j] != '\0'; j++)//all the letter is a lower case letters except from the first
{
if (temp[j] > 'z' || temp[j] < 'a')
{
flag = 0;
break;
}
}
if (Data_duplication(temp, *names, i))//if its not a name that already entered
{
flag = 0;
}
if (flag)//if the name is ok
{
(*names)[i] = (char*)malloc((strlengh + 1) * sizeof(char));
if (!(*names)[i])
{
printf("Error");
return;
}
strcpy((*names)[i], temp);
}
else//if somthing wrong
{
printf("Bad name,try again.\n");
scanf("%s", temp);
}
} while (!flag);
printf("Enter grade\n");
scanf("%d", (*grades + i));
while (*(*grades + i) < 0 || *(*grades + i) > 100)//if the grade between 0 to 100
{
printf("Bad grade,try again.\n");
scanf("%d", (*grades + i));
}
}
}
int Data_duplication(char* temp, char** names, int line)//find if there is another name like this that already entered
{
for (int i = 0; i < line; i++)
{
if (!strcmp(temp, names[i]))
{
return 1;
}
}
return 0;
}
int Find(char** names, int* grades, int size, char* name)
{
int i;
for (i = 0; i < size; i++)
{
if (strcmp(name, names[i]) == 0);
{
return (*(grades + i));
}
}
return -1;
}
void FreeAll(char*** names, int** grades, int size)//free al the dynamic memo allocation
{
for (int i = 0; i < size; i++)
{
free(*(*names + i));
}
free(*names);
free(*grades);
}
arrays passed by reference so you don't need to pass pointer to array.
I made changes in your code.
#include <stdio.h>
#include <stdlib.h>
#include<malloc.h>
#include<string.h>
#define SIZE 20
int Data_duplication(char* temp, char** names, int line);
void Init(char** names, int* grades, int size);
int Find(char** names, int* grades, int size, char* name);
void FreeAll(char** names, int* grades, int size);
int main()
{
int size, grade;
char name[SIZE];
printf("Enter number of students\n");
scanf("%d", &size);
char** Names=(char**)malloc((size) * sizeof(char*));
int* Grades= (int*)malloc((size) * sizeof(int));
Init(Names, Grades, size);
printf("--------------------------------------------------------------------\n");
printf("Enter a student name\n");
scanf("%s",name);
grade = Find(Names,Grades,size,name);
printf("%d", grade);
FreeAll(Names, Grades, size);
return 0;
}
void Init(char** names, int* grades, int size)
{
int i, j, flag;
int strlengh;
if (!(names))
{
printf("Error");
return;
}
for(i = 0; i < size ; i++){
names[i] = malloc(sizeof(char)*SIZE);
if(!names[i])
{
printf("Error");
return;
}
}
if (!grades)
{
printf("Error");
return;
}
for (i = 0; i < size; i++)
{
printf("Student i= %d\n",i);
do
{
char temp[SIZE]="";
printf("Enter a name\n");
scanf("%s", temp);
strlengh = strlen(temp);
flag = 1;
if (strlen(temp) > SIZE)//if it longer then it should be
flag = 0;
if (temp[0] > 'Z' || temp[0] < 'A') //start with capital letter
flag = 0;
for (j = 1; temp[j] != '\0'; j++)//all the letter is a lower case letters except from the first
{
if (temp[j] > 'z' || temp[j] < 'a')
{
flag = 0;
break;
}
}
if (Data_duplication(temp, names, i))//if its not a name that already entered
{
flag = 0;
}
if (flag)//if the name is ok
{
strcpy(names[i], temp);
printf("temp= %s\n", temp);
printf("names[i]= %s\n", names[i]);
}
else//if somthing wrong
{
printf("Bad name,try again.\n");
}
} while (!flag);
printf("Enter grade\n");
scanf("%d",&grades[i]);
while (grades[i] < 0 || grades[i] > 100)//if the grade between 0 to 100
{
printf("Bad grade,try again.\n");
scanf("%d",&grades[i]);
}
}
}
int Data_duplication(char* temp, char** names, int line)//find if there is another name like this that already entered
{
for (int i = 0; i < line; i++)
{
if (!strcmp(temp, names[i]))
{
return 1;
}
}
return 0;
}
int Find(char** names, int* grades, int size, char* name)
{
int i;
for (i = 0; i < size; i++)
{
printf("name= %s\n",name);
printf("names[i]= %s\n", names[i]);
if (strcmp(name, names[i]) == 0)
{
return (grades[i]);
}
}
return -1;
}
void FreeAll(char** names, int* grades, int size)//free al the dynamic memo allocation
{
for (int i = 0; i < size; i++)
{
free(names[i]);
}
free(names);
free(grades);
}

malloc/struct pointer array seg fault

I have this code below, includes and other functions which are called in main are correct. But when I am trying to malloc the words[counter]->input_str, I get a segmentation fault all the time. I don't know what to do.
struct copy {
char *input_str;
char *create_word;
};
int main(int argc, char *argv[]) {
static struct copy *words[ARRAY_SIZE];
char format_str[20];
char word_str[STR_SIZE];
int how_many;
int counter = 0;
char answer;
do{
sprintf(format_str," %%%ds",STR_SIZE - 1);
printf("Enter string: ");
scanf(format_str,word_str);
words[counter]->input_str = (char *)malloc((strlen(word_str) + 1)*sizeof(char));
if(words[counter]->input_str == NULL) {
printf("memory problem\n");
exit(-1);
}
words[counter]->input_str = word_str;
printf("Enter integer: ");
scanf(" %d",&how_many);
words[counter]->create_word = duplicate(word_str,how_many);
counter++;
if(words[counter - 1] == NULL) {
printf("error\n");
exit(-1);
}
print(words,counter);
do{
printf("More (y/n)? ");
scanf(" %c",&answer);
}while(answer != 'y' && answer != 'n');
}while(counter < ARRAY_SIZE && answer == 'y');
clean(words,counter);
return(0);
}
Here are two versions of your code,one using struct copy *words[ARRAY_SIZE]; at the top of main(), the other using struct copy words[ARRAY_SIZE]; (which therefore does less memory allocation).
Array of pointers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct copy
{
char *input_str;
char *create_word;
};
enum { ARRAY_SIZE = 20, STR_SIZE = 30 };
extern char *duplicate(const char *str, int number);
extern void print(struct copy **words, int number);
extern void clean(struct copy **words, int number);
int main(void)
{
struct copy *words[ARRAY_SIZE];
char format_str[20];
char word_str[STR_SIZE];
int how_many;
int counter = 0;
char answer;
sprintf(format_str, " %%%ds", STR_SIZE - 1);
do
{
printf("Enter string: ");
if (scanf(format_str, word_str) != 1)
break;
printf("Enter integer: ");
if (scanf(" %d", &how_many) != 1 || how_many < 0 || how_many > 999)
break;
words[counter] = malloc(sizeof(*words[counter]));
words[counter]->input_str = (char *)malloc((strlen(word_str) + 1) * sizeof(char));
if (words[counter]->input_str == NULL)
{
fprintf(stderr, "memory problem\n");
exit(-1);
}
strcpy(words[counter]->input_str, word_str);
words[counter]->create_word = duplicate(word_str, how_many);
// Superfluous because duplicate exits if there is an allocation error
if (words[counter]->create_word == NULL)
{
fprintf(stderr, "error\n");
exit(-1);
}
counter++;
print(words, counter);
do
{
printf("More (y/n)? ");
if (scanf(" %c", &answer) != 1)
{
answer = 'n';
break;
}
} while (answer != 'y' && answer != 'n');
} while (counter < ARRAY_SIZE && answer == 'y');
clean(words, counter);
return(0);
}
void print(struct copy **words, int number)
{
printf("Words (%d):\n", number);
for (int i = 0; i < number; i++)
printf("[%s] => [%s]\n", words[i]->input_str, words[i]->create_word);
}
char *duplicate(const char *str, int number)
{
int len1 = strlen(str);
int len2 = number * len1 + 1;
char *space = malloc(len2);
if (space == NULL)
{
fprintf(stderr, "memory allocation failed for %d bytes\n", len2);
exit(-1);
}
for (int i = 0; i < number; i++)
strcpy(&space[i * len1], str);
space[len2 - 1] = '\0'; // In case number == 0
return space;
}
void clean(struct copy **words, int number)
{
for (int i = 0; i < number; i++)
{
free(words[i]->input_str);
free(words[i]->create_word);
free(words[i]);
words[i] = NULL;
}
}
Array of structures
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct copy
{
char *input_str;
char *create_word;
};
enum { ARRAY_SIZE = 20, STR_SIZE = 30 };
extern char *duplicate(const char *str, int number);
extern void print(struct copy *words, int number);
extern void clean(struct copy *words, int number);
int main(void)
{
struct copy words[ARRAY_SIZE];
char format_str[20];
char word_str[STR_SIZE];
int how_many;
int counter = 0;
char answer;
sprintf(format_str, " %%%ds", STR_SIZE - 1);
do
{
printf("Enter string: ");
if (scanf(format_str, word_str) != 1)
break;
words[counter].input_str = (char *)malloc((strlen(word_str) + 1) * sizeof(char));
if (words[counter].input_str == NULL)
{
fprintf(stderr, "memory problem\n");
exit(-1);
}
strcpy(words[counter].input_str, word_str);
printf("Enter integer: ");
if (scanf(" %d", &how_many) != 1 || how_many < 0 || how_many > 999)
break;
words[counter].create_word = duplicate(word_str, how_many);
// Superfluous because duplicate exits if there is an allocation error
if (words[counter].create_word == NULL)
{
fprintf(stderr, "error\n");
exit(-1);
}
counter++;
print(words, counter);
do
{
printf("More (y/n)? ");
if (scanf(" %c", &answer) != 1)
{
answer = 'n';
break;
}
} while (answer != 'y' && answer != 'n');
} while (counter < ARRAY_SIZE && answer == 'y');
clean(words, counter);
return(0);
}
void print(struct copy *words, int number)
{
printf("Words (%d):\n", number);
for (int i = 0; i < number; i++)
printf("[%s] => [%s]\n", words[i].input_str, words[i].create_word);
}
char *duplicate(const char *str, int number)
{
int len1 = strlen(str);
int len2 = number * len1 + 1;
char *space = malloc(len2);
if (space == NULL)
{
fprintf(stderr, "memory allocation failed for %d bytes\n", len2);
exit(-1);
}
for (int i = 0; i < number; i++)
strcpy(&space[i * len1], str);
space[len2 - 1] = '\0'; // In case number == 0
return space;
}
void clean(struct copy *words, int number)
{
for (int i = 0; i < number; i++)
{
free(words[i].input_str);
free(words[i].create_word);
words[i].input_str = words[i].create_word = NULL;
}
}
Sample output:
Enter string: abc
Enter integer: 1
Words (1):
[abc] => [abc]
More (y/n)? y
Enter string: def
Enter integer: 2
Words (2):
[abc] => [abc]
[def] => [defdef]
More (y/n)? y
Enter string: absolute-twaddle
Enter integer: 10
Words (3):
[abc] => [abc]
[def] => [defdef]
[absolute-twaddle] => [absolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddle]
More (y/n)? y
Enter string: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Enter integer: 0
Words (4):
[abc] => [abc]
[def] => [defdef]
[absolute-twaddle] => [absolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddleabsolute-twaddle]
[ABCDEFGHIJKLMNOPQRSTUVWXYZ] => []
More (y/n)? n
(Either program gives the same output for the same input. Both run clean under Valgrind — a site which still does not support https connections.)

Why I can't input my name with gets?

Here's the part of my code:
Problem: It skips the input of "please enter your name" to "please enter your marks"
What I tried: flushall(); _flushall(); - which worked yesterday somehow, and trying to place these functions between printf,scanf..
student *Create_Class(int size) {
int i, j;
int idStud, nameStud, markStud;
student *classStudent;
classStudent = (student*)malloc(size * sizeof(student));
for (i = 0; i < size; i++) {
classStudent[i].name = (char*)malloc(51 * sizeof(char));
int numOfmarks = 4;
printf("Please enter your name: ");
gets(classStudent[i].name);
_flushall(); //tried _flushall() and it worked yesterday.. tried fflush(NULL) too.
printf("\nPlease enter 4 marks: ");
for (j = 0; j < numOfmarks; j++) {
scanf("%d", &classStudent[i].marks[j]);
}
Avg_Mark(&classStudent[i]);
}
return classStudent;
}
EDIT: (FULL CODE)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
typedef struct {
char *name;
int marks[4];
float avg;
} student;
student *Create_Class(int);
void Avg_Mark(student*);
void Print_One(student*);
void exStudents(student *s, int size);
int main() {
int size, i;
student *arr;
printf("\nEnter the number of students: \n");
scanf("%d", &size);
arr = Create_Class(size);
exStudents(arr, size);
for (i = 0; i < size; i++)
free(arr[i].name);
free(arr);
getch();
}
student *Create_Class(int size) {
int i, j;
int idStud, nameStud, markStud;
student *classStudent;
classStudent = (student*)malloc(size * sizeof(student));
for (i = 0; i < size; i++) {
classStudent[i].name = (char*)malloc(51 * sizeof(char));
int numOfmarks = 4;
int sizeOfName;
printf("Please enter your name: \n");
_flushall();
fgets(classStudent[i].name,50,stdin);
sizeOfName = strlen(classStudent[i].name);
printf("Please enter 4 marks: ");
for (j = 0; j < numOfmarks; j++) {
scanf("%d", &classStudent[i].marks[j]);
}
Avg_Mark(&classStudent[i]);
}
return classStudent;
}
void Avg_Mark(student *s) {
int i, numOfMarks = 4, sum = 0;
for (i = 0; i < numOfMarks; i++) {
sum += s->marks[i];
}
s->avg = (sum / 4.0);
}
void Print_One(student *s) {
printf("The average of %s is %f", s->name, s->avg);
}
void exStudents(student *s, int size) {
int flag = 1;
while (size > 0) {
if (s->avg > 85) {
Print_One(s);
flag = 0;
}
s++;
size--;
}
if (flag)
printf("\n There're no students with above 85 average.");
}
As you have already been told in comments, the solution is to use a two-step approach: Read lines first, then scan these lines as appropriate. This reflects how users are going to answer your prompts, namely by providing the information and then hitting enter.
Here's a variant of your code which does that. I've also changed the main function, because it also used scanf and I've added a function to strip white space from the string input by fgets. (This function requires the <ctype.h> header.)
#include <ctype.h>
int main()
{
char line[80];
int size, i;
puts("Enter the number of students:");
if (fgets(line, sizeof(line), stdin) == NULL) exit(1);
if (sscanf(line, "%d", &size) == 1 && size > 0) {
student *arr = Create_Class(size);
exStudents(arr, size);
for (i = 0; i < size; i++) free(arr[i].name);
free(arr);
}
return 0;
}
/*
* strip white space from beginning and end of string
*/
char *strip(char *str)
{
size_t l = strlen(str);
while (l > 0 && isspace((unsigned char) str[l - 1])) l--;
str[l] = '\0';
while (isspace((unsigned char) *str)) str++;
return str;
}
/*
* Create students and prompt user for input
*/
student *Create_Class(int size)
{
int i;
student *classStudent = malloc(size * sizeof(student));
for (i = 0; i < size; i++) {
char line[80];
char *p;
int okay = 0;
puts("Please enter your name:");
if (fgets(line, sizeof(line), stdin) == NULL) exit(1);
p = strip(line);
classStudent[i].name = malloc(strlen(p) + 1);
strcpy(classStudent[i].name, p);
while (!okay) {
int j = 0;
okay = 1;
puts("Please enter 4 marks:");
if (fgets(line, sizeof(line), stdin) == NULL) exit(1);
p = line;
while (p && j < 4) {
char *tail;
int m = strtol(p, &tail, 10);
if (p == tail) break;
if (m < 1 || m > 100) {
puts("Illegal mark.");
okay = 0;
}
classStudent[i].marks[j++] = m;
p = tail;
}
if (j < 4) {
printf("Expected 4 marks, but got %d.\n", j);
okay = 0;
}
}
Avg_Mark(&classStudent[i]);
}
return classStudent;
}
Please refrain from flushing buffers wihout reason. When a new-line character is written, stdout is flushed, so make it a rule to terminate all your strings with a new-line. New-lines at the beginning of strings instead of at the end are a sign of untidy output.
SOLUTION:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
typedef struct { //struct decleration
char *name;
int marks[4];
float avg;
} student;
//functions decleration
student *Create_Class(int);
void Avg_Mark(student*);
void Print_One(student*);
void exStudents(student *s, int size);
int main() {
/*variable declerations*/
int i, size;
char line[80];
student *arr;
/*Input number of students*/
printf("\nEnter the number of students: \n");
fgets(line, sizeof(line), stdin);
sscanf(line, "%d", &size);
/*Get name of students, marks, and calculate average above 85*/
arr = Create_Class(size);
exStudents(arr, size);
/*Free memory*/
for (i = 0; i < size; i++)
free(arr[i].name);
free(arr);
getch();
}
student *Create_Class(int size) { /*Get names of each student, and their 4 marks.*/
/*Variable declerations*/
int i, j;
char line[51];
student *classStudent;
/*Dynamic allocation to assign structure to every student*/
classStudent = (student*)malloc(size * sizeof(student));
/*Get name of students and their 4 marks*/
for (i = 0; i < size; i++) {
/*Variable decleration and dynamic allocation of 51 chars*/
classStudent[i].name = (char*)malloc(51 * sizeof(char));
int numOfmarks = 4;
int sizeOfName;
/*Input name of student*/
printf("Please enter your name: \n");
scanf("%s", classStudent[i].name);
/*Input marks of student*/
printf("Please enter 4 marks: ");
for (j = 0; j < numOfmarks; j++) {
scanf("%d", &classStudent[i].marks[j]);
}
/*Calculate average, and print averages of students above 85*/
Avg_Mark(&classStudent[i]);
}
return classStudent;
}
/*Calculate averages of students*/
void Avg_Mark(student *s) {
int i, numOfMarks = 4, sum = 0;
for (i = 0; i < numOfMarks; i++) {
sum += s->marks[i];
}
s->avg = (sum / 4.0);
}
/*Print average (if bigger than 85)*/
void Print_One(student *s) {
printf("The average of %s is %0.1f\n", s->name, s->avg);
}
/*Check whether the average is bigger than 85*/
void exStudents(student *s, int size) {
int flag = 1; //flag to check if there are any students with avg above 85
while (size > 0) {
if (s->avg > 85) {
Print_One(s); //Print the average
flag = 0; //We found atleast one student with avg > 85
}
s++; //Advance to next student
size--;
}
if (flag)
printf("\n There're no students with above 85 average.");
}
The problem in your code is that scanf does not consume the new-line returned by the user in:
scanf("%d", &size);
So when the program reaches:
fgets(classStudent[i].name,50,stdin);
the remaining new-line in stdin is received before the user can type anything.
A solution is to replace the initial scanf call by fgets and atoi calls.
char size_str[5];
fgets(size_str,5,stdin);
size = atoi(size_str);
A combination of fgets and sscanf also works also fine in general to first process user inputs and then convert it.
The variant with sscanf is:
char size_str[5];
fgets(size_str,5,stdin);
sscanf(size_str,"%d\n",&size);
Note that it might be safe to stop the program if the value entered is too large. Here we allow from 0 up to 999.
Note also that you have to do the same change some lines below.
instead of:
scanf("%d", &classStudent[i].marks[j]);
write:
char mark_str[5];
fgets(mark_str,5,stdin);
sscanf(mark_str,"%d\n",&classStudent[i].marks[j]);
Hope this helps.

Resources