Problem with pointer in array of structs in C - c

A problem stops the program working correctly. I guess it is related with using pointer in array of structs. Despite trying many ways, nothing could find out:
#include <stdio.h>
typedef struct {
char* courseName;
char* courseDate;
double grade;
}EXAM;
int main() {
double averageGrade = 0;
EXAM *exams[5];
for(int i = 0; i < 5; i++){
printf("Please enter course name:\n");
scanf("%s", exams[i]->courseName);
printf("Please enter course date:\n");
scanf("%s", exams[i]->courseDate);
printf("Please enter course grade:\n");
scanf("%lf", &exams[i]->grade);
averageGrade += exams[i]->grade;
}
averageGrade /= 5;
printf("Your average grade is: %.1lf.", averageGrade);
return 0;
}

1:
typedef struct {
char* courseName;
char* courseDate;
double grade;
}EXAM;
Having defined courseName and courseDate as 'char *' means that they are only pointers. Pointers that without any allocation point to either NULL or more probably to garbage. How to fix it?
Either define your structure with statically allocated char arrays, like:
typedef struct {
char courseName[41];
char courseDate[41];
double grade;
}EXAM;
or allocate them dynamically before signing anything into them:
for (int i = 0; i < 5; i++) {
exams[i]->courseName = (char *) calloc(41, sizeof(char));
exams[i]->courseDate = (char *) calloc(41, sizeof(char));
}
In both cases you should make sure, that user doesn't overflow this fields so instead of "%s" in scanfs I would suggest using "%40[^\n]", which is simply Get up to 40 characters from user until you find a '\n' character (user hits enter).
In addition in first case, you should make sure that both strings are null-terminated strings, a simple way to do it is to set zeros in all tables.
memset(exams, 0, sizeof(EXAM)*5);
2:
EXAM *exams[5];
This are also only pointers. That unallocated point to NULL or garbage. Go with either:
EXAM exams[5];
and use '.' instead of '->'.
or allocate them:
for (int i = 0; i < 5; i++) {
exams[i]= (EXAM *) calloc(1, sizeof(EXAM));
}

EXAM[*] points to unallocated memory:
To create an array of 5 exam objects do:
EXAM exams[5]; // Note there is no * character.
If the pointer is necessary for some reason, then you will need to allocate space for these objects:
EXAM *exams[5];
for (int i=0; i<5; i++) {
exams[i] = calloc(1, sizeof(EXAM));
}
You also need to allocate space for the strings:
In C, when you use scanf on a %s, the space for that string must already be allocated.
So you should either use char courseName[256];, allocate sufficient space on the heap for the string, or use functions like getline() which will allocate the space for you.

Related

how do I assign individual string to an element in one array of pointer?

I am new to C and still trying to figure out pointer.
So here is a task I am stuck in: I would like to assign 10 fruit names to a pointer of array and print them out one by one. Below is my code;
#include <stdio.h>
#include <string.h>
int main(){
char *arr_of_ptrs[10];
char buffer[20];
int i;
for (i=0;i<10;i++){
printf("Please type in a fruit name:");
fgets(buffer,20,stdin);
arr_of_ptrs[i]= *buffer;
}
int j;
for (j=0;j<10;j++){
printf("%s",*(arr_of_ptrs+j));
}
}
However after execution this, it only shows me the last result for all 10 responses. I tried to consult similar questions others asked but no luck.
My understanding is that
1) pointer of array has been allocated memory with [10] so malloc() is not needed.
2) buffer stores the pointer to each individual answer therefore I dereference it and assign it to the arr_of_ptrs[i]
I am unsure if arr_of_ptrs[i] gets me a pointer or a value. I thought it is definitely a pointer but I deference it with * the code and assign it to *buffer, program would get stuck.
If someone could point out my problem that would be great.
Thanks in advance
Three erros, 1. You must allocate memory for elements of elements of arr_of_ptrs, now you only allocate the memory for elements of arr_of_ptrs on stack memory. 2. arr_of_ptrs[i]= *buffer; means all of the arr_of_ptrs elements are pointed to same memory address, which is the "buffer" pointer. So all the elements of arr_of_ptrs will be same to the last stdin input string. 3. subsequent fgets() call has potential problem, one of the explaination could be here
A quick fix could be,
#include <stdio.h>
#include <string.h>
int main(){
const int ARR_SIZE = 10, BUFFER_SIZE = 20;
char arr_of_ptrs[ARR_SIZE][BUFFER_SIZE];
char *pos;
int i, c;
for (i = 0; i < ARR_SIZE; ++i) {
printf ("Please type in a fruit name: ");
if (fgets (arr_of_ptrs[i], BUFFER_SIZE, stdin) == NULL) return -1;
if ((pos = strchr(arr_of_ptrs[i], '\n')))
*pos = 0;
else
while ((c = getchar()) != '\n' && c != EOF) {}
}
for (i = 0; i < ARR_SIZE; ++i)
printf("%s\n", arr_of_ptrs[i]);
return 0;
}
The misunderstanding is probably that "Dereferencing" an array of characters, unlike dereferencing a pointer to a primitive data type, does not create a copy of that array. Arrays cannot be copied using assignment operator =; There a separate functions for copying arrays (especially for 0-terminated arrays of char aka c-strings, and for allocating memory needed for the copy):
Compare a pointer to a primitive data type like int:
int x = 10;
int *ptr_x = &x;
int copy_of_x = *ptr_x; // dereferences a pointer to x, yielding the integer value 10
However:
char x[20] = "some text"; // array of characters, not a single character!
char *ptr_x = &x[0]; // pointer to the first element of x
char copy_of_first_char_of_x = *ptr_x; // copies the 's', not the entire string
Use:
char x[20] = "some text";
char *ptr_x = &x[0];
char *copy_of_x = malloc(strlen(ptr_x)+1); // allocate memory large enough to store the copy
strcpy(copy_of_x,ptr_x); // copy the string.
printf("%s",copy_of_x);
Output:
some text

How to set multiple strings according to user's request? And then sort them and put in an array of strings?

I received the following question:
Write a program that receives the user's number of friends from the user.
Once the number is received, the program will record the members' names.
Keep the names in the array (it will be a string array, i.e. a char ** array) so that each cell in the array points to the beginning of a string.
Then sort the array in alphabetical order (using the strcmp function and swap between pointers) and print the member names in order.
So far this is what I did and I don't understand why it doesn't work:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int choice = 0;
int i = 0;
printf("Enter number of friends: ");
scanf_s("%d", &choice);
char* friends = (char*)malloc(choice * 50 * sizeof(char));
for (i = 0; i < choice; i++)
{
printf("Enter name of friend %d: ", (i + 1));
scanf_s("%s", &friends[i]);
}
getchar();
return 0;
}
Can someone please help me?
Never cast malloc as you did in your code.
char* friends = (char*)malloc(choice * 50 * sizeof(char));
If you declared friends as a pointer as above, friends[i] is only the i_th character in string friends (for example, with friends = "abcd", friends[0] = 'a', friends[1] = 'b'and so on).
If you want to store all names, you should use 2D array, double pointer, or an array of pointer:
char friends[choice][50]; // (1)
OR
char * friends[choice]; // (2)
OR
char ** friends; // (3)
For solution (2), you have to allocate for each pointer in array friends (do not forget to free the pointer at the end of program):
for(int i = 0; i < choice; i++) {
friends[i] = malloc(sizeof(char) * 50);
if (!friends[i])
// handle the error of malloc function
return -1;
}
Then for scanf function (you can use fgets instead of scanf):
scanf("%49s", friends[i]);
If you want to use double pointer (solution (3)), you have to allocate for pointer friends then for each pointer friend[i] (do not forget to free all pointers at the end of program):
friends = malloc(sizeof(char *)* choice);
if (!friends)
// handle the error of malloc function
return -1;
for(int i = 0; i < choice; i++) {
friends[i] = malloc(sizeof(char) * 50);
if (!friends[i])
// handle the error of malloc function
return -1;
}
One attention, solutions (1) and (2) require VLAs to be available. They are with C since C99 and became optional since C11. Thanks #alk

How to fix my code to return a pointer to a struct from a function

I'm learning pointers and struct now and I'm a bit confused. Particularly, I want to make a function which returns a pointer to a struct. My code is compiling, but it isn't working. After I type the number of students, it asks for name and age (without reading name), and after I type the age, it closes.
#include <stdio.h>
#include <stdlib.h>
struct details
{
char name[100];
int age;
};
struct details * details_pointer(int n)
{
struct details pointer_x[n];
struct details *pointer = pointer_x;
for (int i=0; i<n; i++)
{
printf("Student %d:\n", i);
printf("name:\n");
fgets(pointer[i].name, 100, stdin);
printf("age:\n");
scanf("%d", pointer[i]. age);
}
return pointer;
}
int main()
{
int n;
printf("Type the number of persons:\n");
scanf("%d", &n);
struct details *student = details_pointer(n);
printf("\nName: %s\n", (*student).name);
printf("Age: %d\n", (*student).age);
system("pause");
return 0;
}
The problem here is that you're returning a structure variable (pointer_x) that's allocated locally on the stack of the inner details_pointer() function, but this memory is no longer reserved for you once it returns. This means you get (at best) garbage.
You have to either allocate memory in the function and return it (and remember to free it!) or pass the data to the function to fill it in.
void get_details(int n, struct details p[n])
{
for (int i = 0; i < n; i++)
{
// do stuff with p[i]
}
}
int main()
{
...
scanf("%d", &n);
struct details students[n];
get_details(n, students);
printf("\nName: %s\n", students[0].name);
...
}
I generally prefer passing data to the function when possible just because it simplifies memory management.
EDIT: Two notes about your existing details_pointer() function.
1) In fgets(pointer[i].name, 100, stdin), it's a good practice to derive the number of bytes from the size of the array if it's known; in this case it is, so recommend fgets(pointer[i].name, sizeof(pointer[i].name), stdin). Yours is not incorrect, but it's easier to maintain should the sizes change in the future.
2) the scanf("%d", pointer[i].age) needs to take the address of the .age to stuff its value in there; you're passing a number instead of an address, which is for sure incorrect.
Ok, so if the assignment requires dynamic allocation, then that's what we have to do.
Just specifying an array implicitly allocates the space while the function is running, but it disappears when the function returns. You'll need to allocate it specifically.
Revisiting your code:
struct details *details_pointer(int n)
{
struct details *students = (struct details *)malloc(n * sizeof(struct details));
.... do stuff with students[0] and the like
return students;
}
This uses the malloc() - memory allocate - library function to get you some space, and it remains valid until you release it. The number of bytes is how many things you want (n) times how many bytes in one thing (sizeof(struct details))
Later:
int main()
{
struct details *student = details_pointer(n);
.. do stuff
free(student);
}
This now uses that memory, and releases it back to the OS when you're done.

Changing the array's value through a function

The function saves the new string and sets it at position 2. But when I run the last "for" there are just some strange symbols.
#include <stdio.h>
#include <stdlib.h>
void edit(int target, char *listf[]){
char *name[1];
printf("\nInsert the new name: \n");
scanf("%s", &name[0]);
listf[target] = &name[0];
printf("\nThe information has change!\n");
printf("Name: %s\n", listf[target]);
system("pause"); }
int main() {
char *listf[3] = {"Alpha", "Beta", "Omega"};
int i;
int target = 2;
for(i = 0; i < 3; i++){
printf("%s\n", listf[i]);
}
edit(target,listf);
printf(" \n\n");
for(i = 0; i < 3; i++){
printf("%s\n", listf[i]);
}
return 0; }
There are two problems with your code. First, the char name is an array of pointers, essentially. And those pointers.......they are not pointing to allocated memory, just garbage data. If you want to make good use of it, you have to assign a pointer to an allocated memory. Now, normally, when you use a function like scanf, they assume that the pointer you given to it(in this case &name[0]) have allocated memory to store the input string. But if you dont do this, your pointer would be pointing to a random address in the memory, possibly editing some other memory cells and causing your program to behave very strangely. So how do you solve this? I'm not claiming my solution to be perfect, but just use a plain char array, preferably a large one, to store the input string. Like this-
char name[1000];
The second problem is this line-
listf[target] = &name[0];
The thing here is that the data the name array is pointing to, is temporary. It will only last as long as the function edit() runs. Why? A simple way to say this is that, name belongs to the edit() function. So when edit() terminates, so does everything along with it. Meaning char name wont store whatever you put into it. This is the reason why you were getting strange symbols. So you have to put the contents of the input in another place in the memory. So you'll have to use malloc() here. Here's my fixed version of the edit function-
void edit(int target, char *listf[]){
char name[1000];
printf("\nInsert the new name: \n");
scanf("%s", name);
//figure out the length of the string and allocate memory accordingly
int len = strlen(name);
char* new_name = malloc(len * sizeof(char));
printf("Test %d\n", len * sizeof(char));
//copy the contents of name to new_name
strcpy(new_name,name);
listf[target] = new_name;
printf("\nThe information has change!\n");
printf("Name: %s\n", listf[target]);
system("pause");
}

How do I create (and use) an array of pointers to an array of strings in C?

I need to create an array of pointers that will each point to an array of strings.
The base, is a size 2 array of strings (the length of the strings is unknown at start). For example an array of 2 strings (first-name and last-name):
char *name[2];
Now I need to create an array of an unknown size (entered by the user) that will point to the type that I just created.
My idea was to create it this way:
char **people=name;
And then ask the user how many names he would like to enter and allocate enough space to hold all the names.
people=(char**)malloc(sizeof(char*)*num); //num is the number received by the user.
This is where things got too complicated to me and I can't figure out how to I call each individual name to put a string in it.
I built a loop that will receive all the names but I have no idea how to store them properly.
for(i=0;i<num;i++){
printf("Please enter the #%d first and last name:\n",i+1);
//Receives the first name.
scanf("%s",&bufferFirstName);
getchar();
//Receives the last name (can also include spaces).
gets(bufferLastName);
people[i][0]=(char*)malloc(strlen(bufferFirstName)+1);
people[i][1]=(char*)malloc(strlen(bufferLastName)+1);
//^^Needless to say that it won't even compile :(
}
Can anyone please tell me how to properly use this kind of an array of points?
Thanks.
from cdecl:
declare foo as array of pointer to array 2 of pointer to char
char *(*foo[])[2];
So, foo[0] is a pointer to array 2 of char *
That is the array, but for your use, you want:
declare foo as pointer to array 2 of pointer to char;
char *(*foo)[2];
Now you can do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *(*foo)[2];
printf("How many people?\n");
int n; scanf("%d", &n);
foo = malloc(sizeof *foo * n);
for (int i = 0; i < n; i++) {
char bufFirstName[1024];
char bufLastName[1024];
printf("Please insert the #%d first and last name:\n", i+1);
scanf("%s %s", bufFirstName, bufLastName);
char *firstName = malloc(strlen(bufFirstName) + 1);
char *lastName = malloc(strlen(bufLastName) + 1);
strcpy(firstName, bufFirstName);
strcpy(lastName, bufLastName);
foo[i][0] = firstName;
foo[i][1] = lastName;
}
for (int i = 0; i < n; i++) {
printf("Name: %s LastName: %s\n", foo[i][0], foo[i][1]);
}
return 0;
}
Compile with -std=c99
Note that using scanf, strcpy, strlen like that is unsafe because there can be a buffer overflow.
Also, remember to free your malloc's!
Not that your approach is wrong, but have you considered instead using a struct that includes first and last name, and then malloc'ing based on the number of names the user will enter:
typedef struct {
char* first;
char* last;
} person;
person* people = malloc(num * sizeof(*person));
This just simplifies pointer interaction. While the way you are doing it is a good exercise in understanding pointers better, it may not be the easiest way to understand.
If you are unable to use structs, you should instead be doing:
char** people;
people = malloc(2*num*sizeof(char*));
for (int i = 0; i < 2*num; i++)
people[i] = malloc(MAX_NAME_SIZE*sizeof(char));
Now you would need to reference the i th person via:
first name: people[i*2 + 0]
last name: people[i*2 + 1]

Resources