Topic
7 replies Latest Post - ‏2011-09-20T10:14:59Z by Mathias Mamsch
llandale
llandale
2943 Posts
ACCEPTED ANSWER

Pinned topic Alias-Addr woes

‏2011-09-15T16:51:20Z |

Have only a basic grasp of Aliasing in "C" language, but seem to have no clue about using "*" and "addr_". Can someone please point me to some document that explains that stuff simply?

For the immediate case, is it possible to write function MakeAlias() below such I get this output:
C = A0 A1 A2
C = B0 B1 B2

string A[] = {"A0", "A1", "A2"}
string B[] = {"B0", "B1", "B2"}
string C[0]
 
void    PrintC()
{  int i
   print "C ="
   for (i=0; i<sizeof(C); i++) print "  " C[i]
   print "\n"
}  // end PrintC()
 
void    MakeAlias(string in_AorB[], &outC[])
{  // Define outC to have same base address as input vector in_AorB)
        // ????
}
 
MakeAlias(A, C)
PrintC()        // want "C =  A0  A1  A2"
MakeAlias(B, C)
PrintC()        // want "C =  B0  B1  B2"


Note that I want to change the base address of C dynamically, not when I declare the variable. Are there similar tricks to other data structures like DxlObject and Array?

- Louie

Updated on 2014-01-08T15:24:55Z at 2014-01-08T15:24:55Z by iron-man
  • SystemAdmin
    SystemAdmin
    3180 Posts
    ACCEPTED ANSWER

    Re: Alias-Addr woes

    ‏2011-09-15T18:50:53Z  in response to llandale

    If I'm understanding your desire, you can do it without addr_ or *.

    Here are three ways to do the same thing.
     

    // NOTE the [] before the {
    string referTo(string someStringArray[])[] {
        return someStringArray;
    }
     
     
    void showStringArray(string name,
                         string someStringArray[]) {
        if (null(someStringArray)) {
            print name " is null\n\n";
        } else if (sizeof(someStringArray) == 0){
            print name " has no elements\n\n";
        } else {
            int i;
            for (i = 0; i < sizeof(someStringArray); i++) {
                print name "[" (i) "]=" someStringArray[i] "\n"
            }
            print "\n"
        }
    }
     
    string A[] = {"A0", "A1", "A2"}
    string B[] = {"B0", "B1", "B2"}
    string C[] = {};
     
    print "Original:\n"
     
    showStringArray("A", A);
    showStringArray("B", B);
    showStringArray("C", C);
     
    print "After C = A\n"
    C = A;
    showStringArray("C", C);
     
    print "After C = B\n"
    C = B;
    showStringArray("C", C);
     
    C = referTo(A);
    print "After C = referTo(A)\n"
    showStringArray("C", C);
     
    C = referTo(B);
    print "After C = referTo(B)\n"
    showStringArray("C", C);
     
    C = (addr_ A);
    print "After C = (addr_ A)\n"
    showStringArray("C", C);
     
    C = (addr_ B);
    print "After C = (addr_ B)\n"
    showStringArray("C", C);
    

     


    Output is:
    Original:
    A[0]=A0
    A[1]=A1
    A[2]=A2

    B[0]=B0
    B[1]=B1
    B[2]=B2

    C has no elements

    After C = A
    C[0]=A0
    C[1]=A1
    C[2]=A2

    After C = B
    C[0]=B0
    C[1]=B1
    C[2]=B2

    After C = referTo(A)
    C[0]=A0
    C[1]=A1
    C[2]=A2

    After C = referTo(B)
    C[0]=B0
    C[1]=B1
    C[2]=B2

    After C = (addr_ A)
    C[0]=A0
    C[1]=A1
    C[2]=A2

    After C = (addr_ B)
    C[0]=B0
    C[1]=B1
    C[2]=B2

     

    Updated on 2014-01-08T15:25:13Z at 2014-01-08T15:25:13Z by iron-man
  • Doug.Zawacki
    Doug.Zawacki
    80 Posts
    ACCEPTED ANSWER

    Re: Alias-Addr woes

    ‏2011-09-15T18:58:30Z  in response to llandale

    Louie,
    I know of no documentation of what you are looking for in DXL. But, Here is how I was able to work through your problem. If you need more explaination I'll be happy to elaborate but I think you'll understand once you see the code. Good luck.

    string A[] = {"A0", "A1", "A2"}
    string B[] = {"B0", "B1", "B2"}
    int iptrToStringArray = 0
     
    void    PrintC()
    {  int i
       print "C ="
       for (i=0; i<sizeof((addr_ iptrToStringArray )); i++) print "  " (addr_ iptrToStringArray )[i]
       print "\n"
    }  // end PrintC()
     
    void    MakeAlias( string in_AorB[], int &x)
    {  // Define outC to have same base address as input vector in_AorB)
            // ????
            x = ( int (addr_ in_AorB))
    }
     
    MakeAlias( A,iptrToStringArray )
    PrintC()        // want "C =  A0  A1  A2"
    MakeAlias(B, iptrToStringArray )
    PrintC()        // want "C =  B0  B1  B2"
    
    Updated on 2014-01-08T15:25:29Z at 2014-01-08T15:25:29Z by iron-man
  • Mathias Mamsch
    Mathias Mamsch
    1921 Posts
    ACCEPTED ANSWER

    Re: Alias-Addr woes

    ‏2011-09-15T21:41:53Z  in response to llandale

    Ok, lets try to get this straight and begin with variables in general. What is a variable? A variable is a bunch of memory that you assign a name to. If you do in DXL:

    int a
    a = 66
    

    If you then press run, then the DXL interpreter will read the first line of your program and say "well that guy wants to declare an integer variable, so lets take 4 Bytes of memory, lets see, oh I got something free at position 1235223355, lets name this memory location "a". Then it will read the second line and say "oh bull, now there is that 'a' again the user wants to assign something to it, lets write that number 66 to the memory location I memorized." and writes the value 66 to memory location 1235223355. But not only did DXL remember the name of the variable. It also memorized that a number is stored at that memory location.


    So what is a type cast? A type cast tells DXL "Well I know you memorized that a is referring to a number and is stored at memory location 123522335 but I really want you to treat the variable "a" as a 'char' type for a second. That is what 'addr_' does for you. Without the need to understand to internals you can just memorize the following pattern: ((addr_ var) NewType) will tell DXL please treat the variable 'var' as if it would be of type 'NewType'. Example:

    int a = 66 
    print ((addr_ a) char) "!\n"
    print (charOf 66) "?\n"
    


    That is all one needs to know about "attr_" for starters. Lets talk about references. A reference is basically a new name for the same memory location.

    int a = 66 
    int &b = a
    

    So here DXL will say "Ok... variable 'a' at memory location 1235223355. been there, done that. But now that guy wants to declare another name for the same memory location 1235223355.". And therefore when you do b = 67 you will change a too, because that statement will write the value 67 to the same memory location 1235223355, that the name a already is assigned to. But what is the type of b that DXL remembers? Obviously not 'int' because if you wrote "int b = a" then it would be 'int'. The type of b is 'Reference to int'. And now the price question: What would happen if we told DXL to interpret 'b' as an integer?

    int a = 66
    int &b = a
    print ((addr_ b) int)
    

    Does that print '66'? No. It prints our memory location 1235223355. So obviously a reference is a variable that contains a memory location. But how does DXL know that if you do 'b = 70' to not change the stored memory location to memory location 70, but to change the value at the stored memory location? Well there is a perm for it:

    _k ::= (_k&, _k)      // this is in the perms list where '_k' is just short for "replace me by ANY type", e.g. 
    int   ::= (int  &, int)  // or 
    char  ::= (char &, char)
    

    So what this = operator does is to take a memory location (a reference) and write the value of the second parameter to exactly that memory location. But there is another important thing to know about references. If you declare a function that takes a reference as a parameter then something cool happens:

    int func (int &a) { a = 7 }
    int x = 65
    func x
    

    The function wants a memory location as a parameter, but we give it a variable of type 'int'. Obviously we do not want the function to think that our value 65 is the memory location. Why does this work? Because DXL knows that whenever you make a function that wants to have a reference, and you pass a normal variable to it, that it will pass the memory location of that variable instead. But DXL can even do more. If you pass a reference variable to a function that takes no reference, then DXL will magically know to pass the value of that variable.

    So lets look again at our perm above "_k ::= (_k&, k)". This perm will therefore also take a normal variable as a parameter. So if you do:

    int a = 64
    a = 65
    

    Then DXL will call exactly the same perm, it will pass the memory location of the variable a to the = operator perm that will simply write the second parameter value (65) to that memory location and therefore change the value of a. But there is a huge catch with references. What about that code:

    int a = 64 
    int b = 65
    int &ref = a
    ref = b   // This will not change the reference, but the value of a, since it will call the ::= perm above. 
    print a
    

    So due to the super powers of references (automatically making references out of normal variables) we cannot change a reference once it has been assigned. That is where pointers come into play. A pointer is a memory location, just like a reference but there are no super powers attached.

    int x = 63
    // pointers are declared by just adding a * in the declaration
    int *ptr = x // fails ...
    

    SO DXL does not know how to make a memory location out of the variable x. So how can we get a pointer from a normal variable and how can we get a normal variable from a pointer? Fortunately DOORS has perms for that:

    _k* ::(&) (_k&)    // make a pointer from a reference or normal variable, e.g. return the memory location
    _k&  ::(*) (_k*)   // make a reference from a pointer to use the reference super powers to read the value or pass it to functions.
    

    Great! Now we can do:

    int x = 63
    int y = 64
    int *ptr = &x   // &x makes a pointer to x of our variable x, i.e. let 'ptr' point to x
    print (*ptr)    // *ptr will give us the value of x 
    ptr = &y        // reassign the pointer to point to y 
    *ptr = 100      // change the value of the variable we point to
    

    So the nice thing is pointers can be reassigned (which gets us to your array stuff in a second). But first take a look at the last line! The *ptr makes a reference out of pointer. So this last line will again just call our "_k ::= (_k&, k)" perm, therefore storing the value 100 in the memory location that is stored in our pointer.

    So this is basically all folks ... How do you now declare a pointer to an array?

    Just like this:

    int a[] = {1,2,3}
    int b[] = {4,5,6} 
    int (*c)[]    // a pointer to an array. The braces are needed, to tell DXL its a pointer to an array and not an array of pointers.
    c = &a 
    print (*c)[2]
    c = &b 
    print (*c)[2]
    

    Now you might want to read all that again, not because it will get clearer, but just because I spend a lot of time writing it ;-)

    Regards, Mathias

     

     


    Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

     

    Updated on 2014-01-08T15:29:23Z at 2014-01-08T15:29:23Z by iron-man
    • kbmurphy
      kbmurphy
      160 Posts
      ACCEPTED ANSWER

      Re: Alias-Addr woes

      ‏2011-09-15T23:31:15Z  in response to Mathias Mamsch
      Mathias,

      Thank you so much for the explanation. I wish you were one of my comp sci professors when I was in college.

      Here's what always confused me about pointers and references: other than objects and passing a variable by reference to a function, why use them? Your examples all use int...can you tell me why you would ever want to have a pointer to an int, or why you would ever want to know the memory address being used by an int?

      Thanks again. Great job explaining this complex stuff.

      Kevin
      • Mathias Mamsch
        Mathias Mamsch
        1921 Posts
        ACCEPTED ANSWER

        Re: Alias-Addr woes

        ‏2011-09-18T12:43:34Z  in response to kbmurphy
        Well historically you can think of points as the predecessor of references. The main use of pointers was to pass parameters 'by reference' to functions, which became obsolete by the introduction of reference types. Additionally in other languages pointers are necessary for dynamic memory allocation. Imagine a struct and the declaration of a variable of it. When you do so in C, the C compiler will allocate the space for global variables directly in the compiled code and for local variables on the stack. Both have drawbacks, because the first one increases the size of your executable and the stack is only good for small, temporary allocations. Therefore pointers were declared and only at runtime some memory was allocated and the pointer set to the allocated memory.

        In DXL the memory allocation is hidden from the programmer, since all dynamically allocated objects in DXL are hidden behind the well known datatypes like Buffers, Skips, etc. So all those types behave like pointers, that are allocated by their create functions at runtime, but the programmer does not see it, which is imho the main reason for all that 'alias' confusion.

        Anyway using pointers in DXL is 98% pointless. Pointers do not make sense for all dynamically allocated datatypes, because the already behave like pointers (e.g. assignment making an 'alias', not a copy). So this leaves the question for pointers to one of the basic types. The only normal use of a pointer in DXL that I can think of, is to make a global reassignable reference. But even that is of questionable use. Imagine you have a library, that that needs to store a reference to some integer or string variable from the user program to store some value there later (e.g. an error code, or some data that comes from an event source like a timer or a GUI). Then you could use a pointer to store the reference, and assign the value to the user variable at a later time. But you could do the same, by declaring a global variable and supply a function for the user to read the value with. So also no real use here.

        I personally love to use int pointers for memory manipulations. For example to store the contents of an integer array in a string you could take each integer, cast to a string, append to a buffer and there you go, but this not only wastes string table space, but is also incredibly slow. You can do the same lightning fast, if you use a pointer to a buffer memory, convert the integers to strings by bit manipulation, which I did for a base64(int[]) algorithm. In other cases where you want to manipulate the memory structure of normal DXL types, e.g. for implementing new functions like (sizeOf (Array)) this is the way to go. So this is the main use of pointers for me.

        Regarding why someone would want to know the memory location of a variable, this is much more useful. For debugging it is very valuable to know for example to WHICH Buffer you are writing too (when you get that crash and need to find the code where you decallocated the buffer). Also to do virtual functions (i.e. store functions in a variable and call them from there) you store the address of the function in an integer variable. Virtual functions are very valuable for library code, e.g. an error framework where the user code can specify which function to call in case of an error.

        Hope that answers your question. Regards, Mathias


        Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
        • llandale
          llandale
          2943 Posts
          ACCEPTED ANSWER

          Re: Alias-Addr woes

          ‏2011-09-19T14:11:35Z  in response to Mathias Mamsch
          This is great thanks. Some of it I have vague cob-web memories of, some of it not. Could you please post an example and explanation of how this works vis-a-vis using Functions as parameters?

          When I was in school, and yes they had them then, it was all call-by-reference. When I came across the notion of call-by-value, I presumed it meant the code would find some memory, copy the value there, and then call that by-reference and it wouldn't matter if the function wrote to it since its de-allocated upon return. I see now, however, that they made everything call-by-value and had to have special processing to handle the call-by-reference cases, the 'Reference'.

          On a side note, I've always thought it superior to allow the calling function to decide which parameters are call-by-value and which call-by-reference, and let the called functions presume all the parameters are call-by-reference. Thus the caller can see and can control which of its variables are in fact modified by the called function and the person writing and reading the code can much more easily comprehend what is going on.

          Lets look at this line in an already written main program:

          string GetABC(string in_param1, int &inout_param2){..}

          string Result = GetABC(param1, param2)

          I think we can pretty much figure out what its doing. But it there is some serious uneasiness in not knowing whether GetABC modifies either of the parameters, and that uneasiness can only be satisfied by looking up the function.

          string Result = GetABC(param1, ^param2)

          Where in my new language, "^" means "call-by-ref.." ..err.. "allow function to change".

          Now we see that param1 is NOT changed, and param2 is probably changed. That information is very useful to understanding the main. Also, if the calling program does not use the "^" sign, then we know the value of param2 is NOT changed by the function, and we really don't care if that function is written to change it or not; thus we deny the change.

          The function is likewise written to declare which parameters are subject to changing inside the function with the standard call-by-reference indicators. Thus, the parameter is only changed when both the main and the function say so.

          As for debugging: While I indeed had use for clever debuggers in the old days with tricky and complicated programs (2ms delta increase in fuel injection), the simple nature of DXL so far means I've never had a problem using only print statements, or just relying on the text of the run-time error to figure it out. That may change a little as I advance into the 1990s and start using some non-linear data structures. Even so, "good form" practices like well-formed functions and documenting the strucures and always deleteing the structure in the same function at the same level that creates it helps a lot: if you draw a line from the start-loop statement to the stop-loop statement, none of the lines should cross; ditto for the create and the delete structure statements.
          • Mathias Mamsch
            Mathias Mamsch
            1921 Posts
            ACCEPTED ANSWER

            Re: Alias-Addr woes

            ‏2011-09-20T10:14:59Z  in response to llandale

            With function pointers/references it is a little tricky in DXL. The underscore types do not apply to functions, therefore the assignment perms do not work with variables of type function.

            int func(int a) { return 0 }
            int underScoreFunc(_k x) { return 0 }
            underScoreFunc func // Error ...
            

            You can declare function pointers, but I do not know about a way to call them directly from the pointer.

            void (*funcPtr)(int, int) 
             
            void func(int a, int b) { print a b "\n" }
             
            funcPtr = addr_ func
             
            print ((addr_ funcPtr) int) // print memory address stored in pointer
            print ((addr_ func) int)  // print memory address of function
            

            You cannot declare function variables, since in DXL the syntax is already taken by forward declarations. Therefore I tend to use the following code structure for storing function pointers (in an integer variable)

            // *************** Library Code ************
            int adFunc  // we store the address of the function here
             
            // this function is for de-referencing (i.e. converting the function address
            // to a 'variable' of type function)
            int funcProxy(int f(int,int), int a, int b) { return f(a,b) }
             
            // this is a convenience function to allow user code to set the function
            // that hides the global variable from user code
            void setFunc(int f(int, int)) { adFunc = (addr_ f) int }
             
            // this is the function that will be used to call the proxy
            // this hides the global variable from user code
            int func (int a, int b) { funcProxy (addr_ adFunc, a,b) }
             
            // ************************ User Code *********************
            // declare two prototypes
            int func1(int a, int b) { print "Func 1:" a "," b "\n"; return a }
            int func2(int a, int b) { print "Func 2:" a "," b "\n"; return a }
             
            // Call first function
            setFunc func1 
            func (3,4) 
             
            // Call second function
            setFunc func2
            func (4,5)
            

            Hope that helps, Regards, Mathias

             


            Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

             

            Updated on 2014-01-08T15:30:57Z at 2014-01-08T15:30:57Z by iron-man