Jump to content

Photo

"Invalid pointer passed to array" but array is in global scope


  • Please log in to reply
10 replies to this topic

#1 Twilight Knight

Twilight Knight

    Tell all with glee, Argon's on PureZC

  • Members
  • Real Name:Sven
  • Location:Rotterdam, NL

Posted 22 July 2020 - 05:05 PM

Hi

First off: I'm working with 2.55 a73 build 51. Not sure if that makes a difference though.

When I point to an array that is outside the scope of a function I get the  "Invalid pointer (0) passed to array (don't change the values of your array pointers)" error message.

Saffith once said that this will happen if you create the array in another function (search for "invalid pointer" in this topic: https://www.purezc.n...owtopic=65589),but this is not the case here. I suppose that the real deal is that any array outside of the current scope cannot be used. However I'm declaring and initialising the array in the global scope, like so:

global script Active
{
    void run(){
		while(true){
			loopThroughArray();

			Waitdraw();

			// Draw some things

			Waitframe();
		}
    }
}

int thisArrayWontWork[] = {
	123,
	234
};

void loopThroughArray()
{
	int message1[] = "This will work";
	TraceS(message1);
	TraceNL();
	
	int thisArrayIsFine[] = {
		345,
		456
	};
	
	for(int i = 0; i < SizeOfArray(thisArrayIsFine); i++) {
		Trace(thisArrayIsFine[i]);
		TraceNL();
	}

	int message2[] = "Now attempt the other array";
	TraceS(message2);
	TraceNL();
	
	for(int i = 0; i < SizeOfArray(thisArrayWontWork); i++) {
		Trace(thisArrayWontWork[i]);
		TraceNL();
	}
}

In this article, though it's outdated, I see that you can actually declare arrays outside your scope: https://www.zeldacla...Quirks_of_Scope

 

What am I doing wrong?

 

 

A workaround for this is just declaring and initialising my array in the caller function. But it's simply not very pretty code at all. Especially not if that array will contain a 50 or so items.



#2 Russ

Russ

    Caelan, the Encouraging

  • Administrators
  • Location:Washington

Posted 22 July 2020 - 07:12 PM

The trick I've found is that if you're using a global array like that and ZScript is complaining, passing it into the function as a variable usually makes it work. So instead it'd be:

void loopThroughArray(int thisArrayWontWork)
.

If you edit it the array, because you're passing the array pointer in, it'll still function correctly. I don't get exactly why the error occurs, but in my experience this has done the trick.


  • Twilight Knight likes this

#3 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 23 July 2020 - 04:06 AM

The trick I've found is that if you're using a global array like that and ZScript is complaining, passing it into the function as a variable usually makes it work. So instead it'd be:

void loopThroughArray(int thisArrayWontWork)
.

If you edit it the array, because you're passing the array pointer in, it'll still function correctly. I don't get exactly why the error occurs, but in my experience this has done the trick.

 

 

Hi

First off: I'm working with 2.55 a73 build 51. Not sure if that makes a difference though.

When I point to an array that is outside the scope of a function I get the  "Invalid pointer (0) passed to array (don't change the values of your array pointers)" error message.

Saffith once said that this will happen if you create the array in another function (search for "invalid pointer" in this topic: https://www.purezc.n...owtopic=65589),but this is not the case here. I suppose that the real deal is that any array outside of the current scope cannot be used. However I'm declaring and initialising the array in the global scope, like so:

global script Active
{
    void run(){
		while(true){
			loopThroughArray();

			Waitdraw();

			// Draw some things

			Waitframe();
		}
    }
}

int thisArrayWontWork[] = {
	123,
	234
};

void loopThroughArray()
{
	int message1[] = "This will work";
	TraceS(message1);
	TraceNL();
	
	int thisArrayIsFine[] = {
		345,
		456
	};
	
	for(int i = 0; i < SizeOfArray(thisArrayIsFine); i++) {
		Trace(thisArrayIsFine[i]);
		TraceNL();
	}

	int message2[] = "Now attempt the other array";
	TraceS(message2);
	TraceNL();
	
	for(int i = 0; i < SizeOfArray(thisArrayWontWork); i++) {
		Trace(thisArrayWontWork[i]);
		TraceNL();
	}
}

In this article, though it's outdated, I see that you can actually declare arrays outside your scope: https://www.zeldacla...Quirks_of_Scope

 

What am I doing wrong?

 

 

A workaround for this is just declaring and initialising my array in the caller function. But it's simply not very pretty code at all. Especially not if that array will contain a 50 or so items.

 

No, no, no.

The script is fine. You added a global array to an old save file. Create a new save slot, and try again.


  • Twilight Knight likes this

#4 Twilight Knight

Twilight Knight

    Tell all with glee, Argon's on PureZC

  • Members
  • Real Name:Sven
  • Location:Rotterdam, NL

Posted 23 July 2020 - 05:11 AM

Alright, I'll try that. If you're talking about the save slots in the zscript compiler that is.

My question though is: how was I supposed to find this out on my own?



And to Russ: I tried that, I believe, but that didn't work either.



#5 Emily

Emily

    Scripter / Dev

  • ZC Developers

Posted 23 July 2020 - 09:48 AM

Alright, I'll try that. If you're talking about the save slots in the zscript compiler that is.

No, not slots in the compiler. In ZC. If you make global changes to a script, you must start a fresh save file in ZC. This has *ALWAYS* been the case, as far back as 2.50.2.

The reason? When you define something global, it is initialized when you start the quest. If you add something new global, but load an existing save file, then that is not initialized, as you have not started a fresh quest. So, the array there, is '0', and invalid, because the code that needs to run to initialize it has not run.


The trick I've found is that if you're using a global array like that and ZScript is complaining, passing it into the function as a variable usually makes it work. So instead it'd be:

void loopThroughArray(int thisArrayWontWork)
.

If you edit it the array, because you're passing the array pointer in, it'll still function correctly. I don't get exactly why the error occurs, but in my experience this has done the trick.

I.... have no clue what you are talking about here, or any situation in which this would make any difference whatsoever. If you have examples of times this has worked, I would love to see them, as this sounds like complete nonsense.



#6 Orithan

Orithan

    Studying Scientist - Commission from Silvixen

  • Members
  • Location:Australia

Posted 25 July 2020 - 01:38 AM

No, not slots in the compiler. In ZC. If you make global changes to a script, you must start a fresh save file in ZC. This has *ALWAYS* been the case, as far back as 2.50.2.

The reason? When you define something global, it is initialized when you start the quest. If you add something new global, but load an existing save file, then that is not initialized, as you have not started a fresh quest. So, the array there, is '0', and invalid, because the code that needs to run to initialize it has not run.


I.... have no clue what you are talking about here, or any situation in which this would make any difference whatsoever. If you have examples of times this has worked, I would love to see them, as this sounds like complete nonsense.

What this does allows you to pass arrays through functions in a script. As of 2.53 and IIRC even now, this is the only way you can have variables changed by functions persist after the function ends. Especially helpful for enemies where you need several variables that need to persist after being changed by functions.


Edited by Orithan, 25 July 2020 - 01:44 AM.


#7 Twilight Knight

Twilight Knight

    Tell all with glee, Argon's on PureZC

  • Members
  • Real Name:Sven
  • Location:Rotterdam, NL

Posted 25 July 2020 - 05:53 AM

No, not slots in the compiler. In ZC. If you make global changes to a script, you must start a fresh save file in ZC. This has *ALWAYS* been the case, as far back as 2.50.2.

The reason? When you define something global, it is initialized when you start the quest. If you add something new global, but load an existing save file, then that is not initialized, as you have not started a fresh quest. So, the array there, is '0', and invalid, because the code that needs to run to initialize it has not run.

Yeah, that does make sense indeed!

Didn't even think of that, thanks!



#8 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 25 July 2020 - 10:05 AM

What this does allows you to pass arrays through functions in a script. As of 2.53 and IIRC even now, this is the only way you can have variables changed by functions persist after the function ends. Especially helpful for enemies where you need several variables that need to persist after being changed by functions.

 

That makes no difference here. The array pointer that you pass to a function would still be invalid, as it is global and not properly constructed in an extant save slot. You can do that freely with local arrays, but with any global variable, if you add it after saving when playing the quest, then the quest cannot access it. This applies to all variables, not only arrays. 

 

While you can pass a global pointer to a function by reference, you can also reference it without passing it, as it is global

int arr[16];
 
void foo(int ptr)
{
    ptr[3] = 96;
}
 
ffc script t
{ 
    void run()
    {
        foo(arr);
    }
}
 
ffc script f2
{ 
    void run()
    {
        arr[3] = 96;
    }
}
 
//The array 'arr' is global, so you do not need to worry about it not being in scope.

You can pass an array pointer to a function so that a function outside of the local array scope can read it, but again, those are local arrays. If you see this error on a global variable or array, then you are using a save slot where global variables have already been allocated, and once they are allocated, if you add more, those will not be allocated, and ZASM will not know what to do with references to them. 

 

You do not however, need a new save fileYou need only a clean/blank save slot in your save file, from which to test the quest after adding the new global variables. There are only two instances where a global array can have a pointer of 0. One of them is when you do this, and have save data that does not include the array. The other, is when you intentionally write the pointer to 0, or an otherwise invalid or inactive pointer. 

 

e.g.

int myarray[16] = {1,2,3};
 
global script foo
{
    void run()
    {
        myarray = 0; //this is legal, because the pointer is INT, but it will corrupt the pointer.
        //Access to myarray[] will after this point, be invalid, and will generate an error. 
        //Technically, there is a known valid pointer for this array type, but it is not intended to be used in a literal manner like that. 
    }
}

Changing array pointers is dangerous, and should never be done with literals, as the actual literal pointer value is not guaranteed to be identical from one version to another. Assign by reference OTOH, is valid.

int myarray[16] = {1,2,3};
 
global script foo
{
    void run()
    {
        int newarray = myarray; //Assign an array pointer known to be valid to a new array.
        newarray[1] = 6; //myarray[1] is now 6
    }
}

You can also use IsValidArray(ptr) to validate arrays as of 2.53.1, recent builds, and 2.55 recent builds. 

int myarray[16] = {1,2,3};
	 
	global script foo
	{
	    void run()
	    {
	        myarray = 0; //this is legal, because the pointer is INT, but it will corrupt the pointer.
	        //Access to myarray[] will after this point, be invalid, and will generate an error. 
	        //Technically, there is a known valid pointer for this array type, but it is not intended to be used in a literal manner like that. 
	    }
	}
	

Note that you can only assign arrays to their same type, unless you use an explicit cast. 

 

P.S. never use an identifier of array or string. These are reserved. 

 

e.g. never declare something like int array[5]; or something like char32 string[12];

These will give you headaches in the future. If you need a generic pointer, use ptr and buf.

 

Future real arrays will use array as a keyword for their type, and future real strings (like C++ string) will use string for their type

 

These will be moderately incompatible with old ZScript arrays, but will be multidimensional, an you wlll be able to specify a function to expect an array or a string pointer as an argument. 

 

In C, you can have:

void foo(int *ptr)

This will fail if you pass a normal integer, and will only accept a pointer, which is a distinction that ZScript lacks. In order to do this without breaking extant scripts, the new array and string types will use a token to mark them as a distinct pointer type. That is a olly way down the pipe, but it is worth mentioning for people who declare things like int array[]. If you do this, at some point, it will break an become illegal. 

 

P.P.S. ZScript does know to properly evaluate function parameters when you specify char32 as a type. If you have a set of functions fuch as:

void foo(char32 ptr)
{
    ptr[5] = '\0';
}
 
void foo (int a)
{
    a = 6;
}
 
char32 buf[]="test";
int b = 2;
int c = buf;
char32 d = buf;
 
//call
foo(buf); //typematch to the string
foo(b); //typematch to the variable b
foo(c); //Ambiguous 
foo(d); //Typematches to the string buf. 

An argument typed to char32 tries to match a char32 string first, then it will try matching to int. , so when you pass char32 as an argument, if you have a function set up to expect char32, then ZScript will call that function. If you do not, then it defaults to int. You can pass char32 to int.

 

P.P.P.S.

 

I am extremely paranoid about array or string bugs in 2.55, so, the first thing that I did, was to test the script that the OP posted by compiling and running it. It was fine. It's usually wise to test an issue before giving out erroneous information on how to try to bypass it. Ignoring errors or trying to circumvent them is a doomed ship



#9 Emily

Emily

    Scripter / Dev

  • ZC Developers

Posted 25 July 2020 - 12:28 PM

What this does allows you to pass arrays through functions in a script. As of 2.53 and IIRC even now, this is the only way you can have variables changed by functions persist after the function ends. Especially helpful for enemies where you need several variables that need to persist after being changed by functions.

Does not apply if the array is *GLOBAL*, which is the entire point here. Also, the point was that you don't WANT a function; but you apparently need a function because not using a function is buggy? Regardless, that's not the issue at hand at all; I still have no idea what situations Russ could possibly have gotten in to cause that to be necessary, but, the issue at hand is indeed needing a fresh save file.
 

You do not however, need a new save fileYou need only a clean/blank save slot in your save file, from which to test the quest after adding the new global variables.


The problem here is miscommunication; people pretty much always refer to a new slot as a "New Save File". You most certainly do not need a new 'zc.sav', but you need a new save file, in the terms most people use.



#10 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 29 July 2020 - 05:48 AM

Does not apply if the array is *GLOBAL*, which is the entire point here. Also, the point was that you don't WANT a function; but you apparently need a function because not using a function is buggy? Regardless, that's not the issue at hand at all; I still have no idea what situations Russ could possibly have gotten in to cause that to be necessary, but, the issue at hand is indeed needing a fresh save file.
 


The problem here is miscommunication; people pretty much always refer to a new slot as a "New Save File". You most certainly do not need a new 'zc.sav', but you need a new save file, in the terms most people use.

 

I have seen the term save file applied both to the .sav, and to the slots, and 'save file' has historically been accepted as zc.sav. Thus, clarification is required for that. A save file, is the .sav file; but a save slot, is a slot in the current save file.



#11 Twilight Knight

Twilight Knight

    Tell all with glee, Argon's on PureZC

  • Members
  • Real Name:Sven
  • Location:Rotterdam, NL

Posted 29 July 2020 - 06:47 AM

Just to confirm, it does work after I created a new save file/slot.

And it absolutely makes sense that newly declared global arrays do not work on an older save. I just didn't think of that. Perhaps because I was frantically working on it at midnight.  :heh:




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users