I have the following structure in my .c file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Student {
char* name;
int age;
int id;
} Student;
Student* addStudent(int age, char* name, int id) {
Student* student = (Student*)malloc(sizeof(Student));
if (!student) return NULL;
printf("The base address of the student block is %p\n", student);
printf("The ending address of the student block is %p\n", (char*)student + sizeof(Student));
student->age = age;
student->id = id;
student->name = (char*)malloc(strlen(name) + 1);
if (!student->name) {
free(student);
return NULL;
}
strcpy(student->name, name);
printf("A new student is created.\n");
return student;
}
I'm trying to understand how to calculate the starting and ending address of a dynamically allocated struct in C.
The starting address is clear — it's the value stored in the pointer returned by malloc
.
However, I'm confused about how to calculate the ending address of the structure. I came across this line:
printf("The ending address of the student block is %p\n",
(char*)student + sizeof(Student));
But I don't understand why (char*)student + sizeof(student)
is used here. It seems to be operating on the pointer itself, not on the memory block it points to.
This line wasn’t originally mine — I found it online, but the explanation was not very clear. Could someone clarify the logic behind this and how to correctly calculate the ending address of a dynamically allocated structure in C? Thank you for your time.
The answer is different depending on what you mean by "end address". The answer you have accepted from dbush doesn't give you the end address of student
but what would be the start address of the next Student
if you had created an array of more than one of them.
When the compiler calculates the size of a struct
and the offsets of each member within a struct
, it will add padding so that each member is correctly aligned for the architecture of the target processor. It also needs to take into account that you might have an array of your struct
and therefore it will add padding to the end of it to ensure that the first item is always correctly aligned.
Consider the following declaration
struct Foo
{
char* a;
char b;
int c;
char d;
}
On an LP64 architecture, a
takes 8 bytes, b
takes 1 byte, c
takes 4 bytes and d
takes 1 byte, so sizeof(struct Foo)
is 14, right? Nope. On my Mac, it's 24 bytes. The compiler has added 3 bytes of padding after b
so that c
(a 32 bit int) can be 4 byte aligned and it has added 7 bytes of padding after d
so that the next instance of a struct Foo
in an array has its a
8 byte aligned (pointers on my Mac are 64 bits).
Note that malloc()
always returns a pointer that is aligned for any type. Typically, it will be at least 16 byte aligned.
So the end address of a struct
depends on what's in it. If you want the last address in the struct
including any padding (char*)(aFoo + 1) - 1
will do it. If you want the address of the last member, &aFoo.d
is the right thing in this case. If you really want the first byte not in the struct
which is almost always the case, as dbush has already said &aFoo + 1
is correct, since pointer arithmetic is always done in units of the size of the type being pointed to.