[comp.lang.c] Novice question about malloc and pointers

trotter@ENUXHA.EAS.ASU.EDU (Russell T. Trotter) (04/17/91)

I am trying to get an array of strings, therefore I am using the
following declaration:  char *str[MAX] where MAX is an arbitrary 
constant.  My question is how do I allocate the memory for each 
character position? Do all the characters strings for each element
in the array need to be allocated contiguously?  The problem involves
reading in lines of input.  Each line would be stored as a string and 
the number of lines make up the number of elements in the array.
Any information would be greatly appreciated.  Thank you.

trotter@enuxha.eas.asu.edu

gordon@osiris.cso.uiuc.edu (John Gordon) (04/18/91)

trotter@ENUXHA.EAS.ASU.EDU (Russell T. Trotter) writes:

>I am trying to get an array of strings, therefore I am using the
>following declaration:  char *str[MAX] where MAX is an arbitrary 
>constant.  My question is how do I allocate the memory for each 
>character position? Do all the characters strings for each element
>in the array need to be allocated contiguously?  The problem involves
>reading in lines of input.  Each line would be stored as a string and 
>the number of lines make up the number of elements in the array.
>Any information would be greatly appreciated.  Thank you.

	Here's how I did something similar:  use a temporary variable
such as char buf[200] for getting each line of input, then do a
str[i] = (char  *) malloc(strlen(buf) +1)
to copy each line into str.

---
John Gordon
Internet: gordon@osiris.cso.uiuc.edu        #include <disclaimer.h>
          gordon@cerl.cecer.army.mil       #include <clever_saying.h>

jfw@ksr.com (John F. Woods) (04/18/91)

trotter@ENUXHA.EAS.ASU.EDU (Russell T. Trotter) writes:
>I am trying to get an array of strings, therefore I am using the
>following declaration:  char *str[MAX] where MAX is an arbitrary 
>constant.  My question is how do I allocate the memory for each 
>character position?

I assume by "character position" you really mean each pointer in the
array; each one can be malloc'ed separately, one string at a time.

>Do all the characters strings for each element
>in the array need to be allocated contiguously?

Unless your application wants to run blindly off the end of one string and
onto the next (sounds like a bad idea to me), each string can be discontigous
with the others.  Each string represents a contiguous block of memory, note,
so one string won't be represented by several pieces.

>The problem involves reading in lines of input.
>Each line would be stored as a string and 
>the number of lines make up the number of elements in the array.

Here is where the Art of programming comes in.  The most obvious implementation
(which arbitrarily limits each line to 512 bytes plus newline) is:

	#include <stdio.h>
	...
	#define MAX 666
	char *str[MAX];
	...
	snuffle_file(f)
	FILE *f;
	{
		char buf[514], *p;
		int i;
		/* Read in lines from the file f */
		for (i = 0; i < MAX; i++) {
			/* Read in a line */
			if (fgets(buf, 514, f) == NULL) break;
			/* Treasure the line in a copy */
			if ((str[i] = malloc(strlen(buf))) == NULL) {
				fprintf(stderr,"Go buy more memory.\n");
				return;
			}
			strcpy(str[i], buf);
			/* Note that if you have strdup() the above 5 lines
			 * become:
			 * if ((str[i] = strdup(buf)) == NULL) {
			 *	<gripe>
			 * }
			 */
		}
	}

This isn't necessarily the best one can do, though; arbitrary line length
limits are annoying, so the first obvious improvement is to replace the
fgets-into-a-buffer strategy with something like (in pseudo-C)

	newstring = malloc(some pittance likely to hold most lines, like 32)
	while not-yet-eof and haven't-seen-a-newline
		fgets into a buffer
		if there's not enough room in newstring to add the buffer to
		the end,
			newstring = realloc(newstring, current size + some)
			/* remember that memory can run out */
		add buffer contents to end of newstring
	newstring = realloc(newstring, actual size) /*free a few bytes at end*/
	
I believe that 4.4BSD will have a "readline()" function that does roughly
that, or you can figure out how to roll your own, or dig up any one of the
innumerable versions that have been lodged in netnews postings over the past
decade.

The next annoying limit that should die is "MAX":  a similar strategy of
realloc-if-out-of-room can be used to remove the arbitrary limit on the
number of lines.

Something else to ponder is:  if you *know* that the file size is relatively
small in comparison to the amount of memory you have, and if you *know* that
your OS does reasonable things for large reads from files, it *may* be worth
mallocing a contiguous buffer which is large enough to hold the file, slurp it
all in with one single read, and then pick it apart into lines.  Note that
this strategy may perform poorly on someone else's machine for perfectly good
reasons, and worse, may misfire badly the first time some clown gives your
program a 16Mb text file when you "knew" the limit would be 16Kb.  Balancing
the tradeoffs well is an art.

scs@adam.mit.edu (Steve Summit) (04/19/91)

In article <3182@ksr.com> jfw@ksr.com (John F. Woods) writes:
>trotter@ENUXHA.EAS.ASU.EDU (Russell T. Trotter) writes:
>>I am trying to get an array of strings...
>>...how do I allocate the memory for each [string]?
>The most obvious implementation... is:
[most code deleted]
>			/* Treasure the line in a copy */
>			if ((str[i] = malloc(strlen(buf))) == NULL) {

Make that malloc(strlen(buf) + 1).  (Side note: never tangle your
fingers and type malloc(strlen(buf + 1)), either.)

In article <1991Apr17.203253.13854@ux1.cso.uiuc.edu> gordon@osiris.cso.uiuc.edu (John Gordon) writes:
>...do a
>str[i] = (char  *) malloc(strlen(buf) +1)
>to copy each line into str.

make that "do [a malloc], *then* copy each line into str."

                                            Steve Summit
                                            scs@adam.mit.edu

jfw@ksr.com (John F. Woods) (04/22/91)

scs@adam.mit.edu (Steve Summit) writes:
>In article <3182@ksr.com> jfw@ksr.com (John F. Woods) writes:
>>			/* Treasure the line in a copy */
>>			if ((str[i] = malloc(strlen(buf))) == NULL) {
>Make that malloc(strlen(buf) + 1).  (Side note: never tangle your
>fingers and type malloc(strlen(buf + 1)), either.)

Oops.

Consider that another reason to use strdup() if you've got it.

Assuming the library writer didn't screw up, too.	:-)

csp@gtenmc.UUCP (04/27/91)

In article <9104171614.AA14362@enuxha.eas.asu.edu> trotter@enuxha.eas.asu.edu (Russell T. Trotter) writes:
>constant.  My question is how do I allocate the memory for each 
                                    ^^^^^^^^
	    Malloc , my dear Russell. Malloc !
>character position? Do all the characters strings for each element
>in the array need to be allocated contiguously?  The problem involves
                                   ^^^^^^^^^^^^
	    Did't you know that memory is linear ! ( NO )
>reading in lines of input.  Each line would be stored as a string and 
>the number of lines make up the number of elements in the array.
>Any information would be greatly appreciated.  Thank you.

	    This proves without doubt 'Ignorance is NOT bliss'.

	 
	 Solution :
	 #include <string.h>
	 #include <malloc.h>
	 #include <stdio.h>

	 #define MAX 20
	 #define MAX_SZ 512

	 char *str[MAX];

	 Read_Input()
	 {
	    int i;
	    char buff[MAX_SZ];

	    for ( i = 0 ; i < MAX ; i++ )
	       str[i] = malloc(strlen(gets(buff)+1)),
	       strcpy(str[i],buff);
	 }

	 C S Palkar
	 --
>
>trotter@enuxha.eas.asu.edu

gregory@ritcsh.csh.rit.edu (Greg Conway) (04/30/91)

In article <1139@gtenmc.UUCP> csp@gtenmc.UUCP () writes:
>	    for ( i = 0 ; i < MAX ; i++ )
>	       str[i] = malloc(strlen(gets(buff)+1)),
>	       strcpy(str[i],buff);

My, my, my -- live on the edge, don't we?
Using malloc without checking the return value is risky, at best...

						- Greg

-- 
		Gregory Conway @ Computer Science House, RIT