There are questions LIKE this one, but they are not similar enough to my specific question FOR ME to pick up on.
My question is about how to make a deep copy of a struct
I'm not sure what the original poster's understanding of a pointer is, but I know personally I had to internally stop thinking of them as "pointing" to anything and think of them as a "memory address of" something.
The code you have listed as making a shallow copy has a subtle (but potentially catastrophic) oversight.
In your main() function:
Student *s1 = create_Student("Bo","Diddly", 100, 221);
Student *s2 = create_Student("Leeroy","Jenkins",50,1337);
memcpy(&s2,&s1,sizeof(Student)); //shallow copy of s1 INTO s2?
The local (pointer/memory address) variables s1 and s2 are declared (on the stack):
s1 and s2 being pointers are memory addresses of student structs which happen to be allocated in heap memory due to the fact that your create_Student() function is using malloc() which allocates memory on the heap (heap meaning it will stick around even after create_Student() exits).
Putting an ampersand in front of s1 or s2 is like saying: "Give me the address of the address of my Student struct"
&s1 and &s2 now represent the memory locations (in the stack) of your s1 and s2 pointers (or memory addresses). In other words you are now 2 levels of pointer deep: a pointer to a (stack located) pointer to a (heap located) Student struct.
By specifying memcpy(&s2,&s1,sizeof(Student)) you have asked memcpy to overwrite the stack pointer s2 with the contents (or address) of stack pointer s1 as well as corrupt 24 more bytes of main()'s stack memory that immediately follows the 8 bytes starting at &s2 with the 24 bytes that immediately follows &s1. So to quote Anomie:
If you had the size right, that would be the same as s2 = s1;
So using the same logic of "needing to make a copy of what pointers point to" your copy_Student() DEEP copy might look like:
// I swapped the s1 and s2 arguments with
// target and source for clarity as well as their order
// to more closely mimic memcpy()
void copy_Student(Student *target, Student *source)
{
if (target!=NULL) free_Student(target); // if target is already allocated, free it...
assert(source != NULL);
target->grade = source->grade;
target->id = source->id;
target->last_name = (malloc((strlen(source->last_name) + 1) * sizeof(char)));
target->first_name = (malloc((strlen(source->first_name) + 1) * sizeof(char)));
strncpy(target->first_name, source->first_name, strlen(source->first_name) + 1);
strncpy(target->last_name, source->last_name, strlen(source->last_name) + 1);
}