Michi
Michi

Reputation: 5297

How to realloc another size with realloc

I have a program which takes as input a Name and if the input is less then the allowed size it will be saved in a Pointer.

If the input is bigger then allowed size then the realloc function will be used to satisfy the memory needed. The program at this point allocates only 6 bytes, 5 for name MICHI and 1 for '\0'. If the user types MICHAEL then the program allocates enough memory for that pointer to fit MICHAEL inside that pointer.

Here is the Program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct person{
    char *name;
}pers;

void addMem(void){
    unsigned int length = 6;
    size_t newLength = 0;
    unsigned int newSize = 0;
    unsigned int i =0;
    char *name;
    int c;

    name = malloc(lenght);
    if(name == NULL){
        exit(1);
    }
    newSize = length;

    printf("Enter your name:>  ");

    while ((c = getchar()) != '\n' && c!=EOF){
        name[i++]=(char)c;

        if(i == newSize){
            newSize = i+length;
            name = realloc(name, newSize);
        }
    }

    name[i] = '\0';

    newLength = strlen(name)+1;
    pers.name = malloc(newLength);

    memcpy(pers.name, name, newLength);

    free(name);
    name = NULL;
}

int main(void){
    addMem();

    printf("Your name is:>  %s\n",pers.name);
    free(pers.name);
    pers.name = NULL;
    return 0;
}

The Program works fine, but I need to make it somehow that realloc() will give only a maximum size of memory, because at this point it will realloc until User pres ENTER.

This means that i have 6 byte for Michi (5 + 1 for '\0') and the maximum size should be 8 (7 for Michael and 1 for '\0').

How can I do that?

EDIT:

I hope you understand now.

My program accept only names with 5 letters. At some point I decided that may program should accept names with max 10 Letters. I allocated already memory for 5 (michi). If you type Michael we have 7 letters so the program should allocate another two for Michael. But if i type Schwarzernegger, well this name is too long i do not accept it so i need to make my program to allocate memory only for michael.

Upvotes: 1

Views: 1597

Answers (3)

dbush
dbush

Reputation: 223992

Let's break down your main loop:

unsigned int length = 6;
unsigned int newSize = 0;

name = malloc(length);
...
newSize = length;

while ((c = getchar()) != '\n' && c!=EOF){
    name[i++]=(char)c;

    if(i == newSize){
        newSize = i+length;
        name = realloc(name, newSize);
    }
}

You start out allocating 6 bytes for name. That's good for 5 characters plus a NULL byte. Then for each character you read you add it to the end of name.

On each iteration, you check if the number of characters you entered is equal to the allocated size of the buffer. If so, you realloc the buffer and add 6 more bytes, so now it's 12 bytes long.

You then keep going, reading more characters. If you find that you've entered 12 characters, you realloc 6 more bytes, so now name is 18 bytes long.

This is a good approach. By doing it this way, you're allocating enough memory to hold whatever the user types in, with up to 5 extra bytes allocated. So there's no need to restrict the name to 7 bytes or any other specific number. You could add a check that says if the total number of characters is more than 7 bytes, print an error and exit, but there doesn't appear to be any good reason to do so.

EDIT:

If a name will never be longer than 7 bytes, you could do this instead of malloc and realloc for name:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct person{
    char *name;
}pers;

void addMem(void){
    unsigned int i = 0;
    char name[8];
    int c;

    printf("Enter your name:>  ");

    while ((c = getchar()) != '\n' && c!=EOF){
        name[i++]=(char)c;

        if(i == sizeof(name)){
            printf("name too long\n");
            pers.name = NULL;
            return;
        }
    }

    name[i] = '\0';

    pers.name = malloc(strlen(name)+1);
    strcpy(pers.name, name);
}

int main(void){
    addMem();

    printf("Your name is:>  %s\n",pers.name);
    free(pers.name);
    pers.name = NULL;
    return 0;
}

This way, your internal buffer that reads the name is a fixed size, and when you're done reading the name you still allocate just enough space to copy the string to pers.name.

Upvotes: 1

Enzo Ferber
Enzo Ferber

Reputation: 3094

Your calculation on how many memory you should get is wrong. (As @dbush said in a comment, it's fine since you're allocating memory in blocks of 6 bytes, which is more efficient than allocating by bytes)

Anyway, to change your behavior of allocating in blocks of 6 bytes to allocating in bytes, do this:

while ((c = getchar()) != '\n' && c != EOF) {
    name[i++] = (char) c;

    /* added IF requests on comments */
    if (i > 7) {
        printf("Names longer than 7 not allowed!\n");
        name[7] = 0x0;
        break;
    }
    else if (i >= length) 
        name = realloc(name, newSize);
}

Which will print:

$ ./draft
Enter your name:>  Michael
reallocing 7
reallocing 8
Your name is:>  Michael

$ ./draft
Enter your name:>  StackOverflow          
reallocing 7
reallocing 8
reallocing 9
reallocing 10
reallocing 11
reallocing 12
reallocing 13
reallocing 14
Your name is:>  StackOverflow

$ ./draft
Enter your name:>  Michi
Your name is:>  Michi

Read More

As dbush Answer says, you are better off allocating a big memory block and using realloc as few times as possible. The second link sustains that. It also suggests that you should use a second loop just to calculate the size you need, so you only need to call malloc once.

Upvotes: 1

ashiquzzaman33
ashiquzzaman33

Reputation: 5741

As Enzo's answer to stop after 7 letter you can use.

while ((c = getchar()) != '\n' && c != EOF) {
name[i++] = (char) c;

if (i >= length) {
    if(i+1<=7)
       name = realloc(name, i + 1);
    else
       {
          //generate error message
       }
}
}

Upvotes: 1

Related Questions