I do not work with C directly much and a basic language nuance just set me back and I would like to understand it better so I can avoid it in the future.
I am using the Queue implementation found at the following link.
http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=9202&lngWId=3
It's a pretty common queue where values are pushed and popped. (FIFO) But for some reason the last value that I push onto the queue sets all values in the queue with the same value.
I started with the following code.
Queue q;
queue_init(&q);
for (int i=0;i<10;i++) {
MyData *in_md;
in_md->mNumber = i;
queue_push(&q, in_md);
MyData *h = (MyData *)q.head->data;
MyData *t = (MyData *)q.tail->data;
NSLog(@"in_md: %i", in_md->mNumber);
NSLog(@"h->mNumber: %i", h->mNumber);
NSLog(@"t->mNumber: %i", t->mNumber);
if (q.head->link) {
MyData *l = (MyData *)q.head->link->data;
NSLog(@"l->mNumber: %i", l->mNumber);
}
}
But I found that when I popped the values off the number value was 9 for each instance. I thought I had some pointer bug and tried to fix it for a while. I thought that since I declared in_md inside of the block 开发者_运维问答of the for loop it would be a unique instance with a unique memory address. Eventually updated the code to the following...
Queue q;
queue_init(&q);
for (int i=0;i<10;i++) {
void *data = malloc(sizeof(MyData)); // key change
MyData *in_md = (MyData *)data;
in_md->mNumber = i;
queue_push(&q, data);
MyData *h = (MyData *)q.head->data;
MyData *t = (MyData *)q.tail->data;
NSLog(@"in_md: %i", in_md->mNumber);
NSLog(@"h->mNumber: %i", h->mNumber);
NSLog(@"t->mNumber: %i", t->mNumber);
if (q.head->link) {
MyData *l = (MyData *)q.head->link->data;
NSLog(@"l->mNumber: %i", l->mNumber);
}
}
Now the ensures that a new instance is created because malloc is used and I cast it to a variable I can set the int value. This creates the output appears as I expect.
I assume this is how C is supposed to work but I did not expect it. Normally in other languages like Java, C# and even JavaScript declaring a variable within a block would create a new instance of the variable which is what I expected here. But that is not the case.
What is happening in this case?
In your code:
You created a pointer:
MyData *in_md;
and without reserving memory for that pointer (that's what the malloc does in your second code block) you assing values to it's members:
in_md->mNumber = i;
You were basically accessing undefined memory. In your case the value was '9' but you could have gotten an application crash as well.
In your second code block you allocated memory for that pointer and then stored it in the queue - that's the right way.
Keep in mind that you need to deallocate the memory using the free() method before you finally release the queue.
Declaring a pointer does create the pointer, but not the pointee. You still need to initialize the pointer (just like you would need to initialize any other variable), but in the case of a pointer, it needs to be initialized to a memory address, which can be done via the use of new (C++) or malloc (C) to allocate memory (it is also possible to assign the memory address of an existing variable via &).
It is generally good programming practice to combine declaration with initialization. Ensuring that you never use:
Type identifier;
And always use:
Type identifier = initializing_expression;
Will ensure that you don't run into such issues and will save you quite a bit of pain. Note that this is not all that different from other languages (in Java, the language forbids the use of uninitialized variables, altogether, while in JavaScript declaring without initializing will result in it having a value of undefined).
Your second implementation is indeed the way to go.
The part that I think you are getting hung up on is that doing "MyData *in_md;" will indeed make a "new instance" in a sense, but it won't make a new instance of MyData, it makes a new instance of a MyData pointer. A pointer doesn't do you very much good though until it actually points at something useful, hence the malloc call.
So where in Java you would have MyData in_md = new MyData()
it will allocate space for MyData automatically, but in C you have MyData *in_md = (MyData*)malloc(sizeof(MyData));
bueause you have much lower-level control of memory. After that, you can work with the object just like you would in Java or C#. The only other thing to remember is that in Java, if memory allocation were to fail, it would probably throw you a nice error, but that is not the case in C, so it may be a good idea to make sure that in_md is not NULL, depnding on the application. Finally, C lacks garbage collection, so it is important to make sure you free() the item that you malloc'ed once you dequeue it.
Queue q;
queue_init(&q);
for (int i=0;i<10;i++) {
MyData *in_md = (MyData *)malloc(sizeof(MyData));
in_md->mNumber = i;
queue_push(&q, (void*)in_md);
MyData *h = (MyData *)q.head->data;
MyData *t = (MyData *)q.tail->data;
NSLog(@"in_md: %i", in_md->mNumber);
NSLog(@"h->mNumber: %i", h->mNumber);
NSLog(@"t->mNumber: %i", t->mNumber);
if (q.head->link) {
MyData *l = (MyData *)q.head->link->data;
NSLog(@"l->mNumber: %i", l->mNumber);
}
}
精彩评论