Shop OBEX P1 Docs P2 Docs Learn Events
Struct Initialization in Simple IDE — Parallax Forums

Struct Initialization in Simple IDE

DomanikDomanik Posts: 233
edited 2014-12-07 11:26 in Learn with BlocklyProp
My struct "COORD" will not initialize unless I have a 1ms pause in the function. Or I copy a value to a global "Fval". I suspect it is initialized but something throws away the pointer to my struct before it's passed back to my calling routine. If so why does a 1ms pause or global activity make a difference. It's a mystery to me; I've spent a couple of hours trying all different combinations. The reason I don't simply put in a 1ms pause work-around, is the next time it may slip through unnoticed. Thanks for looking.
#include "simpletools.h"

typedef struct {float x,y,z;} COORD;

float Fval=-1;

int main()
{
  float xcoord=-1;
  print("go\n");
  COORD pa, *coord_fn();
                             //  print(" &pa = %04x \n", &pa);
  pa = *coord_fn();
                             //  pa.y = 21;
                             //  print(" &pa = %04x \n", &pa);
  xcoord = pa.x;
  print("\n\n  xcoord = %4.0f   %4.0f \n", xcoord, pa.y);

}  

COORD *coord_fn()
 { 
   COORD pb;// = { 31, 32, 33};
   pb = (COORD){ 31, 32, 33};
//   Fval = pb.x;
//   pause(1);
                             //   print(" &pb = %04x ", &pb);
	 return &pb;
}

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2014-12-03 22:17
    pb is a local structure which only exists in the context of coord_fn(). Once you return it's gone. You need a more permanent way of allocating this structure.
  • Dave HeinDave Hein Posts: 6,347
    edited 2014-12-04 05:50
    Instead of declaring coord_fn as (COORD *) you could just declare it as COORD. That way you would return the value of pb instead of a pointer to it. Or you could pass a pointer to pa and have the function coord_fn copy the contents of pb to pa.
  • edited 2014-12-04 10:21
    Here is the first of two structure usages that seem to be common with Propeller applications. In this case, the structures are dynamically created in the main function. Their pointers are then passed to functions that do work on them, like modify_coord and display_coord in this case. Since the main function tends not to fall out of scope, the instances are alive for the duration of the application.
    /*
      Structure experiment 1.c
    */
    
    #include "simpletools.h"
    
    typedef struct coord_st{float x,y,z;} coord_t;
    
    void modify_coord(coord_t *pointer);
    void display_coord(coord_t *pointer);
    
    int main()
    {
      coord_t pa1 = {1.0, 2.0, 3.0};
      coord_t pa2 = {4.0, 5.0, 6.0};
    
      print("Before\n");
      print("pa1\n");
      display_coord(&pa1);
      print("pa2\n");
      display_coord(&pa2);
      
      modify_coord(&pa1);
      modify_coord(&pa2);
      
      print("\nAfter\n");
      print("pa1\n");
      display_coord(&pa1);
      print("pa2\n");
      display_coord(&pa2);
    }  
    
    void modify_coord(coord_t *pointer)
    {
      pointer->x = pointer->x + 1.0;
      pointer->y = pointer->y + 1.0;
      pointer->z = pointer->z + 1.0;
    }
    
    void display_coord(coord_t *pointer)
    {
      print("pointer->x = %f\n", pointer->x);
      print("pointer->y = %f\n", pointer->y);
      print("pointer->z = %f\n", pointer->z);
    }
    


    The before and after states of the structures can be observed using SimpleIDE's Run with Terminal button.

    attachment.php?attachmentid=112150&d=1417716447
    544 x 461 - 203K
  • edited 2014-12-04 10:59
    The second common propeller usage is when a Simple Library is designed to dynamically create instances of a structure on-demand. Note that now the pa1 and pa2 are declared as pointers, and no & pointer operators are used when passing structure addresses to the functions. That's because the malloc (short for memory-allocate) in the create_coord function sets aside enough memory to fit the structure and returns the address pointer of that memory. The rest of the functions can treat that memory as though it is the structure.

    This is under-the-hood of the approach some simple libraries use to create multiple instances of the same process running in different cogs. All the process settings are stored by structure, and each instance of the same code running in different cogs looks to a different structure for its settings.

    When you run the code, the result will be the same as the screen capture from the previous post. The only difference is that the structure was created dynamically and is referenced by memory address throughout.
    /*
      Structure experiment 2.c
    */
    
    #include "simpletools.h"
    
    typedef struct coord_st{float x,y,z;} coord_t;
    
    coord_t *create_coord(float x, float y, float z);
    void modify_coord(coord_t *pointer);
    void display_coord(coord_t *pointer);
    
    int main()
    {
      coord_t *pa1 = create_coord(1.0, 2.0, 3.0);
      coord_t *pa2 = create_coord(4.0, 5.0, 6.0);
      
      print("Before\n");
      print("pa1\n");
      display_coord(pa1);
      print("pa2\n");
      display_coord(pa2);
      
      modify_coord(pa1);
      modify_coord(pa2);
      
      print("\nAfter\n");
      print("pa1\n");
      display_coord(pa1);
      print("pa2\n");
      display_coord(pa2);
    }  
    
    coord_t *create_coord(float x, float y, float z)
    { 
      coord_t *pointer = (coord_t *) malloc(sizeof(coord_t)); 
      pointer->x = x;
      pointer->y = y;
      pointer->z = z;
      return pointer;
    }
    
    void modify_coord(coord_t *pointer)
    {
      pointer->x = pointer->x + 1.0;
      pointer->y = pointer->y + 1.0;
      pointer->z = pointer->z + 1.0;
    }
    
    void display_coord(coord_t *pointer)
    {
      print("pointer->x = %f\n", pointer->x);
      print("pointer->y = %f\n", pointer->y);
      print("pointer->z = %f\n", pointer->z);
    }
    


    P.S.

    The malloc function is part of stdlib.h, which simpletools includes.

    C programmers prefer to check the result to find out if there was enough memory. So you might see a main function that looks more like this:
    // Alternate main function
    
    int main()
    {
      coord_t *pa1 = create_coord(1.0, 2.0, 3.0);
      coord_t *pa2 = create_coord(4.0, 5.0, 6.0);
      
      if(pa1 == NULL || pa2 == NULL)
      {
        print("An error occurred");
      }
      else
      {
        print("Before\n");
        print("pa1\n");
        display_coord(pa1);
        print("pa2\n");
        display_coord(pa2);
        
        modify_coord(pa1);
        modify_coord(pa2);
        
        print("\nAfter\n");
        print("pa1\n");
        display_coord(pa1);
        print("pa2\n");
        display_coord(pa2);
      }    
    }  
    
    
  • DomanikDomanik Posts: 233
    edited 2014-12-07 11:26
    Domanik wrote: »
    why does a 1ms pause or global activity make a difference. It's a mystery to me...
    The pattern is interesting. By knowing ahead of time the address location that the struct will occupy (within the function) and by manually setting a pointer to that value, I was able to look for expected values and test various conditions within the function. One of my assumptions was the values of the function's local strut should be transferred to the main struct upon return to main. And that this transfer would take place before the system reabsorbed the memory and put it to other uses. I believed the values should still be there if I grab them right away.

    They were there all right, that is, as long as there was a way for code outside the function to "observe" the function's struct value. For example if a global or a print was used within the function or using one of the values or any number of references to the struct values. But if my external reference went away so did my data even though the struct was declared and initialized. So it became like looking in the box to see if the cat is dead or alive or both? However, by knowing the future struct address in main I got around this and could look at data without the system knowing what I was up to. Then it was possible to apply different conditions in the function to observe the cat (and the data too).

    My conclusion is that the compiler is very clever to optimize code and, in this case, when it determined data was not used outside the function it wouldn't set aside memory or initialize the values. When there was any kind of external reference the values would appear again. It may even be the function never existed?

    I tried to simulate the pause with a for loop. the count was 9,000,000,000 - and another within that. There was no noticeable delay and no data appeared. However the pause(1) was observable outside the function therefore the function was not ignored. That was interesting.

    Although returning data with a local struct is not advisable I thought I could get away with it since the data "had" to be there, and I would snatch it before it disappeared.

    Andy, Thank you for an excellent presentation of using structures. And kuroneko, you are right, it is best to assume a local structure only exists while in context and avoid trouble. Dave I haven't tried your suggestion but will. Thank you for taking the time to post.
    Dom...
Sign In or Register to comment.