A friend and I are trying to teach ourselves C and have decided to do, a what was first thought to be easy, an exercise, where we create a struct of two char containing 1. the forename and 2. the surname. A function read_person gets the users input, saves it in the structure and returns it. The input should be saved in a dynamically allocated array (all of which we have allegedly done correct so far). Then, using qsort, the array should be sorted ascendingly when it comes to the forename, descendingly when it comes to the surname and lastly, considering the length of the forename. if the forenames are equally long, the surnames should be compared. Both of us were trying really hard to make the qsort work but it just wouldn't sort, therefore we were wondering whether anyone has an idea how to do it?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct details{
char forename[10];
char surname[10];
}[5];
struct details read_person(){
struct details d;
printf("Enter your forename: ");
fgets(d.forename, 10, stdin);
printf("Enter your surname: ");
fgets(d.surname, 10, stdin);
struct details *arr_dt=malloc(5 * sizeof(struct details));
free(arr_dt);
return d;
}
int main(){
read_person();
return 0;
}
You have a number of things going on here that are just not right, but that also touch on a few subtle issues that won't be apparent in attempting to stumble though how to fill your struct.
First, you declare a global array of
struct details
(5 of them). Don't do that. While legal, instead you simply want to declare thestruct
at global scope and then declare each instance withinmain()
and pass either a copy of the struct, or a pointer to the struct as parameters to any function within your code that needs it.Second, you declare
d
local toread_person
and then returnd
at the end for assignment back inmain()
. This is fine, ... but ... understand why it is fine. When you declared
all members ofstruct details
all have automatic storage type and storage for each member is fully defined at that point. There is no need to callmalloc
anywhere in your function. When you returnd
at the end,struct
assignment allows the function to return the struct and have all values assigned and available back inmain()
.Finally in
main()
, you callread_person();
but fail to assign the return or make use of the stored values ind
in any way.Instead of creating a global array of struct, simply declare the struct itself, e.g.:
Then for your
read_person(void)
function, eliminate the calls tomalloc
and simply do:(note: you do not want the line-ending
'\n'
left at the end of each of the names, so you need to overwrite the trailing'\n'
with the nul-character'\0'
(or equivalently0
). While there are several ways to do this, the use ofstrcspn
is probably one of the most robust and simplest ways to do this.strcspn
returns the number of characters in the string NOT included in the exclude set. So simply have your exclude set include the line-ending"\n"
and it returns the number of characters in the string up to the'\n'
which you then simply set to0
)(also note: the use of
void
instruct details read_person (void)
to specify thatread_person
takes no arguments. In C if you simply leave empty()
, then the function takes an indeterminate number of arguments)Then in
main()
assign the return and use it in some fashion, e.g.You other alternative is to declare your struct in
main()
and pass a pointer to yourread_person
function for filling. Simply declare a struct inmain()
and then pass the address of the struct toread_person
, but note that with a pointer to struct you use the->
operator to access members rather than'.'
. For example you could do:And finally, since you did include
mailloc
, you may as well learn how you could have used it to allocate storage for the struct, as well as eachforename
andsurname
so that both use exactly the correct number of bytes to hold the names entered and no more. When allocating storage for anything you have 3 responsibilities regarding any block of memory allocated: (1) always validate the allocations succeeds before making use of the block of memory, (2) always preserve a pointer to the starting address for the block of memory so, (3) it can be freed when it is no longer needed.That will add a few repetitive, but important, lines of code wherever you dynamically allocate storage. For example in this case where you dynamically allocate for the struct and for
forename
andsurname
within the struct, your struct declaration and function, could be:(note: the use of
memcpy
rather thanstrcpy
. You have already scanned for the end-of-string withstrcspn
to obtain the number of characters in the string and then nul-termianted the string at that point. There is no need to scan for end-of-string again withstrcpy
, just copy the number of characters (+1 to also copy the nul-terminating character) withmemcpy
.Try to see if you can understand why the
free()
function is include in the function above and what purpose it is serving.Putting a complete example together that dynamically allocates, you could do something similar to the following:
(note: all memory is freed before the program exits. Understand that the memory would be freed automatically on exit, but if you make a habit of always taking care of your 3-responsibilities concerning dynamically allocated memory, you will never have problems leaking memory later on as your code size grows.)
Example Use/Output
All of the examples produce the same output. An example would be:
Memory Use/Error Check
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux
valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Handling qsort of Array of struct details
After handling all of the initial problems with your use of
struct details
, I came close to forgetting that your original question related toqsort
.qsort
is simple to use, you just pass the array, the number of members to sort, the size of each member, and a compare function that returns-1, 0, 1
based on whether the first element sorts before, is equal to, or sorts after the second element passed to the function.Your only responsibility using
qsort
is to write thecompare
function. While new users toqsort
usually have their eyes roll back in their heads when they see the declaration for the function:It's actually quite simple.
a
andb
are simply pointers to elements of the array to compare. So if you have an array ofstruct details
. Thea
andb
are just pointers tostruct details
. You simply need to write yourcompare
function to casta
andb
to the appropriate type.To sort on
forename
, you compare function could be:To sort on
surname
, you would have:Then within
main()
you simply declare an array ofstruct details
and callqsort
, e.g.Or, putting it altogether in a full example you would have:
Example Use/Output
(note: since
Mickey
andMinnie
both have last nameMouse
, for the sort bysurname
they are then further sorted byforname
so there is a correct cannonical sort in the list above)Now, hopefully, we have address all the aspects of your question. Look things over and let me know if you have further questions.