Announcement

Collapse
No announcement yet.

Pass Undimensioned Array As Parameter

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Pass Undimensioned Array As Parameter

    I know there have been discussions of this in the past concerning redimensioning arrays in procedures to which they were passed. I know I always came down on the side of it not being a good idea; however, I know some members regularly do it, and supposedly the compiler keeps track of the current dimensions of the array as it is passed about. Deciding to 'live dangerously' and give it a try I immediately ran into problems. However, I see that what I was doing was passing an undimensioned array to a function which, as part of its processing, would determine the proper limits of the array, and dimension it to those limits there before returning execution to the calling function. The calling function (where the array was initially declared but not dimensioned) would then have a properly sized array able to be passed to other procedures for additional processing. Well, that isn't working. On reflection, I'm wondering if the proper technique is to dimension the array initially to some size - perhaps any size, or one large enough to handle any possible value the particular context is capable of giving it, then pass it to the function where it would be resized to the correct dimensions. I havn't tried that yet, but I'm seeing it as the only alternative because passing the uninitialized array isn't working.

    Here is a small test program (Console Compiler) that shows the issue with some simple data. As posted it will run but crash as program closing. However, the program is aware of the dimensioning of the array in the procedure and returns a correct UBound back in Main. Note that if you uncomment the commented out line it won't compile as the compiler will balk at the 'undimensioned array' - even though it reports a correct UBound.
    Any thoughts on this issue?

    Code:
    #Compile Exe
    #Dim All
    
    Type SomeThingOrOther
      First       As Long
      Second      As Long
      AnArray(8)  As Byte
    End Type
    
    
    Function blnSomeProc(Byref SomeType() As SomeThingOrOther) As Long
      Register i As Long
      Register j As Long
    
      Redim SomeType(2) As SomeThingOrOther
      For i=0 To 2
        SomeType(i).First=i : SomeType(i).Second=i
        For j=65 To 73
          SomeType(i).AnArray(j)=(i*8)+j
        Next j
      Next i
    
      Function=1
    End Function
    
    
    Function PBMain() As Long
      Local typ() As SomeThingOrOther
    
      If blnSomeProc(typ()) Then
         Print "blnSomeProc() Succeeded!
         Print "UBound(typ,1) = " UBound(typ,1)
         'Print "typ(1).First  = " typ(1).First
      End If
      WaitKey$
    
      PBMain=0
    End Function
    Fred
    "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

  • #2
    Ooops! Got a stupid array subscripting error in test code. Give me a minute to fix this before trying it!!!
    Fred
    "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

    Comment


    • #3
      AnArray(8) is exceeding its bounds with subscripts 65 to 73.

      Comment


      • #4
        There, I fixed the dumb thing. Now its not crashing anymore but the gist of my original observations seem to remain true. An array has to be dimensioned first to anything before it can be passed as a parameter. Once in a prcedure it can be redimensioned OK and the changed array will show up correctly in the calling procedure after termination of the procedure where it was passed.

        Code:
        #Compile Exe
        #Dim All
        
        Type SomeThingOrOther
          First       As Long
          Second      As Long
          AnArray(8)  As Byte
        End Type
        
        
        Function blnSomeProc(Byref SomeType() As SomeThingOrOther) As Long
          Register i As Long
          Register j As Long
        
          Redim SomeType(2) As SomeThingOrOther
          For i=0 To 2
            SomeType(i).First=i : SomeType(i).Second=i
            For j=1 To 8
              SomeType(i).AnArray(j)=(i*8)+65
            Next j
          Next i
        
          Function=1
        End Function
        
        
        Function PBMain() As Long
          Local typ() As SomeThingOrOther
        
          'Redim typ(5)
          If blnSomeProc(typ()) Then
             Print "blnSomeProc() Succeeded!
             Print "UBound(typ,1) = " UBound(typ,1)
             'Print "typ(1).First  = " typ(1).First
             
          End If
          WaitKey$
        
          PBMain=0
        End Function
        Fred
        "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

        Comment


        • #5
          AnArray(8) is exceeding its bounds with subscripts 65 to 73.
          Yea, I caught that John

          It does appear that what I was thinking was allowed is a definite no-no, i.e., passing an undimentioned array and re-dimming it in the procedure. It seems to work OK in the procedure, but the changes are lost somewhere in the shuffle. However, back in the original procedure the UBound is showing up OK.

          In my actual program I found even dimming the array to be passed to (0) is OK - as long as it is dimmed to something.

          The reason I posted about this is that several years back there was a long drawn out thread about this and it turned out there were indeed some 'issues' with this in regards to the various compiler versions, i.e., 6 to 7 to 8.
          Fred
          "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

          Comment


          • #6
            passing an undimentioned array and re-dimming it in the procedure
            This discussion came up here before. I wanted to do that, too, but the compiler "helps" by giving an "undimensioned array" error if there is no possibility that array was the object of a DIM or REDIM statement in the current code module.

            (Like, I KNEW it was not dimensioned in this module; that's what I WANTED to do but you insisted on telling me it was a mistake!) (And it's not like you couldn't know what to pass, as I had "LOCAL myarray() AS LONG" earlier in the procedure, so you KNEW it was an array of LONG integers!)

            Kinda pooches the idea of deliberately passing an undimensioned array to a function in a DLL, but you learn to live with "REDIM myarray(0)" and just REDIMing it in the target function.

            MCM
            Michael Mattias
            Tal Systems (retired)
            Port Washington WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              Well, even so, its pretty powerful stuff to be able to pull it off successfully in terms of redimming in called procedures. I can live with 'Byref MyArray(0) As ...'
              Fred
              "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

              Comment


              • #8
                If the size of the array is indetermate, start off with:

                dim MyArray(0)

                and then REDIM PRESERVE on as needed basis.

                Should fix your problem.
                There are no atheists in a fox hole or the morning of a math test.
                If my flag offends you, I'll help you pack.

                Comment


                • #9
                  Personally, any time I declare an array using one of the scope statements (LOCAL, STATIC, et al) I always DIM it shortly after that. Doing a REDIM on an array that hasn't had a DIM shouldn't be allowed, in my opinion.

                  If you don't intially DIM the array, how does the compiler know how many dimensions the array has? In other words, if you do this declare:

                  Code:
                  LOCAL MyArray() AS LONG
                  and then pass that undimensioned array to another procedure where you do a REDIM MyArray(8), how is the compiler supposed to know that MyArray is supposed to have only a single dimension? If you have a DIM MyArray(2, 8) before passing it to that procedure, it would then know that the REDIM statement is incorrect and can flag it as an error.
                  Jeff Blakeney

                  Comment


                  • #10
                    Never having attempted the writting of any compilers, I can't speak as to how they keep track of anything really. The PowerBASIC compiler does however clearly keep track of certain things about arrays across function calls such as the re-dimensioning of arrays, UBound changes etc. If it can do that and it obviously can, it doesn't seem like such a jump to me that it could make the true/false distinction of whether a passed array is dimmed or not.

                    Now don't get me wrong Jeff. I'm not finding any fault with your comment. The way you described your procedure for handling array dimensioning is what I've been doing all along myself. Just lately I guess I've been 'pushing the envelop', so to speak, to see just what works and what doesn't, and perhaps more importantly, what I'd feel comfortable doing even if something does seem to work.

                    I find it interesting that in the case above where I passed the undimmed array, it did successfully Redim in the passed procedure, function correctly there, and return to the calling procedure with the correct UBound set. However, as Michael mentioned, accessing the array in the original procedure in any other way than testing the UBound, generates the undimmed array compiler error.
                    Fred
                    "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

                    Comment


                    • #11
                      how is the compiler supposed to know that MyArray is supposed to have only a single dimension
                      It doesn't need to. When you REDIM it, you tell it how many dimensions.

                      You may not refer to any element of the array until it's been DIM/REDIMed.

                      Curious how ARRAYATTR will tell you the array has not been dimensioned... but I don't think know how you can get to the point where you can bother asking.

                      FWIW, I NEVER use "DIM" for arrays, I always use REDIM; and I also always use the "SCOPE arrayname() AS sometype" statement at the top of my procedures.

                      DIM is funky in that it can be ignored ..
                      Code:
                      #COMPILE EXE
                      #DIM ALL
                      FUNCTION PBMAIN () AS LONG
                         CALL Foo
                      END FUNCTION
                      
                      FUNCTION FOO () AS LONG
                          DIM  A(10)  AS LOCAL LONG
                          IF 1   THEN
                              DIM  A(12) AS LOCAL LONG
                         END IF
                         MSGBOX FORMAT$(UBOUND(A,1))
                      END FUNCTION
                      Too spooky for me.

                      MCM
                      Michael Mattias
                      Tal Systems (retired)
                      Port Washington WI USA
                      [email protected]
                      http://www.talsystems.com

                      Comment


                      • #12
                        Well, Michael, I guess you just didn't read the documentation.

                        DIM is used to dimension an array which is currently inactive. If the array is already dimensioned, no further action is taken. In particular, this makes it much easier to manipulate persistent arrays (Static, Global...) in a function called frequently. You don't have to worry about coding around a DIM to avoid re-executing it after the first invocation of the function. DIM is not intended to change the dimensions of an active array.

                        REDIM is designed to change the dimensions of an active array, or to dimension an inactive array.

                        You see, these are two different statements with two different definitions. You can then choose which is best for your particular purpose of the moment.

                        One other thing... I'd recommend you avoid constructs like "IF 1 THEN...". Using a true constant in a boolean expression merely causes the encapsulated code to be executed every time, but adds extra code generation and overhead to your program.

                        Best regards,

                        Bob Zale
                        PowerBASIC Inc.

                        Comment


                        • #13
                          >I'd recommend you avoid constructs like "IF 1 THEN...

                          Sheesh. It is a "compilable example demonstrating the behavior"

                          Oh, please, bill me for that programming consultation. I could use a good laugh.
                          Michael Mattias
                          Tal Systems (retired)
                          Port Washington WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                          • #14
                            No charge, Michael. We're pleased to help free of cost.

                            I'm just glad to learn that my advice was a help to you.

                            Best regards,

                            Bob Zale
                            PowerBASIC Inc.

                            Comment


                            • #15
                              Originally posted by Fred Harris View Post
                              Never having attempted the writting of any compilers, I can't speak as to how they keep track of anything really. The PowerBASIC compiler does however clearly keep track of certain things about arrays across function calls such as the re-dimensioning of arrays, UBound changes etc. If it can do that and it obviously can, it doesn't seem like such a jump to me that it could make the true/false distinction of whether a passed array is dimmed or not.
                              Yes, the compiler keeps track of certain things about arrays. It needs to know the number of dimensions, the lower and upper bounds of each of those dimensions and the data type being stored in the array for sure. I would guess that an undimensioned array would only know the data type.

                              As to being easy to tell if an array is dimensioned or not, it is quite possible that you could dimension an array using external code (DLL, COM or whatever) that the compiler cannot check. The compiler can look through your code to see if it has been dimensioned but even that could require some tricky code. After looking through the current procedure for a DIM or REDIM, if it doesn't find one, it needs to check each procedure called to see if that array has been passed as a parameter. This could entail also checking to see if any variables are being set to the VARPTR of the first element of the array. If the array or a pointer to it is passed as a parameter and the procedure being called is external, you are out of luck. If the procedure is in the source file or one of its include files, then it needs to check the procedure definition to find out what the array or array pointer is called there and then check to see if that procedure does a DIM or REDIM on the array in question. As you can see, this is pretty ugly and probably still doesn't cover all possibilities.

                              Now don't get me wrong Jeff. I'm not finding any fault with your comment. The way you described your procedure for handling array dimensioning is what I've been doing all along myself. Just lately I guess I've been 'pushing the envelop', so to speak, to see just what works and what doesn't, and perhaps more importantly, what I'd feel comfortable doing even if something does seem to work.
                              I'm usually more comfortable not pushing the envelope but sometimes experimenting can be fun.

                              I find it interesting that in the case above where I passed the undimmed array, it did successfully Redim in the passed procedure, function correctly there, and return to the calling procedure with the correct UBound set. However, as Michael mentioned, accessing the array in the original procedure in any other way than testing the UBound, generates the undimmed array compiler error.
                              Seeing as REDIM can be used to dimension an undimensioned array (as well as change the dimensions of a dimensioned array which is the only way I use it), it seems pretty clear to me that the compiler shouldn't even be checking to see if an array has been dimensioned or not. Better to have a run time error that the array is not dimensioned or let the program GPF if it hasn't been dimensioned so that the user needs to turn #DEBUG ERROR ON to find out what went wrong.
                              Jeff Blakeney

                              Comment


                              • #16
                                As to being easy to tell if an array is dimensioned or not, it is quite possible that you could dimension an array using external code (DLL, COM or whatever) that the compiler cannot check
                                Really?

                                Take a look at the ARRAYATTR() function, attribute value zero.
                                Michael Mattias
                                Tal Systems (retired)
                                Port Washington WI USA
                                [email protected]
                                http://www.talsystems.com

                                Comment


                                • #17
                                  I haven't used the ARRAYATTR function in some time so I never thought to look at it in the documentation. However, this doesn't help the compiler to determine if you've dimensioned an array before trying to use it but it is another argument for the compiler not to bother checking to see if you dimensioned an array or not before trying to use it. PB could make it the programmer's responsibility to make sure an array is dimensioned before trying to use it just like the programmer has to make sure they don't go outside the bounds of the array index(es) (unless #DEBUG ERROR is on).
                                  Last edited by Jeff Blakeney; 15 Oct 2009, 12:49 PM.
                                  Jeff Blakeney

                                  Comment


                                  • #18
                                    > However, this doesn't help the compiler to determine if you've dimensioned an array....

                                    LOCAL X() AS LONG|SHORT|WIDGET

                                    Tells compiler all references to "X()" are to an array of LONG|SHORT|WIDGET which is all it needs to know. If an array is not dimensioned, any command used with an array argument except for a handful of the ARRAYATTR options should just error out at runtime.

                                    As far as I'm concerned it's the PROGRAMMER's responsibility to check if an array has been dimensioned, not the compiler's.

                                    MCM
                                    Michael Mattias
                                    Tal Systems (retired)
                                    Port Washington WI USA
                                    [email protected]
                                    http://www.talsystems.com

                                    Comment


                                    • #19
                                      Which is what Fred and I were discussing. The compiler was complaing that he hadn't dimensioned an array in PBMAIN when it was dimensioned in a routine that the array got passed to before it was accessed in PBMAIN. He couldn't compile and run his program, even though it would have worked fine. This is why I've been saying the same thing as you, the compiler shouildn't check to see if an array has been dimensioned.

                                      Someone should probably send this request to support. I've added it to my todo list but may not get around to it until Sunday.
                                      Jeff Blakeney

                                      Comment


                                      • #20
                                        If the compiler doesn't check to see if an array has been dimensioned then the DIM statement would be able to do the same as the REDIM statement, in fact more, for it would be able to reset the inner dimensions as well.
                                        Rod
                                        I want not 'not', not Knot, not Knott, not Nott, not knot, not naught, not nought, but aught.

                                        Comment

                                        Working...
                                        X