Reputation: 5297
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
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
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
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
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