Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

Using malloc() and realloc() for a variable-length string

realloc malloc string

  • Please log in to reply
6 replies to this topic

#1 DarkLordCthulhu

DarkLordCthulhu

    CC Devotee

  • Senior Member
  • PipPipPipPipPipPip
  • 422 posts
  • Location:The bash shell
  • Programming Language:C, JavaScript, Bash, Others
  • Learning:Ruby, Others

Posted 17 March 2011 - 12:07 PM

Please read this. I'm having a very dire problem understanding something very basic in C, and I can't find an answer anywhere.

I have a problem with dynamic memory management. Let's say I have a trivial command shell, call it tsh, that takes a command from the user and then echoes it back, and continues doing that until the user hits ^C.

Here's the problem. I need to create a character array and have it be of a variable length, since I don't know the length of the input. I need to allocate the memory for it dynamically, and not allow for memory overflow.

Here are three trivial programs I've written. I haven't tested any of them, but I know they wouldn't work.

Program 1:
#include <stdio.h>

int main( int argc, char **argv ){
	char *cmd;
	for(;;){
		cmd = fgets( stdin );
		printf( "%s\n", cmd );
	}
	return 0;
}

This code creates a string variable cmd and gets its value from the user through fgets( stdin ). This obviously won't work, because cmd is not dynamically allocated, so its length can not be changed.

Program 2:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main( int argc, char **argv ){
	char *cmd = (char *) malloc( 8 );
	for(;;){
		cmd = realloc( cmd, strlen( (cmd = fgets( stdin )) ) );
		printf( "%s\n", cmd );
	}
	return 0;
}

This program dynamically allocates memory for the string. Every time the user inputs a string, the amount of memory allocated changes to the length of the input. So I have to do three things:

1. Set cmd to fgets( stdin )
2. Find the length of fgets( stdin )
3. Reallocate memory.

Here I did it in the order 1, 2, 3. The problem with this is that cmd is set to fgets( stdin ) before its memory is reallocated, so it won't be the right size. Thus Step 3 has to come before Step 1. Step 3 can't be completed unless I know the length of the string, so the order has to be 2, 3, 1. I can't do Step 2 until I have an input to get the length of. Thus, Step 1 must come before Step 2. It's a chicken and egg problem.

Program 3:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main( int argc, char **argv ){
	char *cmd = (char *) malloc( 8 );
	for(;;){
		cmd = realloc( cmd, 1024 );
		cmd = fgets( stdin );
		cmd = realloc( cmd, strlen(cmd) );
		printf( "%s\n", cmd );
	}
	return 0;
}

This program first sets the amount of memory allocated to a really high value, gets the input, and then reallocates it to reduce the amount of memory to the length of the input. The problem with this is a. too much overhead from the size of the initial block of memory, and b. the contents of the larger block are not necessarily copied to the smaller block, so loss of data is likely.

So yeah, there seems to be no logical way to create a variable-length string and set its length according to input. Is there any way to do this?

Please help me. This is very important!
  • 0
Programming is a journey, not a destination.

#2 rocketboy9000

rocketboy9000

    CC Resident

  • Advanced Member
  • PipPipPipPip
  • 79 posts

Posted 17 March 2011 - 12:29 PM

There's no way to do it in one line, you need a loop.
#include "stdio.h"
#include "string.h"
#include "stdlib.h"

int main(){
	int mem=64;
	char *str=malloc(mem);
	fgets(str,mem,stdin);
	while(str[strlen(str)-1]!='\n'){//checks if we ran out of space
		mem*=2;
		str=realloc(str,mem);//double the amount of space
		fgets(str+mem/2-1,mem/2+1,stdin);//read the rest (hopefully) of the line into the new space.
	}
	printf("%s",str);
}
EDIT: added some comments so you can tell what's going on
  • 0

#3 DarkLordCthulhu

DarkLordCthulhu

    CC Devotee

  • Senior Member
  • PipPipPipPipPipPip
  • 422 posts
  • Location:The bash shell
  • Programming Language:C, JavaScript, Bash, Others
  • Learning:Ruby, Others

Posted 17 March 2011 - 12:53 PM

Perhaps I'm misunderstanding what fgets() does, but when you use fgets() with stdin twice, wouldn't it prompt twice? Or does it just prompt once and read until it gets to the \n (presumably the user pressing enter)?

I also don't understand this part:
fgets(str+mem/2-1,mem/2+1,stdin);//read the rest (hopefully) of the line into the new space.

Why use str+mem/2-1? Aren't you adding a char pointer to an integer?

EDIT: Actually, I understand, because str is a pointer, so you're moving the place for fgets to start appending characters to up to the end of the string.
  • 0
Programming is a journey, not a destination.

#4 rocketboy9000

rocketboy9000

    CC Resident

  • Advanced Member
  • PipPipPipPip
  • 79 posts

Posted 17 March 2011 - 01:34 PM

Yup, pointers are really just another kind of integer.
  • 0

#5 DarkLordCthulhu

DarkLordCthulhu

    CC Devotee

  • Senior Member
  • PipPipPipPipPipPip
  • 422 posts
  • Location:The bash shell
  • Programming Language:C, JavaScript, Bash, Others
  • Learning:Ruby, Others

Posted 17 March 2011 - 06:33 PM

One thing I still don't understand: Your program subtracts 1 from mem/2, which means if you read 64 bytes in, then you move up to the 64th byte in the array. Wouldn't this overwrite the 64th character?

EDIT: I noticed that when I set mem to 8 initially, then had it print every character in the string after the initial fgets(), it only printed the first 7 characters rather than the first 8. This would explain the subtraction. I'm guessing that the 8th character is a null character, which is the only logical explanation I can think of for why it does that.

EDIT: I was right. It is a null character. I just tested it by having it print the ASCII value, which came out to 0.
  • 0
Programming is a journey, not a destination.

#6 DarkLordCthulhu

DarkLordCthulhu

    CC Devotee

  • Senior Member
  • PipPipPipPipPipPip
  • 422 posts
  • Location:The bash shell
  • Programming Language:C, JavaScript, Bash, Others
  • Learning:Ruby, Others

Posted 17 March 2011 - 07:33 PM

I think I've had I/O in C mixed up for all these months I've been learning it. I always thought scanf() meant "Prompt the user for a value." when in reality it means "Read the next value from stdin." Similarly, fgets( str , n, stdin ) isn't an input prompt, but rather a command to read the next n characters from stdin. It does not prompt the user for a new string unless it reaches \n, because that is the end of the current line in stdin. I always had a vague idea of what file descriptors were, but I never realized how important they were until now.
  • 0
Programming is a journey, not a destination.

#7 rocketboy9000

rocketboy9000

    CC Resident

  • Advanced Member
  • PipPipPipPip
  • 79 posts

Posted 17 March 2011 - 09:04 PM

Good, you figured all that out properly! ^_^
  • 0





Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download