Thread: strange memory error.

  1. #1
    Registered User stormbringer's Avatar
    Join Date
    Jul 2002
    Posts
    90

    strange memory error.

    i wrote a programm to scann files and move all content into memory. if there isn't enough memory it returns NULL. well it works partially. if i have a printf statement (comented out in following listing) it works o.k. But if i comment out the printf statement (what i want to do), it scans some chars and then aborts returning a NULL pointer.
    i am using win nt 4 and lcc win32 compiler, programming a console app.
    could it be that the maschine is to fast for realloc? what is the nop (no operation) statement in c? any better idea to fix it?

    thanks, here's the code:

    #include "extio.h"


    char *freadbuf(FILE *f)
    {
    char c = 1;
    char *buf,*tmp,*tmp2;
    if ((buf = (char *)malloc(sizeof(char))) == NULL)
    return NULL;

    tmp = buf;
    tmp2 = buf;
    while(c != EOF)
    {
    *tmp = fgetc(f);
    c = *tmp;
    //printf(".");
    if ((buf = (char *)realloc(buf,((&(*tmp) - &(*buf)) + sizeof(char)))) == NULL)
    return NULL;
    tmp = &(*buf) + (&(*tmp) - &(*tmp2)); // adjust tmp
    ++tmp;
    tmp2 = buf;
    }
    *tmp = EOF;
    while (*buf != EOF) //will be removed
    {
    printf("%c",*buf);
    ++buf;
    }
    return buf;
    }

  2. #2
    End Of Line Hammer's Avatar
    Join Date
    Apr 2002
    Posts
    6,231
    First, this is wrong:
    >char c = 1;
    >while (c != EOF)
    because EOF is an int and c is only a char. You must make c an int for this to work.

    Also, you can do the loop control like this:
    >while ((c = fgetc(f)) != EOF)

    >realloc()
    Don't pass the same pointer you are using the store the return from realloc as it's first arg. If realloc fails, it will return NULL, but the pointer in its first arg will still be valid. If you use the same pointer for both, you loose the address of the original memory block, and thereby create a leak.

    >if ((buf = (char *) realloc(buf, ((&(*tmp) - &(*buf)) + sizeof(char)))) == NULL) return NULL;
    This looks rather complicated for what it is. Can't you simplify the second arg to something like
    >(tmp - buf) + sizeof(char)
    (I'm not sure what you try to do, so maybe I'm wrong here).


    As you are reading the whole file into memory, you could also use this method:
    - Determine the size of the file (maybe using fseek())
    - malloc that amount of bytes
    - fread() the file into memory.

    Here's a sample
    Code:
    char* LoadFile(char *FileName, long int *CharsRead)
    {
    	/*
    	 * Load a complete file into malloc'd memory
    	 *
    	 * Return a pointer to the start of the data, or NULL if error occurs.
    	 * It is up to the caller to free the memory when it's finished with.
    	 *
    	 * CharsRead will be populated with the number of bytes read in.
    	 */
    	FILE *fp;
    	char *ptr;
    	long int FileSize;
    
    	*CharsRead = 0;
    	
    	if ((fp = fopen(FileName, "rb")) == NULL)
    	{
    		perror (FileName);
    		return (NULL);
    	}
    	
    	fseek(fp, 0, SEEK_END);
    	FileSize = ftell(fp);
    	fseek(fp, 0, SEEK_SET);
    
    	if ((ptr = malloc(FileSize)) == NULL)
    	{
    		perror ("Memory allocation failure!");
    		return (NULL);
    	}
    
    	if ((fread(ptr, FileSize, 1, fp)) != 1)
    	{
    		perror (FileName);
    		free(ptr);
    		ptr = NULL;
    	}
    	else *CharsRead = FileSize;
    	
    	return (ptr);
    }	/* End of LoadFile *
    When all else fails, read the instructions.
    If you're posting code, use code tags: [code] /* insert code here */ [/code]

  3. #3
    Registered User stormbringer's Avatar
    Join Date
    Jul 2002
    Posts
    90
    i adjusted the code. i wanna try getting my code to work, because 1) i'd like to learn something and 2) i mybe once do not want to read a whole file. so if you don't mind helping me some more:

    Code:
    char *freadbuf(char *fname)
    {
    	FILE *f;
    	int c;
    	int *buf,*tmp,*tmp2;
    
    	if ((f = fopen(fname,"r")) == NULL)
    		return NULL;
    
    
    	if ((buf = (int *)malloc(sizeof(int))) == NULL)
    		return NULL;
    
    	tmp = buf;
    	tmp2 = buf;
    	while((c = fgetc(f)) != EOF)
    		{
    			*tmp = c;
    			if ((buf = (int *)realloc(tmp2,((1+tmp-buf)*sizeof(int)))) == NULL)
    				{
    					fclose(f);
    					free(tmp2);
    					return NULL;
    				}
    			tmp = buf + (tmp - tmp2);			// adjust tmp
    			++tmp;
    			tmp2 = buf;
    		}
    	
    	*tmp = EOF;
    	while (*buf != EOF)				//testing
    		{
    			printf("%c",*buf);
    			++buf;
    		}
    	fclose(f);
    	return buf;
    }
    i print the buffer for testing. everytime realloc changes the memory adress, characters are ("forgotten"). i'll give an example.

    in the testfile stands: testfile for freadbuf
    the output is: t tfile for freadbuf (two spaces)

    thanks

    stormbringer

  4. #4
    End Of Line Hammer's Avatar
    Join Date
    Apr 2002
    Posts
    6,231
    Confusion reigns ...

    The bit I mentioned before about int/char..... only the variable receiving the return code from fgetc() need to be an int. The buffer variables should be chars (or pointers to).

    Here's your code back, with some changes. Compare it to your original and see how you get on.

    It still leaves a dead byte at the end of the buffer (because you always alloc one after reading one in. This is not the best way to do things, imo. I also think the function should tell the caller how many bytes are in the buffer. This is because the buffer is not NULL terminated (in my version at least), and therefore the caller has no way of knowing where it ends.

    Code:
    /* This is not guaranteed to be bullet proof!*/
    char *freadbuf(char *fname)
    {
        FILE    *f;
        int     c, buflen, i;
        char    *buf, *newbuf;
        
        if ((f = fopen(fname, "r")) == NULL) 
        {
        	return NULL;
        }
        if ((buf = malloc(sizeof(char))) == NULL) 
        {
        	fclose(f);
        	return NULL;
        }
        
        buflen = 0;
        
        while ((c = fgetc(f)) != EOF)
        {
            buf[buflen] = c;
            ++buflen;
            if ((newbuf = realloc(buf, (buflen+1) * sizeof(char))) == NULL)
            {
                fclose(f);
                free(buf);
                return NULL;
            }
                buf = newbuf;
        }
    
        printf ("buflen=%d\n", buflen);    
        for (i = 0; i < buflen; i++)
        	putchar (buf[i]);
    
        fclose(f);
        return buf;
    }
    Another way to do this type of thing is to:
    - Read one line into a char array.
    - Determine the length of the line.
    - malloc memory for length+1
    - strcpy from array to new memory.
    - goto start (reusing the same array to read into)
    This way you only malloc memory for data you have loaded. This is a common approach when using binary trees and the like.
    When all else fails, read the instructions.
    If you're posting code, use code tags: [code] /* insert code here */ [/code]

  5. #5
    Registered User stormbringer's Avatar
    Join Date
    Jul 2002
    Posts
    90
    ok,

    you are using buflen to determine the lenght of the buffer. since this is a integer there is always a chance of an overflow (even if it's double.....). i am trying to avoid depending on variables, thats why i compare memory adress. thats the right try, ain't it?

    anyway, need to play with your solution a little bit. didn't have the time yet.

    thanks

    stormbringer

  6. #6
    End Of Line Hammer's Avatar
    Join Date
    Apr 2002
    Posts
    6,231
    >you are using buflen to determine the lenght of the buffer. since this is a integer there is always a chance of an overflow (even if it's double.....).
    A double is for floating point, a long int is what you need. It'll hold 2147483647L on my system (maybe different on yours).
    Yes, you can compare the pointers if you like, I think you'd need the 3rd pointer like you had originally. I changed it to make it easier to understand.

    Have fun
    When all else fails, read the instructions.
    If you're posting code, use code tags: [code] /* insert code here */ [/code]

  7. #7
    Registered User stormbringer's Avatar
    Join Date
    Jul 2002
    Posts
    90
    here i am again

    i tried my version with the pointer (and not using buflen). my result is very close to hammers. however, the big difference is hammers works, mine not.
    buflen equals (tmp - buf), doesn't it (see code below). and the second thing i have to do seperately is adjusting the tmp in case of a memory switch. the strange thing now ist, with the version below it overeads some chars everytime it makes a memory change and the first char isn't visible anymore. i suppose wenn the block is growing too big, realloc moves the position of the whole block. am i right (fragmenting the block would be rather difficult). if i use the command newbuf = (char *)realloc(buf,(1+tmp-buf)*sizeof(int ), everything works fine. but how can that be? there must be an error.

    Code:
    char *freadbuf(char *fname)
    {
    	FILE *f;
    	int c,i;
    	char *buf,*tmp,*newbuf;
    
    
    	if ((f = fopen(fname,"r")) == NULL)
    		return NULL;
    
    
    	if ((buf = (char *)malloc(sizeof(char))) == NULL)
    		{
    			fclose(f);
    			return NULL;
    		}
    
    	tmp = buf;
    	newbuf = buf;
    	while((c = fgetc(f)) != EOF)
    		{
    			*tmp = c;
    			if ((newbuf = (char *)realloc(buf,((1+tmp-buf)*sizeof(char)))) == NULL)
    				{
    					fclose(f);
    					free(buf);
    					return NULL;
    				}
    			
    			tmp = newbuf + (tmp - buf);			// adjust tmp
    			++tmp;
    			buf = newbuf;
    		}
    	*tmp = EOF;
    	fclose(f);
    	return buf;
    }

  8. #8
    End Of Line Hammer's Avatar
    Join Date
    Apr 2002
    Posts
    6,231
    >realloc(buf,((1+tmp-buf)*sizeof(char))))
    This bit is wrong. Picture the numbers for the first cycle through the loop. tmp and buf are the same, therefore tmp-buf will equal 0. This will result in you reallocing a buf from length 1 to length 1.
    >realloc(buf,1*1)
    This is why you must increment tmp before the realloc (which is why I incremented buflen).

    >*tmp = EOF;
    EOF is not a character, it's an int. It is not needed in your buffer. If you want to terminate the buffer use
    >*tmp = '\0';

    Not sure if this is everything....
    When all else fails, read the instructions.
    If you're posting code, use code tags: [code] /* insert code here */ [/code]

  9. #9
    Registered User stormbringer's Avatar
    Join Date
    Jul 2002
    Posts
    90
    man you're quick to answer questions

    the ++tmp was in the wrong location, right. the error in my thought was, that tmp is pointing to not allocated memory (what's true, but doesn't matter because we don't try to write it)

    it runs now. thanks a lot to everybody who helped me

    stormbringer

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Testing some code, lots of errors...
    By Sparrowhawk in forum C Programming
    Replies: 48
    Last Post: 12-15-2008, 04:09 AM
  2. Quantum Random Bit Generator
    By shawnt in forum C++ Programming
    Replies: 62
    Last Post: 06-18-2008, 10:17 AM
  3. Getting other processes class names
    By Hawkin in forum Windows Programming
    Replies: 3
    Last Post: 03-20-2008, 04:02 PM
  4. file reading
    By gunghomiller in forum C++ Programming
    Replies: 9
    Last Post: 08-07-2007, 10:55 PM
  5. load gif into program
    By willc0de4food in forum Windows Programming
    Replies: 14
    Last Post: 01-11-2006, 10:43 AM