Shop OBEX P1 Docs P2 Docs Learn Events
A little help with understanding this C code — Parallax Forums

A little help with understanding this C code

ke4pjwke4pjw Posts: 1,082

I am in the process of trying to convert some code from C to Spin2 and am getting a little hung up on what is happening here. It appears that the following occurs:

  1. A structure is instantiated that is modeled on the BitBucket struct and is named codewords.
  2. Possibly a byte array is created that is the size of bb_getBufferSizeBytes(moduleCount)? I don't know. I don't really understand what is happening at this step.
  3. The address of the codewords structure, address of codewordBytes array?, and size of the codewordBytes array is passed to bb_initBuffer.
    struct BitBucket codewords;
    uint8_t codewordBytes[bb_getBufferSizeBytes(moduleCount)];
    bb_initBuffer(&codewords, codewordBytes, (int32_t)sizeof(codewordBytes));

Am I correct in those assumptions?

Also, these dynamic allocations of memory and variables are really cooking my noodle. I suppose there is no good strategy to deal with this in spin. It's a whole different way of doing things.

Any help would be appreciated. I haven't programmed any C or C++ in 20+ years. C# is just easier.

Comments

  • Yep, that's exactly what it does. No, this isn't what good C code looks like. The variable-sized array is considered a dubious feature (only added in C99, later made optional). Unless bb_getBufferSizeBytes is a macro of course, in which case they should make it uppercase or something.

  • ke4pjwke4pjw Posts: 1,082

    bb_getBufferSizeBytes returns a uint16_t so to cover all use cases, I need to allocate 64Kbytes. Ug. This is annoying. Maybe I can test edge cases and see what the actual limit should be.

  • ke4pjwke4pjw Posts: 1,082

    Follow-up question:
    Is this struct really just a LONG, WORD, and BYTE that is really a pointer?

    typedef struct BitBucket {
        uint32_t bitOffsetOrWidth;
        uint16_t capacityBytes;
        uint8_t *data;
    } BitBucket;
    

    Seems really odd that a pointer would be of type BYTE.

  • It's a pointer to a byte (in this case multiple bytes, as it were). All pointers are 32 bit on a 32 bit machine.

  • ke4pjwke4pjw Posts: 1,082
    edited 2024-04-12 20:08

    Got it. That really helps Ada! Thank you!

  • ke4pjwke4pjw Posts: 1,082

    OK, I want to make sure I understand this correctly.

    BitBucket *bitGrid is just a pointer to the actual struct that gets passed? The real structs have the ampersand. &modulesGrid and &isFunctionGrid

    Is my understanding correct?

    static void bb_initGrid(BitBucket *bitGrid, uint8_t *data, uint8_t size) {
        bitGrid->bitOffsetOrWidth = size;
        bitGrid->capacityBytes = bb_getGridSizeBytes(size);
        bitGrid->data = data;
    
        memset(data, 0, bitGrid->capacityBytes);
    }
    
    // A little further down the code.......
    
        BitBucket modulesGrid;
        bb_initGrid(&modulesGrid, modules, size);
    
        BitBucket isFunctionGrid;
        uint8_t isFunctionGridBytes[bb_getGridSizeBytes(size)];
        bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size);
    
  • evanhevanh Posts: 15,202
    edited 2024-04-16 06:52

    Roughly, yes. But ampersand just means "address of", for assigning to a pointer. It can be applied to even a pointer, to make a pointer to a pointer.

    And when passing a pointer, it's only the pointer being passed. The structure stays put.

  • ke4pjwke4pjw Posts: 1,082

    So, in the code above, a pointer to isFunctionGrid, which is a struct of type BitBucket, is passed into bb_initGrid as bitGrid. Where inside bb_initGrid assignments for bitGrid properties are done, they are really making assignments to isFunctionGrid properties. Is that Correct?

  • evanhevanh Posts: 15,202
    edited 2024-04-17 05:04

    Yes. The pointer provides access to the original definition (instance).

    Actually, that example has a quirk of C in it.

        BitBucket isFunctionGrid;
        uint8_t isFunctionGridBytes[bb_getGridSizeBytes(size)];
        bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size);
    

    The passing of byte array isFunctionGridBytes to function bb_initGrid() is missing its address-of ampersand. This is okay syntactically, if a little ambiguous. C has no direct passing of arrays/structures, therefore the compiler assumes address-of.
    Err, looks like I am a little wrong there. Just placing an ampersand in front of an array's symbol will produce a type mismatch warning. The verbose version of that example would be bb_initGrid(&isFunctionGrid, &isFunctionGridBytes[0], size);. So structs and arrays differ in that respect.

  • ke4pjwke4pjw Posts: 1,082
    edited 2024-04-17 15:41

    Thanks @evanh ! This helps.

    I have taken 5 pages of handwritten notes about how the structure of this C program works. Hopefully over the next few days I can get it converted to Spin. I am sure this would be an easy task for most. I have started and stopped half a dozen times on this project. I have a feeling it will work out this time. I now have actual hardware the C code was written for and finally figured out how to have debug info output to the serial connection. Arduinos are weird.

    --Terry

  • ke4pjwke4pjw Posts: 1,082
    edited 2024-04-17 22:46

    And more dumb questions.....

    In the code I have identified 3 structs that are instantiated of type BitBucket, however it appears they use two separate syntaxes.

    When I hover over BitBucket for codewords, it says "struct BitBucket"

    struct BitBucket codewords;

    When I hover over BitBucket for modulesGrid or isFunctionGrid, I get "Type-Alias Bitbucket"

    BitBucket modulesGrid;

    BitBucket isFunctionGrid;

    Is this just a style difference in syntax, or is does the use of "struct" before the datatype make a difference?

  • evanhevanh Posts: 15,202
    edited 2024-04-18 00:23

    Short answer: There will be a typedef BitBucket BitBucket somewhere I suspect. I've never tried using identical spelling like that but I presume it's legal.
    I'll let someone else better versed in the formal C descriptions fill you in more.

  • ke4pjwke4pjw Posts: 1,082

    Yes, I see the type definition. I just don't understand the difference between the two syntaxes above.

  • evanhevanh Posts: 15,202
    edited 2024-04-18 06:20

    Structure namespace is separate from the typedef namespace, so they can use identical names.

    For those simple uses in your example they are the same. You can define a structure with either struct BitBucket <symbol name> or, because of the typedef, BitBucket <symbol name> defines the same.

    Structure namespace has an important use case when declaring self-referencing structure types. Structure declarations for linked lists being the poster child of this.

  • ke4pjwke4pjw Posts: 1,082
    edited 2024-04-18 13:56

    It was my understanding that the typedef defines the structure and names it.

    typedef struct BitBucket {
        uint32_t bitOffsetOrWidth;
        uint16_t capacityBytes;
        uint8_t *data;
    } BitBucket;
    

    And that the following creates an instance of the structure and names that instance.

    struct BitBucket codewords;

    And the following also creates an instance of the structure and names that instance.

    BitBucket isFunctionGrid;

    I am just trying to understand the difference between the last two, or if this was just inconsistent coding style.

    I appreciate your patience. Trying to understand code when you don't know the little caveats of the language can be difficult.

  • evanhevanh Posts: 15,202
    edited 2024-04-18 14:21

    "Defining" is the C term for creating an instance. "Declaring" is constructing the object type so to speak.

    Typedef'ing is an optional alternative path, and entirely unneeded. I think it's solely available as a tidiness option.

    The structure type can be declared as so:

    struct BitBucket {
        uint32_t bitOffsetOrWidth;
        uint16_t capacityBytes;
        uint8_t *data;
    };
    

    And then an instance defined with struct BitBucket <symbol_name>;. Which is one of the solutions employed in your example.

    BitBucket isFunctionGrid; can just as easily be defined this way: struct BitBucket isFunctionGrid;
    And while the typedef exists then struct BitBucket codewords; can be defined this way: BitBucket codewords;

    Hope I've covered the bases this time. :)

  • evanhevanh Posts: 15,202

    Huh, I only just paid attention to what Ada was talking about. That variable sized array is totally new to me. I've only ever known ANSI C, aka C89. So I can't see how that works since there is no such thing as memory management in C natively. That's not how C was meant to be.

  • ke4pjwke4pjw Posts: 1,082

    Thanks @evanh ! For the life of me I couldn't understand why it was written two different ways to define it. I didn't know if it could have been a difference in scoping, or something else. I really appreciate your help and showing me the correct verbiage.

  • evanhevanh Posts: 15,202
    edited 2024-04-18 15:42

    Cool.

    Speaking of C ways of doing things. The ternary operator in C, a ? b : c, it always felt an odd fit to me. As if it had been grafted from an older language. I've just looked it up on Wikipedia and sure enough it is almost verbatim from the 1963 CPL language, then BCPL, then C. https://en.wikipedia.org/wiki/Ternary_conditional_operator

  • RossHRossH Posts: 5,351

    @evanh said:
    Huh, I only just paid attention to what Ada was talking about. That variable sized array is totally new to me. I've only ever known ANSI C, aka C89. So I can't see how that works since there is no such thing as memory management in C natively. That's not how C was meant to be.

    Indeed. VLAs were added in C99 and subsequently removed as a requirement again in C11. Which just goes to show that everyone makes mistakes. Since C compilers are no longer required to support VLAs they should never be used if you want portable C. Use malloc instead.

    Ross.

Sign In or Register to comment.