Am I freeing memory properly? - c

Okay, so I have a homework assignment for a C programming class and I just finished with the output doing what I expected. However, I am still a bit confused on memory allocation and freeing.
Basically what has me question my self is the freeing of the structure memory and the "Change_name" function. In my program I am just taking the new name and setting the value who.name to the new name. But what happens to the "old" name in this scenario? when I call free(who), is the old name being deleted?
any clarification would be appreciated!
code:
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
struct Person {
char *name;
int age;
int height;
int weight;
};
/* complete this function, which initialize the fileds of the struct, and return a pointer to the initialzied struct */
struct Person *Person_create(char *name, int age, int height, int weight)
{
struct Person *who = malloc(sizeof(struct Person));
assert(who != NULL);
(*who).name = name;
(*who).age = age;
(*who).height = height;
(*who).weight = weight;
return who;
}
/* complete this function, which free memory that was allocated for a struct*/
void Person_destroy(struct Person *who)
{
assert(who != NULL);
free(who);
}
/* complete this function, which print the value of member of struct for the input argument */
void Person_print(struct Person *who)
{
printf("This person have the values of...\n");
printf("name: %s\n", (*who).name);
printf("age: %d\n", (*who).age);
printf("height: %d\n", (*who).height);
printf("weight: %d\n", (*who).weight);
}
/* complete this function, which change the value of filed member of the struct to the value of newName */
void Change_name (struct Person *who, char * newName)
{
(*who).name = newName;
}
int main(int argc, char *argv[])
{
// make two people structures
struct Person *joe = Person_create(
"Joe Alex", 32, 64, 140);
struct Person *frank = Person_create(
"Frank Blank", 20, 72, 180);
// print them out and where they are in memory
printf("Joe is at memory location %p:\n", joe);
Person_print(joe);
printf("Frank is at memory location %p:\n", frank);
Person_print(frank);
// make changes in filed of goe's struct print them again
joe->age += 20;
joe->height -= 2;
joe->weight += 40;
Change_name(joe, "Jack The third Junior Smith Benedickt");
Person_print(joe);
// destroy them both so we clean up
Person_destroy(joe);
Person_destroy(frank);
return 0;
}

Your call to Person_destroy doesn't free any of the names because you're just freeing who. But that's OK because you're also not dynamically allocating any of the names with malloc/strdup/etc...

TL;DR: For your specific example: yes, but there are caveats to what you're doing.
One question that needs addressing WRT change_name is this:
ut what happens to the "old" name in this scenario? when I call free(who), is the old name being deleted?
For reasons that, I hope, will become clear further down, there is no clear answer to this question. Your code assigns a char * blindly. You don't know where that string is stored. If it's a string constant with static storage (either global variable or const char * in main), that old name will remain in memory for as long as your application runs. If it's dynamically allocated, unsetting a pointer doesn't free the memory either. assigning a new pointer can potentially cause you to leak memory. The safest way is to copy the string (strdup), and free the pointer prior to changing the name field.
There's an underlying problem here: You can only free memory in the right way if you allocate it correctly. Strictly speaking, you are doing just that. However, a function that takes a char * shouldn't blindly assign that same pointer. The pointer might be a stack char[] that decayed into a pointer (because it was passed as an argument).
You have no idea where that string was allocated, how, and most importantly: how long the pointer will remain valid. A couple of scenario's where a char * can cause problems:
int main ( void )
{
struct Person *p = foobar();
printf("name => %s?", p->name);
Person_destroy(p);
return 0;
}
struct Person *foobar( void )
{
char bar[] = "this is a local string";
return Person_create(bar, 32, 64, 140);
}
The pointer to bar expires once foobar returns, so this is a problem (stack memory pointers).
A pointer on the heap might suffer from the same problem:
struct Person *foobar( void )
{
const char *x = "Name";
char *bar = strdup(x); // allocates on heap and copies string
// check for null's etc...
struct Person *person = Person_create(bar, 32, 64, 140);
//some more stuff happens, including:
bar = realloc(bar, strlen(x) + 255);
strncat(bar, " has been successfully allocated", 33);
return person;
}
The problem here is that realloc might memmove the original string, and return an entirely new pointer, in which case the name field of the struct will become invalid. If that doesn't happen, person->name now points to Name has been successfully allocated, which is a potential bug.
So I strongly advise you to copy the name string:
// note: const char *name
struct Person *Person_create(const char *name, int age, int height, int weight)
{
struct Person *who = malloc(sizeof *who); // shorter to write, more reliable
if (who == NULL)
exit(1);// or whatever
who->name = strdup(name); // create copy
//etc...
return who;
}
This means, of course that struct Person will need to free the name pointer:
void Person_destroy(struct Person *who)
{
free(who->name);
free(who);
}
Double indirection is a bit risky a lot of the time, but imagine someone doing something like this:
int main( void )
{
struct Person *p = Person_create("Name", 1, 2, 3);
//do stuff
Person_destroy(p);
// more stuff, eg:
printf("%p\n", (void *)p);
Person_destroy(p);
return 0;
}
This is not good,. p should be null'ed after freeing it. Freeing an invalid pointer is bad, mkay. 2 ways to make this a less common problem:
#define FREE_PERSON(p) do {\
Person_destroy(p);\
p = NULL;\
} while(0);
This macro will always set the person variable to NULL after calling Person_destroy. The downside is: it's a clunky macro, and people can (and will) bypass it.
Change Person_destroy a bit:
void Person_destroy(struct Person **p)
{
if (p == NULL)
return; // this is needed now
struct Person *tmp = *p;
free(tmp->name);
free(tmp);
*p = NULL; // set the pointer itself to NULL
}
This forces people to call Person_destroy with a pointer to their pointer, and automatically sets their pointer to NULL.
Again, good practice requires devs to do this themselves, but it's a trivial change and helps prevent problems over time.
Demo using the double-indirection approach

Related

using free() function causes runtime error

I have a struct called Person, that contains two attributes - first and last name.
After successfully dynamic allocation of memory for a variable of Person type, giving values to the attributes I would like to free the memory, but I keep getting a runtime error (the program window just crashes)
this it the code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char firstName[15];
char lastName[15];
} Person;
void main(){
int len = 0;
char firstName[]="danny", lastName[]="johnes";
Person *temp = (Person*)malloc(sizeof(Person));
if (temp == NULL)
return;
len = strlen(firstName);
temp->firstName[len] = (char*)malloc(sizeof(char)*(len));
if (temp->firstName == NULL)
return;
strcpy(temp->firstName, firstName);
len = strlen(lastName);
temp->lastName[len] = (char*)malloc(sizeof(char)*(len));
if (temp->firstName == NULL)
return;
strcpy(temp->lastName, lastName);
freePerson(temp);
system("pause");
return;
}
This is the function I use to free the memory:
void freePerson(Person* ps) {
if (ps != NULL) {
free(ps->firstName);
free(ps->lastName);
free(ps);
}
}
All I want the code to do - is to store the name in a dynamically allocated structure, and free it.
Later on, I plan to replace the hard-coded names with values inputed from file.
Any ideas about the error? Thank you.
You have already space allocated for firstName, so you have to copy the name within the size constraits (15 bytes). You can do this best with snprintf like this:
snprintf(temp->firstName, sizeof(temp->firstName), "%s", firstName);
Same goes for lastName. Mind that both might be truncated if the length exceeds the size of the field.
The other option is to allocate the fields dynamically. Then your struct members should be pointers, not char arrays:
typedef struct {
char *firstName;
char *lastName;
} Person;
You can then allocate and assign the names like this:
temp->firstName = strdup(firstName); // (same for lastName)
But mind that you have to free these fields seperately if you want to free the whole item.
If you don't want to specify a maximum size for the names in the structure, you need to declare them as pointers, not arrays.
typedef struct {
char *firstName;
char *lastName;
} Person;
Then you should assign the result of malloc() to the member, without indexing it. You also need to add 1 to strlen(firstName), to make space for the null terminator.
temp->firstName = malloc(strlen(firstName)+1);
if (temp->firstName == NULL) {
return;
}
strcpy(temp->firstName, firstName);
This is how I would write this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define FIRSTNAME_MAXLEN 15
#define LASTNAME_MAXLEN 15
typedef struct
{
char firstName[FIRSTNAME_MAXLEN+1];
char lastName[LASTNAME_MAXLEN+1];
} person_t;
void freePerson(person_t *ps) {
if (ps) {
free(ps); ps=NULL;
}
}
int main(){
const char *firstName="danny";
const char *lastName="johnes";
person_t *temp = calloc(1, sizeof(person_t));
if (!temp) return 1;
strncpy(temp->firstName, firstName, FIRSTNAME_MAXLEN);
strncpy(temp->lastName, lastName, LASTNAME_MAXLEN);
printf("test: firstname: %s\n", temp->firstName);
printf("test: lastname: %s\n", temp->lastName);
freePerson(temp);
return 0;
}
You allocate enough room on the heap and cleanup things with calloc(), then you copy your string with strncpy() limiting to the bytes reserved and avoiding buffer overflow. At the end you need to free() the memory returned by calloc().
Since you allocated char firstName[] and char lastName[] inside your struct you don't need to reserve other memory with malloc() for those members, and you also don't need to free() them.
At least 5 issues:
To duplicate a string, insure allocation includes enough room for the characters including the null character.
Otherwise the strcpy() writes outside the allocation which is undefined behavior (UB).
len = strlen(firstName);
// temp->firstName[len] = (char*)malloc(sizeof(char)*(len ));
temp->firstName = (char*)malloc(sizeof(char)*(len + 1));
// + 1
...
strcpy(temp->firstName, firstName);
Same for lastName.
Also assign to the pointer, not the char. #Barmar
Person members are arrays. For dynamic allocation, they should be pointers. #NthDeveloper
typedef struct {
// char firstName[15];
// char lastName[15];
char *firstName;
char *lastName;
} Person;
2nd test is wrong
// if (temp->firstName == NULL)
if (temp->lastName == NULL)
int vs. size_t.
int len = 0; assumes the string length fits in a int. Although this is exceedingly common, the type returned from strlen() is size_t. That unsigned type is right-sized for array indexing and sizing - not too wide, not too narrow. Not a key issue in this learner code.
// int len = 0;
size_t len = 0;
Tip: cast not needed. Allocate to the referenced object, not the type. Easier to code right, review and maintain.
// Person *temp = (Person*)malloc(sizeof(Person));
Person *temp = malloc(sizeof *temp);
// temp->firstName[len] = (char*)malloc(sizeof(char)*(len + 1));
temp->firstName = malloc(sizeof *(temp->firstName) * (len + 1));
Tip: Although not C standard, many platforms provide strdup() to allocated and copy strings. Sample strdup() code.
temp->firstName = strdup(firstName);
Tip: Likely the most valuable one: A good compiler with warnings well enabled should have warned about temp->firstName[len] = (char*)malloc(sizeof(char)*(len)); as it is a questionable type mis-match in the assignment. These warnings save you and us all time. Insure your next compilation has all warning enabled.

Storing void pointers in a struct

I'm getting segmentation fault on code that is trying to initialize a struct of pointers to 0mq context and socket. The commented out code in the main method works, but it's only using local variables. I would like to initialize them and pass them around in a struct, but my google foo is failing me on how to do this properly.
#include "zhelpers.h"
#include <stdio.h>
#include <stdlib.h>
#include <zmq.h>
struct publisher{
void *handle;
void *context;
};
void init_publisher(struct publisher *p);
void destroy_publisher(struct publisher *p);
void publish(struct publisher *p,char *msg);
void init_publisher(struct publisher *p)
{
p = (struct publisher *)malloc(sizeof(struct publisher));
p->context = malloc(sizeof(void *));
p->handle = malloc(sizeof(void *));
void *context = zmq_ctx_new();
void *handle = zmq_socket(context,ZMQ_PUB);
zmq_bind(handle, "tcp://*:5556");
zmq_bind(handle, "ipc://feed.ipc");
p->context = context;
p->handle = handle;
}
void destroy_publisher(struct publisher *p)
{
zmq_close(p->handle);
zmq_ctx_destroy(p->context);
free(p->handle);
free(p->context);
free(p);
}
void publish(struct publisher *p,char *msg)
{
s_send(p->handle, msg);
}
int main(void)
{
/**
void *context = zmq_ctx_new();
void *publisher = zmq_socket(context, ZMQ_PUB);
int rc = zmq_bind(publisher, "tcp://*:5556");
assert(rc == 0);
rc = zmq_bind(publisher, "ipc://weather.ipc");
assert(rc == 0);
printf("Started Weather Server...\n");
srandom((unsigned) time (NULL));
int zipcode, temperature, relhumidity;
zipcode = randof(100000);
temperature = randof (215) - 80;
relhumidity = randof (50) + 10;
char update[20];
sprintf(update, "%05d %d %d", zipcode, temperature, relhumidity);
s_send(publisher, update);
zmq_close(publisher);
zmq_ctx_destroy(context);
*/
struct publisher *p;
init_publisher(p);
printf("Setup pub\n");
srandom((unsigned) time (NULL));
int zipcode, temperature, relhumidity;
zipcode = randof(100000);
temperature = randof (215) - 80;
relhumidity = randof (50) + 10;
char update[20];
sprintf(update, "%05d %d %d", zipcode, temperature, relhumidity);
publish(p,update);
printf("Published Message\n");
destroy_publisher(p);
printf("Destroyed publisher\n");
return 0;
}
There appears to be nothing in this code that would make it crash. (Assuming you know how all your zmq_... stuff works.)
It would have helped if you told us precisely where the error occurs, but my guess would be that the error occurs outside of this code.
You see, you are passing struct publisher *p to your init_publisher() function, but then you are allocating memory for p inside that method, (which makes passing p pointless,) and then you are not returning p. As a result, the code that calls init_publisher() probably expects p to be initialized, but it is not. The memory pointed by p is just allocated and leaked locally within your init_publisher() function.
So, instead of passing p, just have the function declare it and return it.
Alternatively, if the caller has already allocated p, then do not allocate it all over again from within init_publisher().
Please also note that the statements p->context = malloc(sizeof(void *)); are unnecessary and they are leaking small amounts of memory, because you proceed to overwrite these struct members.
The problem is that the passed pointer and the pointer you malloc()ed are not the same. The passed pointer contains the same address of your original pointer, presumably an invalid address, but the addresses of the poninters them selves are different because in c you can only pass a variable by value and hence, the pointer is copied.
That means that when you reassign p inside the function, the p from outside the function is unaltered. It would be different if it was allocated outside and you just use the function to access it's members.
You also don't need to malloc() every pointer you want to use, the thing is that it must point to a valid address before dereferencing it. When you want to request new uninitialized memory then you use malloc() otherwise you just make the pointer point to a valid address so that dereferencing it is defined, one example of using a pointer without malloc()ing it is
int *pointer;
int value;
value = 4;
pointer = &value; // Now `pointer' points to `value's` address
*pointer = 3;
printf("%d\n", value);
One way to write the function would be
int
init_publisher(struct publisher **pp)
{
struct publisher *p;
*pp = malloc(sizeof(struct publisher));
if (*pp == NULL)
return -1;
p = *pp;
p->context = zmq_ctx_new();
p->handle = zmq_socket(context,ZMQ_PUB);
if (p->handle != NULL) /* Just in case, do not dereference a NULL pointer */
{
zmq_bind(p->handle, "tcp://*:5556");
zmq_bind(p->handle, "ipc://feed.ipc");
}
return 0;
}
and then you could use it like this
struct publisher *p;
if (init_publisher(&p) != 0)
do_something_there_was_an_error();
/* Continue using `p' */
Note that the funcion is returning a value indicating whether allocations succeeded or not. Normally malloc() will not fail, but that doesn't mean that you should ignore the possible failure.
What I mean when I say if you allocate p first, is that if you instead do this
struct publisher *p;
p = malloc(sizeof(*p));
if (p == NULL)
return handle_error();
init_publisher(p);
then init_publisher() could be
void
init_publisher(struct publisher *pp)
{
void *context;
void *handle;
p->context = zmq_ctx_new();
p->handle = zmq_socket(context,ZMQ_PUB);
if (p->handle != NULL) /* Just in case, do not dereference a NULL pointer */
{
zmq_bind(p->handle, "tcp://*:5556");
zmq_bind(p->handle, "ipc://feed.ipc");
}
}
which is probably what you was trying to do.

Duplicating strings passed to functions as parameters in C

I couldn't think of a proper title to my question so here it goes. I am trying to learn C and the following code is from the tutorial I am following.
struct Person {
char *name;
int age;
int height;
int weight;
};
struct Person *Person_create(char *name, int age, int height, int weight){
struct Person *who = malloc(sizeof(struct Person));
assert(who != NULL);
who->name = strdup(name);
who->age = age;
who->height = height;
who->weight = weight;
return who;
}
void Person_destroy(struct Person *who){
assert(who != NULL);
free(who->name);
free(who);
}
int main(int argc, char *argv[]){
struct Person *joe = Person_create("Joe Alex", 32, 64, 140);
........
My question is in Person_create function why are we duplicating name to a new memory location for who->name . Why can't we just make who->name point to the same location provided by the *name supplied to the function.
Also if we directly assigned the address of *name to who->name do we have to free it in Person_destroy.
Why can't we just make who->name point to the same location provided by the *name supplied to the function.
For me this who->name = strdup(name); is better than this who->name = name; if i know i will modify the string pointed by name later somewhere.
So you might as well do this:
who->name = name;
However a string literal like "Joe Alex" is in a read-only location - so if you wanted to do something like this (later in some part of your code):
who->name[3] = 'x';
you would get segmentation fault. So if you want to modify it you would like to malloc some writable space from heap which strdup does for you.
You might want to have a look at: Modifying String Literal
The char array *name is not an allocated array, that means if you leave your function scope, this array is not usable anymore. So the tutorial copies it in order to do operations later on this variable.
Moreover, if you directly assigned your variable *name to who->name you must not free it because it was not returned by malloc.

memcpy of structure having pointers as members in C

I have a structure with some pointers as members and I am trying to do memcpy and I have been suggested that I should not use memcpy in this case as memcpy do a shallow copy (meaning it copies pointers) rather deep copy (meaning copying what pointers point to).
But I am not sure why it is not making any difference in the following program:
Please have a look at code and output and please explain why it is not a shallow copy in this case?
#include <stdio.h>
#include <malloc.h>
#include <string.h>
struct student {
char *username;
char *id;
int roll;
};
void print_struct(struct student *);
void print_struct_addr(struct student *);
void changeme(struct student *);
int main (void) {
struct student *student1;
char *name = "ram";
char *id = "200ABCD";
int roll = 34;
student1 = (struct student *)malloc(sizeof(struct student));
student1->username = name;
student1->id = id;
student1->roll = roll;
print_struct_addr(student1);
print_struct(student1);
changeme(student1);
print_struct(student1);
print_struct_addr(student1);
return 0;
}
void print_struct(struct student *s) {
printf("Name: %s\n", s->username);
printf("Id: %s\n", s->id);
printf("R.No: %d\n", s->roll);
return;
}
void print_struct_addr(struct student *s) {
printf("Addr(Name): %x\n", &s->username);
printf("Addr(Id): %x\n", &s->id);
printf("Addr(R.No): %x\n", &s->roll);
return;
}
void changeme(struct student *s) {
struct student *student2;
student2->username = "someone";
student2->id = "200EFGH";
student2->roll = 35;
print_struct_addr(student2);
memcpy(s, student2, sizeof(struct student));
student2->username = "somebodyelse";
return;
}
output:
Addr(Name): 9b72008
Addr(Id): 9b7200c
Addr(R.No): 9b72010
Name: ram
Id: 200ABCD
R.No: 34
Addr(Name): fa163c
Addr(Id): fa1640
Addr(R.No): fa1644
Name: someone
Id: 200EFGH
R.No: 35
Addr(Name): 9b72008
Addr(Id): 9b7200c
Addr(R.No): 9b72010
If memcpy does a shallow copy, how come student1->username is NOT "somebodyelse".
Please explain in which scenario, this code can create problem, I want student2 information in student1 after changeme() call in main() and should be able to use this modified student1 data afterwards.
I have been suggested to NOT to use memcpy() here, but it seems to be working fine.
Thanks
This is the modified code: But still I dont see concept of shallow copy here:
#include <stdio.h>
#include <malloc.h>
#include <string.h>
struct student {
char *username;
char *id;
int roll;
};
void print_struct(struct student *);
void print_struct_addr(struct student *);
void changeme(struct student *);
int main (void) {
struct student *student1;
char *name = "ram";
char *id = "200ABCD";
int roll = 34;
student1 = malloc(sizeof(*student1));
student1->username = name;
student1->id = id;
student1->roll = roll;
print_struct_addr(student1);
print_struct(student1);
changeme(student1);
print_struct(student1);
print_struct_addr(student1);
return 0;
}
void print_struct(struct student *s) {
printf("Name: %s\n", s->username);
printf("Id: %s\n", s->id);
printf("R.No: %d\n", s->roll);
return;
}
void print_struct_addr(struct student *s) {
printf("Addr(Name): %x\n", &s->username);
printf("Addr(Id): %x\n", &s->id);
printf("Addr(R.No): %x\n", &s->roll);
return;
}
void changeme(struct student *s) {
struct student *student2;
student2 = malloc(sizeof(*s));
student2->username = strdup("someone");
student2->id = strdup("200EFGH");
student2->roll = 35;
print_struct_addr(student2);
memcpy(s, student2, sizeof(struct student));
student2->username = strdup("somebodyelse");
free(student2);
return;
}
This:
struct student *student2;
student2->username = "someone";
student2->id = "200EFGH";
student2->roll = 35;
Is writing into non-allocated memory, invoking undefined behavior. You need to make sure student2 is pointing at somewhere valid, before writing.
Either allocate it, or use an on-stack instance since you're just going to copy from it anyway.
Of course, this entire business of initializing student2 and then overwriting s with it is needlessly complicated, you should just modify s directly.
Also, this:
student1 = (struct student *)malloc(sizeof(struct student));
is better written, in C, as:
student1 = malloc(sizeof *student1);
This removes the pointless (and potentially dangerous) cast, and makes sure the size is the proper one for the type, replacing a dependency checked by the programmer with one handled by the compiler.
Thirdly, it's a bit of a classic "symptom" of the beginning C programmer to not realize that you can assign structures. So, instead of
memcpy(s, student2, sizeof *s);
You can just write:
*s = *student2;
And have the compiler to the right thing. This might be a performance win, since the structure can contain a lot of padding which the assignment can be aware of and not copy, but which memcpy() cannot ignore.
That it works at all is a fluke. In your changeme() function you are creating a new pointer for student2, but you are not allocating the memory for it.
Secondly, in that same function you are changing student2 after you've copied it into s.
A shallow copy does not mean that any pointers within the copies are shared - it means that the values of the pointers themselves are also copied. So when you change student2->username after the memcpy it doesn't change the value of s->username.
As you progress, you also need to be more careful with the allocation of memory within those structures. AFAICR, if you use a constant literal string then the pointer will point at a chunk of statically initialised data within the program's memory space. However a more rigourous design would malloc() and free() dynamic memory for those elements. If you ever needed a statically initialised value you would use strdup() or similar to copy the string from the static space into heap memory.
You set the username to "somebodyelse" after copying. And that changes only the local copy inside the function "changeme()". Try printing out student2 inside "changeme()" and you will see what I mean.

C -- Structs and Pointers Basic Questions

So I'm trying to learn C right now, and I have some basic struct questions I'd like to clear up:
Basically, everything centers around this snippet of code:
#include <stdio.h>
#include <stdlib.h>
#define MAX_NAME_LEN 127
typedef struct {
char name[MAX_NAME_LEN + 1];
unsigned long sid;
} Student;
/* return the name of student s */
const char* getName (const Student* s) { // the parameter 's' is a pointer to a Student struct
return s->name; // returns the 'name' member of a Student struct
}
/* set the name of student s
If name is too long, cut off characters after the maximum number of characters allowed.
*/
void setName(Student* s, const char* name) { // 's' is a pointer to a Student struct | 'name' is a pointer to the first element of a char array (repres. a string)
char temp;
int i;
for (i = 0, temp = &name; temp != '\0'; temp++, i++) {
*((s->name) + i) = temp;
}
/* return the SID of student s */
unsigned long getStudentID(const Student* s) { // 's' is a pointer to a Student struct
return s->sid;
}
/* set the SID of student s */
void setStudentID(Student* s, unsigned long sid) { // 's' is a pointer to a Student struct | 'sid' is a 'long' representing the desired SID
s->sid = sid;
}
I've commented up the code in an attempt to solidify my understanding of pointers; I hope they're all accurate.
Also, I have another method,
Student* makeAndrew(void) {
Student s;
setName(&s, "Andrew");
setStudentID(&s, 12345678);
return &s;
}
which I'm sure is wrong in some way... I also think my setName is implemented incorrectly.
Any pointers? (no pun intended)
This is very wrong. If you insist on not using strcpy do something like this (not tested)
int iStringLength = strlen(name);
for (i = 0; i < iStringLength; i++) {
s->name[i] = name[i];
}
but make sure that the length is not longer than your array size.
This is also wrong
Student* makeAndrew(void) {
Student s;
setName(&s, "Andrew");
setStudentID(&s, 12345678);
return &s;
}
because the s object is destroyed when the function exits - it is local to the function scope and yet you return a pointer to it. So if you try to access the struct using this pointer it will not be valid as the instance no longer exists. If you want to do this you should dynamically allocate it using malloc . Alternatively do not return a pointer at all and use the alternative option of #Andrew .
In your "another method" you are locally declaring Student s, which will dynamically allocate space (usually on the stack) and you are returning that address on completion.
However, that stack-space will be released on the return, so there is no guarantee that the data is uncorrupted - in fact the likelyhood is that it will be!
Declare Student s in the call to your method, and pass the pointer to makeAndrew:
void makeAndrew(Student *s) {
setName( s, "Andrew");
setStudentID( s, 12345678);
}
...
Student s;
makeAndrew( &s );
...
Your function makeAndrew returns pointer to a local variable. It is only valid before the scope ends, so as soon as the function finishes, it will change when the memory gets overwritten - i. e. almost instantly. You would have to allocate it dynamically (using Student *s = new Student;, or if you really want to stick to pure C, Student *s = malloc (sizeof Student );, and then free it outside the function after it is not needed to avoid memory leak.
Or do it as Andrew suggested, it's less error-prone.
I would change the makeAndrew() function to just return a struct, not a pointer to a struct to correct the error with respect to returning a pointer to a temporary variable:
Student makeAndrew(void)
{
Student s;
setName(&s, "Andrew");
setStudentID(&s, 12345678);
return s;
}
Student aStudent = makeAndrew();
Your setName does have an error with respect to temp, which should be a char *, since you are incrementing it in your loop to point to another character in the input c-string. I think it was missing the null termination as well. And as you mention in your comment, there should be a check for overflow of the name char array in Student:
void setName(Student* s, const char* name) { // 's' is a pointer to a Student struct |
// 'name' is a pointer to the first element of a char array (repres. a string)
const char *temp;
int i;
for (i = 0, temp = name; *temp != '\0' && i <= MAX_NAME_LEN; temp++, i++)
{
*((s->name) + i) = *temp;
}
s->name[i] = '\0';
}
You can use strncpy to simplify setName:
void setName2(Student *s,const char *name)
{
#include <string.h>
strncpy(s->name, name,MAX_NAME_LEN);
s->name[MAX_NAME_LEN] = '\0';
}

Resources