October 30, 2025, 09:22:02 AM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


Possible bug with pointer arrays

Started by Mike Stefanik, February 06, 2006, 01:55:50 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Mike Stefanik

If you have an array of pointers to strings, you can do something like this:


    pointer lpValues[MAXVALUES];
    pointer lpValue;
    int nIndex;

    // Assume the lpValues array has been initialized at this point
    // with each element pointing to a null-terminated string

    for (nIndex = 0; nIndex < MAXVALUES; nIndex++)
    {
         lpValue = lpValues[nIndex];
        pListBox->AddString(*(string)lpValue);
    }


That code works fine. However, curiously enough, the following code won't work:


    pointer lpValues[MAXVALUES];
    int nIndex;

    // Assume the lpValues array has been initialized at this point
    // with each element pointing to a null-terminated string

    for (nIndex = 0; nIndex < MAXVALUES; nIndex++)
    {
        pListBox->AddString(*(string)lpValues[nIndex]);
    }


My guess would be that there's some kind of operator precendence problem when casting an element of an array vs. a plain variable. Trying to do this results in a parsing error:


pListBox->AddString(*(string)(lpValues[nIndex]));


That would also appear to be a bug in the parser.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

lviklund

I have also struggled with this.
I presumed it was a bug in my head  :).

Looking forward to an explanation to where the bug is located.

/Lasse

Rock Ridge Farm (Larry)

Unless I am mistaken (happens a lot).
You allocated an array of pointers but no actual storage for the strings.
The pointer will just be an address. You still need actual storage for the stirng.

Parker

I think the parser isn't seeing [] before *. But something like this works
*(string)(variable + 4*nIndex)
4 is the size of a 32 bit pointer.

Mike Stefanik

February 06, 2006, 08:15:51 AM #4 Last Edit: February 06, 2006, 08:19:27 AM by Mike Stefanik
In that little code snippet, presume that the pointers in the array are valid and the string space is allocated. The problem appears to be how the compliler is generating code when you attempt to cast a value for an element of an array instead of a regular variable. In other words, to boil it down, the following code works:


p = a[n];
function(*(string)p);


The following code should be equivalient to the above, but the compiler (apparently) doesn't handle it correctly:


function(*(string)a[n]);


The above code only works if 'n' is the first element (0) of the array. If n > 0, then it breaks.

Edit: Yup, you can do pointer arithmetic to get around the problem, but its an ugly hack (and generally speaking, assuming that an integer value is always 4 bytes wide is non-portable).
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Ionic Wind Support Team

Use pointer math.   Typecasting has higher precedence than the array operator currently and reversing that will break other things.

It was something I was going to look at in the future when I had a few hours to kill.  Pointer math will always work no matter what changes in precedence though.

Paul.

Ionic Wind Support Team

Parker

Quote from: Mike Stefanik on February 06, 2006, 08:15:51 AMand generally speaking, assuming that an integer value is always 4 bytes wide is non-portable

That's why we need the sizeof() function to work, so instead you can do
*(string)(variable + sizeof(pointer)*nIndex);
which would work on 64 bit systems when aurora supports them too.

My idea is to have len stay how it is now, where it can still be used to determine UDT lengths, but sizeof() be added that can't do string length but just tells what the size of a built in variable type, or a UDT length.

Ionic Wind Support Team

LEN can be changed for intrinsic types too.  This isn't C so I had no plans on adding 'sizeof' since we already have two length operators.
Ionic Wind Support Team

Mike Stefanik

February 06, 2006, 08:45:25 AM #8 Last Edit: February 06, 2006, 08:49:01 AM by Mike Stefanik
Just so you know "sizeof" does show up as a keyword in the editor. It made me think that had been implemented, until I tried to actually use it. Personally I think that the use of a function like len() instead of a sizeof operator is a bit confusing, but Visual Basic does the same thing so I guess folks are used to that these days.

Back to the array issue, I've found that the pointer arithmetic doesn't work as expected either. Let's say that you have the following block of text in memory (\0 will represent a null byte): "abcdefghijk\0lmnopqrs\0tuvwxyz\0" and you setup an array of pointers where a[0] points to the first null-terminated string (ie: "abcdefghijk"), a[1] points to the second string and so on.

If you use the two-step approach, then everything works correctly:


for (n = 0; n < 3; n++)
{
    p = a[n];
   writeln(*(string)p);
}


All well and good. However, if you use pointer math things go screwy:


for (n = 0; n < 3; n++)
{
    writeln(*(string)(a + (n * 4)));
}


In the second case, instead of getting "abcdefghijk", "lmnopqrs" and "tuvwxyz" as output, you'll get "abcdefghijk", "efghijk" and "ijk". Instead of advancing through the array, it's advancing through what the first element of the array points to. It seems to be treating "a" as the same as "&a[0]" which would be wrong.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Ionic Wind Support Team

Pointer math works for an array of the some sized elements, the string type is 255 bytes long so you wouldn't be accessing the correct location  You need two type casting operators maybe.

*(string)*(pointer)(p + n*4);

Haven't looked into it yet.  But I can understand why it is happening.

Again the array operators will be fixed when I have a few hours to kill.  Right now the IDE is being ripped apart on my end so I can't test your code yet ;)
Ionic Wind Support Team

Mike Stefanik

No worries, just something to point out for future reference. The work-around is simple and in some cases can actually make the code a bit clearer to read. :)
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Ionic Wind Support Team

sizeof can be an alias for LEN.  Just didn't see the point in having three separate 'length' functions.

It's colored blue because I used the C keyword table of Scintilla as a starting point.  Just haven't gone through and removed all of the non-existing ones yet.

Paul.
Ionic Wind Support Team

Parker

What's the second already existing one? I only know of LEN.

Ionic Wind Support Team

Ionic Wind Support Team

lviklund

I don't get it ???.

Why does this work:
Quotemessagebox(null, str$((*(SBall)pBalls.posX)), "");

but not this:
Quotemessagebox(null, str$((*(SBall)pBalls.posX) + (RADIUS/2)), "");

As Larry said. Take it slowly. I need to understand this.

Lasse

Ionic Wind Support Team

What doesn't work?  a compiler error, a crash, unexpected result?  Be a bit more specific ;)
Ionic Wind Support Team

lviklund

Oh, I thought you could read my mind.
Actually my previous post works but that just makes it even more confusing.
I have to include the stripped code again.

Here it is:
SillyBalls with pointers ;D ;D ;D.

This works but if you take away the /* around the IF clauses in DrawBalls strange things starts to happen.

File: E:\EGEN_Kod\Aurora\Demos\SillyBalls_with_pointers\DemoBalls_with_pointers.src (117) unsupported use of * - {

#autodefine "off"

#typedef uint unsigned int

#define WINDOW_WIDTH  400
#define WINDOW_HEIGHT 400

#define RADIUS        10
#define NUM_BALLS      3
#define MAX_VELOCITY  3

declare import, GetTickCount(),uint;

struct SBall
{
  int posX;  //to hold the balls coordinates
  int posY;
  int velX;  //and velocity
  int velY;
  int ball_color; //Some fancy colors
}

class mywindow : window
{
//overridden functions.
declare OnClose(),int;
declare OnCreate(),int;
declare OnPaint(),int;
declare DrawBalls(),int;
declare SetUpBalls(),int;

RECT rect;
int cxClient, cyClient, cwClient, chClient;
SBall balls[NUM_BALLS];
pointer pBalls[NUM_BALLS];
}

global sub main()
{
mywindow w;
w.Create(0,0,WINDOW_WIDTH,WINDOW_HEIGHT,AWS_CAPTION|AWS_VISIBLE|AWS_SYSMENU,0,"Silly balls!!",0);

do
{
wait();
}until w.m_hwnd = 0;
return;
}

mywindow::OnClose(),int
{
Destroy();
return TRUE;
}

mywindow::OnCreate(),int
{
CenterWindow();
SetUpBalls();
return true;
}

mywindow::OnPaint(),int
{
DrawBalls();
return true;
}

mywindow::SetUpBalls(),int
{
rect = GetClientRect();
cxClient = rect.left;
cyClient = rect.top;

cwClient = rect.right-rect.left;
chClient = rect.bottom-rect.top;

//seed random number generator
seedrnd(GetTickCount());

//set up the balls with random positions and velocities
for (int i=0; i<NUM_BALLS; i++)
{
pBalls[i] = new(SBall,len(SBall));

*(SBall)pBalls[i].posX = Rand((RADIUS/2), cwClient - (RADIUS/2));
*(SBall)pBalls[i].posY = Rand((RADIUS/2), chClient - (RADIUS/2));
*(SBall)pBalls[i].velX = Rand(1,MAX_VELOCITY);
*(SBall)pBalls[i].velY = Rand(1,MAX_VELOCITY);
*(SBall)pBalls[i].ball_color = RGB(rand(255),rand(255),rand(255));
}

//DrawBalls();
return true;
}

mywindow::DrawBalls(),int
{
uint cacheDC;
cacheDC = GetHDC();
DrawMode(TRANSPARENT);

for (int i=0; i<NUM_BALLS; i++)
{
//check to see if they have hit any walls and reverse velocity
//accordingly

//messagebox(null,str$(*(SBall)pBalls[i].posX + (RADIUS/2)),"");
//messagebox(null,str$(*(SBall)pBalls[i].posX),"");

/* if (*(SBall)pBalls[i].posX + (RADIUS/2) >= cwClient or *(SBALL)pBalls[i].posX - (RADIUS/2) < 0)
{
*(SBall)pBalls[i].velX = -(*(SBall)pBalls[i].velX);
}

if (*(SBALL)pBalls[i].posY + (RADIUS/2) >= chClient or *(SBALL)balls[i].posY - (RADIUS/2) < 0)
{
*(SBall)pBalls[i].velY = *(SBall)pBalls[i].velY;
}
*/ // update their position
*(SBall)pBalls[i].posX += *(SBall)pBalls[i].velX;
*(SBall)pBalls[i].posY += *(SBall)pBalls[i].velY;

//render to display
Circle(*(SBall)pBalls[i].posX, *(SBall)pBalls[i].posY, RADIUS,
*(SBall)pBalls[i].ball_color, *(SBall)pBalls[i].ball_color);

}
DrawMode(OPAQUE);
WriteText(rect.left+20,rect.bottom-20, "Last ball number: " + str$(i) + " ready. " +
str$(*(SBall)pBalls[i].velX) + " " +
str$(*(SBall)pBalls[i].velY) + " " + str$(RADIUS));

ReleaseHDC(cacheDC);
return true;
}



It seems like IF's don't like me.

Lasse

Ionic Wind Support Team

As mentioned before array operators have lower precendence than type casting.  It's something we are working on.
Ionic Wind Support Team

lviklund

Yes
That's why I get so confused when this worked:

messagebox(null,str$(*(SBall)pBalls[i].posX + (RADIUS/2)),"");

(Above the if's in my previous post)

Anyway, I put this on ice for now.