Omnimaga

Calculator Community => TI Calculators => Calculator C => Topic started by: TC01 on August 03, 2010, 07:45:23 pm

Title: Pointer trouble
Post by: TC01 on August 03, 2010, 07:45:23 pm
A note- this is 68k C (GCC4TI), not Nspire C. Shouldn't really be a difference except for the nature of my error...

I've mostly finished making z89 (that's my z80 Basic editor for 68k calcs) read in the list of tokens from a token file, then build them into an array of tokenInfo structures. The code to do this is rather complicated and probably poorly written- I'm not the best C programmer. I only started programming in C recently. However, it works.

Or, at least, it would work if not for the Address Error I get when I try to free allocated memory at the end of the process. I have four pointers to strings being dynamically allocated- the text of the token file, a "working" string (for storing end-of-line characters and other things necessary in procedures like strcpsn()), a line string (for storing each line of the text file), and a "tokenstr" string, for storing token information (first the name, then the first token in hex).

I had had problems with this before. Specifically, originally I was using realloc() to shrink and decrease the line string on each pass through the while loop. But that was causing an Address Error. I "solved" it by allocating enough memory to store the entire file to the line string- I'm not entirely sure why that solved the problem, though.

The GCC4TI docs offer the definition of an address error below, but I'm not sure how trying to free the pointers would cause that.

Quote from: GCC4TI docs
Q: What kind of error in my program usually causes an "Address Error"?

A: Well, writing over the boundaries of an array can usually cause all sorts of errors, since it usually destroys code or the return address of the function. However, an "Address Error" actually means that a short or long value is read or written at an odd address. So if you get an "Address Error" while you are dealing with pointers, check that you do not cast an odd address to a pointer to a short or long integer. 

Here is my long and probably over-complex initTokens() function, which is where the array of token infos is built. It assumes the text file is formatted as follows (name\byte1\byte2, with byte2=0 if the token is one byte):

Code: [Select]
:BoxPlot\0x05\0
:ClrHome\0xE1\0

Code: [Select]
void initTokens(tokenInfo *tokens, char *tokenfile)
{
    int length;
    int bool;
    int newlength;
    int span = 0;
    int i = 0;
    char newchar;
    char endline = '\r';
    unsigned long rawhex;
    unsigned short size = 0;
    unsigned char hex = '0';

    char *tokentext = NULL;
    char *working = NULL;
    char *line = NULL;
    char *tokenstr = NULL;

    tokenInfo token;
    SYM_ENTRY *sym;
   
    if ((working = (char *)calloc(2, sizeof(char))) == NULL)
    {
        DlgMessage("DMA Failure", "Unable to allocate space for working string", BT_OK, BT_NONE);
        return;
    }
    if ((line = (char *)calloc(10, sizeof(char))) == NULL)
    {
        DlgMessage("DMA Failure", "Unable to allocate space for line string", BT_OK, BT_NONE);
        free(working);
        return;
    }
    if ((tokenstr = (char *)calloc(16, sizeof(char))) == NULL)
    {
        DlgMessage("DMA Failure", "Unable to allocate space for token working string", BT_OK, BT_NONE);
        free(working);
        free(line);
        return;
    }
   
    //Get the size of the token file and allocate memory accordingly
    sym = SymFindPtr(SYMSTR(tokenfile), 0);
    size = ((MULTI_EXPR*)HeapDeref(sym->handle))->Size + 2;
    if ((tokentext = (char *)calloc(size, sizeof(char))) == NULL)
    {
        DlgMessage("DMA Failure", "Unable to allocate space for token file string", BT_OK, BT_NONE);
        free(working);
        free(line);
        free(tokenstr);
        return;
    }
   
    //Get input from file, set length and reallocate memory to troublesome pointers
    bool = getPrgmFromText(tokenfile, tokentext, size);
    if (bool == 0)
    {
        free(tokentext);
        free(working);
        free(line);
        return;
    }
    length = strlen(tokentext);
    line = (char *)realloc(line, (length + 2) * sizeof(char));
    tokenstr = (char *)realloc(tokenstr, (length + 2) * sizeof(char));

    //Loop until the length of the input string is zero
    while (length > 0)
    {
        //Maintain num token size!
        i += 1;
        if (i > NUM_TOKENS)
        {
            tokens = (tokenInfo *)realloc(tokens, i * sizeof(tokenInfo));
            NUM_TOKENS = i;
        }

        //Get the length of the line up to trailing character, reallocate memory
        memset(working, '\0', strlen(working));
        memset(line, '\0', strlen(line));   
        memset(tokenstr, '\0', strlen(tokenstr));           
        sprintf(working, "%c", endline);
        span = strcspn(tokentext, working);
       
        //Load the line into a string, and then remove it
        line = strncat(line, tokentext, span);
        tokentext = strpbrk(tokentext, working);
       
        //Then clear out the \r at the front of the string and clear the working string out
        newchar = tokentext[2];
        sprintf(working, "%c", newchar);
        tokentext = strpbrk(tokentext, working);
        length = strlen(tokentext);
       
        //Init the token
        token = tokens[i];
        token.twoByteToken = 0;

        //Seperate the name component from the line and set it (and it's length!)
        memset(working, '\0', strlen(working));
        sprintf(working, "%c", '\\');
        newlength = strcspn(line, working);
        tokenstr = strncat(tokenstr, line, newlength);
        line += (newlength + 1);
        token.chars = newlength;
        token.name = tokenstr;
       
        //Seperate the hex component from the line and set it
        memset(tokenstr, '\0', strlen(tokenstr));
        tokenstr = strncat(tokenstr, line, 4);
        rawhex = strtol(tokenstr, NULL, 0);
        hex = (unsigned char)rawhex;
        token.hex[0] = hex;
        line += 5;

        //Then check if what's left of the string is the second byte of a two-byte token and deal with it
        if (line[0] != '0')
        {
            rawhex = strtol(tokenstr, NULL, 0);
            ngetchx();
            hex = (unsigned char)rawhex;
            token.hex[1] = hex;
            token.twoByteToken = 1;
        }

        //Update the token
        tokens[i] = token;
    }
   
    //Free memory
    free(working);
    free(tokenstr);
    free(line);
    free(tokentext);
}
Title: Re: Pointer trouble
Post by: Lionel Debroux on August 04, 2010, 08:26:30 am
Stylistically speaking, DMA usually stands for "Direct Memory Access" instead of "Dynamic Memory Allocation" :)

It's often hard to debug a program with just one function, could you upload the source somewhere so that we can 1) look at the full source, 2) compile it and 3) execute it?
Title: Re: Pointer trouble
Post by: TC01 on August 04, 2010, 10:20:42 am
Stylistically speaking, DMA usually stands for "Direct Memory Access" instead of "Dynamic Memory Allocation" :)

Oh, okay. (I based it on the Technoplaza tutorials, which use an error message similar to that).

Quote
It's often hard to debug a program with just one function, could you upload the source somewhere so that we can 1) look at the full source, 2) compile it and 3) execute it?

Okay, I've attached it (with the compiled 89z, 9xz, and v2z files included).
Title: Re: Pointer trouble
Post by: Lionel Debroux on August 05, 2010, 05:46:00 am
I'll try to look at that, but don't let that prevent you from proceeding further :)

Note about "Solar": there's a "Solar Striker" game, http://www.yaronet.com/t3/?id=8 .
Title: Re: Pointer trouble
Post by: TC01 on August 05, 2010, 09:52:17 am
Thanks Lionel. And yes, I'll continue to work on it myself at the same time.

I did notice an older version of that game when I searched the ticalc.org archives for Solar to see if anything used the name Solar89, but wasn't actually called Solar89 so I decided it was okay.
Title: Re: Pointer trouble
Post by: TC01 on August 06, 2010, 01:10:55 pm
Okay, well I guess I've figured it out but I don't know what to do about it.

You can't free a pointer after you've changed it. And in that code, all pointers are either being memset (to reinitialize them to point to nothing) or changed (for instance, the line pointer is incremented).

The advice given in the GCC4TI docs is "preserve the original pointer and pass it to free". How would I do this? Make a function and pass the code where I'm manipulating the pointer there?
Title: Re: Pointer trouble
Post by: Lionel Debroux on August 07, 2010, 05:04:37 am
Quote
The advice given in the GCC4TI docs is "preserve the original pointer and pass it to free". How would I do this? Make a function and pass the code where I'm manipulating the pointer there?
That's one of the ways to do it. But without creating a function, you can just use a temporary pointer:
Code: [Select]
type * orig_ptr = malloc/calloc(size);
type * ptr = orig_ptr;
<do something with ptr, leave orig_ptr alone>
free(orig_ptr);