0

I did extensive research before asking this question here, but I couldn't find an answer.

I have a simple struct that contains an array of another struct and the total:

typedef struct {
    int total;
    struct Struct1 arrayOfStructs[];
} Struct2;
Struct2 _myVar;  // the variable I need
  • arrayOfStructs[] should be a C array containing structs of type Struct1 (this syntax is not valid, because you need a constant value)
  • total is the count of the arrayOfStructs
  • (Struct1 is not relevant, it just contains some primitive data)

The problem is that I want this array to be dynamic. I want to be able to do this:

for (int i = 0; i < _myVar.total; i++) {
    NSLog(@"%d", _myVar.arrayOfStructs[i].index);
}

I tried the following:

typedef struct {
    int total;
    struct Struct1 *arrayOfStructs;  // USING A POINTER HERE
} Struct2;
Struct2 _myVar;  // the variable I need

and for adding values:

int total = ++_myVar.total;  // increase total count
_myVar.arrayOfStructs = realloc(_myVar.arrayOfStructs, total * sizeof(Struct1));
_myVar.arrayOfStructs[total - 1] = {1, 5.3};  // appending a new value in the array

The above code adds the value correctly, but after passing it into NSData and retrieving it, the array items are lost, and I end up with some bad pointers:

[NSData dataWithBytes:&_myVar length:sizeof(_myVar) + sizeof(Struct1) * total];

I suspect this is because I need to do sizeof(_myVar) + sizeof(Struct1) * total instead of just sizeof(_myVar), so because of the pointer addresses that are different I end up with bad data. I think I'm not copying the data correctly into my structure.
Any help would be greatly appreciated!

2
  • 3
    If you use a pointer, struct Struct1 *arrayOfStructs;, you don't know where the payload resides. You can be fairly sure that it won't be directly after the Struct2 _myVar;. So if [NSData dataWithBytes:&_myVar length:sizeof(_myVar) + sizeof(Struct1) * total]; takes sizeof _myVar + total*sizeof(Struct1) bytes starting from &_myVar; that will take an int, a pointer, and a lot of garbage you have no right to. Commented Mar 20, 2013 at 22:17
  • I understand, thanks. And how could I do what I need then, I can even change the way I add data into the NSData, but I'm not sure how to do it. Commented Mar 20, 2013 at 22:22

4 Answers 4

2

You need to write the contents of Struct1 separately:

myData = [NSMutableData dataWithBytes:&_myVar length:sizeof(_myVar)];
[myData appendBytes: _myVar.arrayOfStructs length: (_myVar.total * sizeof(Struct1)];

When you read it back, you need to read the first part of it, ignore the value of arrayOfStructs that you read, reallocate it based on the length, and read the remainder of it into the newly allocated pointer.

Sign up to request clarification or add additional context in comments.

Comments

1

_myVar is a Struct2 variable. So it only has an integer and a pointer. The array of structs doesn't belong to _myVar.

And so, there is a bug with the length for the NSData. At &_myVar there is only a struct of size sizeof(Struct2).

Comments

1

In C it is common to use an array of size 1 at the end of the struct. Don't know if it is standard in Objective-C. Reallocing with

(total- 1) * sizeof(Struct1) + sizeof(Struct2)

may work.

I think.

Edit:

I mean, you can do something like:

This would be your struct:

typedef struct {
    int total;
    struct Struct1 arrayOfStructs[1];
} Struct2;
Struct2 *_myVar = malloc(sizeof(Struct2));

And this is how you add an element:

int total = ++_myVar->total;
realloc(_myVar, (total - 1) * sizeof(Struct1) + sizeof(Struct2));
_myVar->arrayOfStructs[total - 1] = {1, 5.3};

or something like that

Comments

0

Track the number of elements in the array. To add a child, re-calloc, memcpy, and set the child in the array.

typedef struct Person {
    char *name;
    short age;
    struct Person *children;
    int numberOfChildren;
} Person;


#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
    @autoreleasepool {

        NSData *data;
        {
            // setup Carol
            Person ethan = { .name="Ethan", .age=14, .children=nil, .numberOfChildren=0 };
            Person terry = { .name="Terry", .age=16, .children=nil, .numberOfChildren=0 };
            Person carol = { .name="Carol", .age=40, .children=nil, .numberOfChildren=2 };
            carol.children=(Person  *)calloc(carol.numberOfChildren, sizeof(&carol));
            carol.children[0] = ethan;
            carol.children[1] = terry;

            // people
            Person  *people = (Person  *)calloc(1+carol.numberOfChildren, sizeof(*people));
            people[0] = carol;

            size_t bytesToCopy = 1+carol.numberOfChildren * sizeof(*people);
            data = [NSData dataWithBytes:people length:bytesToCopy];
        }

        {
            int elements = 1 + [data length]/sizeof(Person);
            Person  *people = (Person  *)calloc(elements, sizeof(*people));
            size_t bytesToCopy = [data length];
            const void *source = [data bytes];
            memcpy((void*)people, source, bytesToCopy); // memxxx are an ARC no-no

            for (int i = 0; i < 1; i=i+people[i].numberOfChildren) {
                printf("\nname=%s, age=%d, children=%d", people[i].name, people[i].age, people[i].numberOfChildren);
                for (int j = 0; j < people[i].numberOfChildren; j++) {
                     printf("\nname=%s, age=%d, children=%d", people[i].children[j].name, people[i].children[j].age, people[i].children[j].numberOfChildren);
                }
            }
        }

    }
}

Prints

name=Carol, age=40, children=2
name=Ethan, age=14, children=0
name=Terry, age=16, children=0

Consider avoiding all this and switching to objects.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.