1

EDIT: SOLVED. THANKS!

I have a function that returns a pointer to a struct I built. I have a problem that my code runs in debugging mode but crashes in normal mode, It crashes only when I'm trying to add kids to the person- starts at: if (kidsNumber>0){ I'm pretty sure it's initializing issue but I can't find where and I'm stuck. I tried to put flags and breakpoint and nothing helped.. Thank you!

Person* CreatePerson(){
    Person* newPerson;
    newPerson=(Person*)malloc(sizeof(newPerson));
    if (newPerson==NULL){
        return NULL;
    }

    InitPersonValues(newPerson);
    char tempName[MAX_NAME];
    int id;
    int kidsNumber;
    printf("Name:\n");
    scanf("%s",tempName);
    newPerson->name=(char*)malloc(1 + strlen(tempName));
    if (newPerson->name == NULL){
        return NULL;
    }
    strcpy(newPerson->name,tempName);

    printf("ID:\n");
    scanf("%d", &id);
    newPerson->id=id;

    printf("Num of kids:\n");
    scanf("%d",&kidsNumber);
    newPerson->numOfKids=kidsNumber;
    if (kidsNumber>0){
        newPerson->kids=malloc(kidsNumber*sizeof(char*));
        if (newPerson->kids==NULL){
            return NULL;
        }
        int i=0;
        for (i=0;i<kidsNumber;i++){
            strcpy(tempName,"");
            printf("Kid #%d name:\n",i+1);
            scanf("%s",tempName);
            newPerson->kids[i]=malloc(strlen(tempName)+1);
            if (newPerson->kids[i]==NULL){
                return NULL;
            }
            // printf("%s\n",tempName);
            strcpy(newPerson->kids[i],tempName);
            printf("%s\n",newPerson->kids[i]);
        }
    }
    return newPerson;
}
A. Lev
  • 11
  • 3
  • 1
    what's `tempName`, `newPerson`, `kidsNumber`? Presumably this should be `newPerson->kids=calloc(kidsNumber,sizeof(char*));` Presumably this should be `newPerson->kids[i]=)malloc((strlen(tempName)+1));` – yano Jan 05 '18 at 23:16
  • when you use malloc for a string, you must add 1 more char for the null character... Plus do not type cast malloc... – Grantly Jan 05 '18 at 23:16
  • The second `malloc` in your code should be `malloc(strlen(tempName) + 1)`. Don't forget that in C-Strings are `'\0'`-terminated, you have to allocate a space for them. – Pablo Jan 05 '18 at 23:18
  • I'm also not sure about your `calloc` call. Post your `Person` struct please – Pablo Jan 05 '18 at 23:19
  • 1
    @A.Lev oh god, can you please post that in your question, the comment section does not scale well with to many lines of codes. – Pablo Jan 05 '18 at 23:24
  • If creating a person fails, you are leaking memory. If the creation fails, free the memory you've already allocated. I would create a `freePerson` function that does that. – Pablo Jan 05 '18 at 23:48

2 Answers2

2

This code doesn't allocate enough memory for the terminating NUL character of the string copied:

newPerson->name=(char*)malloc(strlen(tempName)*sizeof(char));

A string contains strlen() char values, plus the terminating NUL.

Change that line to read

newPerson->name=malloc(1 + strlen(tempName));

Note that sizeof(char) is one by definition. And you do not need to cast the result of malloc() in C.

Or, if it's available, you can simply use strdup(). It's not standard C because it violates an unwritten "rule" regarding implicit allocation of memory that needs to be free()'d, but it works.

Also, as noted in the comments:

newPerson=(Person*)malloc(sizeof(newPerson));

is wrong as it allocates a block of memory the size of newPerson, a pointer, instead of the struct. Change it to

newPerson=malloc(sizeof(*newPerson));
Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
  • @Grantly *I think even scanf is deprecated now..* [Not at all:](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) **7.21.6.4 The `scanf` function** No mention of deprecation at all. – Andrew Henle Jan 05 '18 at 23:25
  • My mistake, it was *gets* – Grantly Jan 05 '18 at 23:30
  • 1
    @Grantly The entire `*scanf()` family of functions really have never been usable in robust code. They fail badly when fed unexpected data. – Andrew Henle Jan 05 '18 at 23:30
  • i tried it all and still doesnt work, now what happens is that when kids is 1 it lets me inser a name and then crashes. if kids is bigger than 1 it just crashes – A. Lev Jan 05 '18 at 23:33
  • @A.Lev You haven't provided complete code. If you're corrupting memory elsewhere, the error can cause a failure when you hit the corrupted memory from some other portion of your code. And if you corrupt memory allocated with `malloc()` or similar functions, any use of such a function - or `free()` - can result in a failure. – Andrew Henle Jan 05 '18 at 23:42
  • 1
    `malloc(sizeof(newPerson))` should be `malloc(sizeof(*newPerson))` – Barmar Jan 05 '18 at 23:44
  • @A.Lev This answer fixes some important problems. Please add in your Question (you can edit it many times), where exactly it crashes regarding Kids > 0 – Grantly Jan 05 '18 at 23:45
  • @Grantly that's mostly because people use `scanf` for the wrong purposes, like reading from the user. `scanf` should be use for (proper) formatted inputs, user input is random at best, it's not a surprise that `scanf` doesn't behave as "expected". Sadly most beginners guides and books do not explain properly what `scanf` really does and why it behaves the way it does. – Pablo Jan 06 '18 at 00:13
1

Please don't cast malloc and other functions of it's family. See Do I cast the result of malloc?

The best way of using a malloc is:

int *var = malloc(size * sizeof *var);

Avoid using sizeof(int*) in the arguments. It's easy to make mistakes, to forget a *, etc. If you later change the data type, you have to change the arguments as well. sizeof *var returns the correct size every time, regardless of the type (as long as var is a pointer).

Same goes for calloc

int *var = calloc(size, sizeof *var);

You see the problem?

newPerson->kids=(char**)calloc(kidsNumber,sizeof(char));

doesn't allocate enough space, it's easy to miss the * in the size argument for calloc.

newPerson->kids=calloc(kidsNumber, sizeof *newPerson->kids);

returns you the exact amount of memory.

Also,

newPerson->name=(char*)malloc(strlen(tempName)*sizeof(char));

Do not cast, don't use sizeof(char)

newPerson->name=malloc(strlen(tempName) + 1); // for strings

// or if you want to be 100% correct, but that's overkill
// since you know that you are requesting space for a C-String
newPerson->name=malloc((strlen(tempName) + 1) * sizeof *newPerson->name);

If possible, also use strdup if you want to clone a string. If you're environment doesn't have strdup, write one yourself:

char *strdup(const char *s)
{
    char *str = malloc(strlen(s) + 1);
    if(str == NULL)
        return NULL;
    strcpy(str, s);
    return str;
}

That will make your code more readable.


EDIT

One minor thing I noticed after re-reading your question.

If you notice that while creating a new Person something fails, don't just return NULL and be done with it. Free the memory you've already allocated.

I'd do something like this:

void DestroyPerson(Person *person)
{
    if(person == NULL)
        return;

    if(person->kids)
    {
        int i;
        for(i = 0; i < person->numOfKids; ++i)
            free(person->kids[i]);

        free(person->kids);
    }

    free(person);
}

// returns 1 on success, 0 otherwise
int InitPerson(Person *person)
{
    memset(person, 0, sizeof *person);

    // some other initializations
    ...
    return 1;
}

Person* CreatePerson() {
    Person *newPerson;

    newPerson=malloc(sizeof *newPerson);
    if (newPerson==NULL){
        return NULL;
    }

    // initializing kids
    if(!InitPerson(person))
    {
        DestroyPerson(person);
        return NULL;
    }

    ...

    if(some_error_detected)
    {
        DestroyPerson(newPerson);
        return NULL;
    }

    ...
}
Pablo
  • 13,271
  • 4
  • 39
  • 59