PDA

View Full Version : Instance variable is pointer to C struct, crashing




MarcVanH
Sep 12, 2008, 11:55 AM
Hello all,

I have been playing with an issue for hours now and can't figure it out. Basically, I am writing a class, and trying to initialize an instance variable that has a user-defined type (C struct). If I declare it as a direct variable (without a "*"), I can assign a value to it in init. But if I make it a pointer, my app compiles fine, but freezes when I try to assign a value. Here is a sample that reproduces the error:


typedef struct Mystruct_s {
int subVar;
} Mystruct_t;

@interface MyClass : NSObject {
Mystruct_t noPointer;
Mystruct_t *yesPointer;
}
-(MyClass*)init;
@end
@implementation MyClass
-(MyClass*) init {
self = [super init];
if ( self ) {
noPointer.subVar = 123; // works just fine
yesPointer->subVar = 123; // compiles, but program chrashes here. mem not allocated?
}
return self;
}
@end


This code compiles just fine. But it crashes where the comments indicate.

Can anyone out there help me assign a value to "yesPointer->subVar" without it crashing?

(and I'd also love to know why I need to use a "." for the noPointer variable, and a "->" for the yesPointer variable :) )



lee1210
Sep 12, 2008, 12:28 PM
Hello all,

I have been playing with an issue for hours now and can't figure it out. Basically, I am writing a class, and trying to initialize an instance variable that has a user-defined type (C struct). If I declare it as a direct variable (without a "*"), I can assign a value to it in init. But if I make it a pointer, my app compiles fine, but freezes when I try to assign a value. Here is a sample that reproduces the error:


typedef struct Mystruct_s {
int subVar;
} Mystruct_t;

@interface MyClass : NSObject {
Mystruct_t noPointer;
Mystruct_t *yesPointer;
}
-(MyClass*)init;
@end
@implementation MyClass
-(MyClass*) init {
self = [super init];
if ( self ) {
noPointer.subVar = 123; // works just fine
yesPointer->subVar = 123; // compiles, but program chrashes here. mem not allocated?
}
return self;
}
@end


This code compiles just fine. But it crashes where the comments indicate.

Can anyone out there help me assign a value to "yesPointer->subVar" without it crashing?

(and I'd also love to know why I need to use a "." for the noPointer variable, and a "->" for the yesPointer variable :) )

So a pointer in C is a variable that holds a memory address, and at that memory address there is the data type declared. So a Mystruct_t * that is not set to the address of a structure instance (i.e. &nopointer) or assigned the result of a malloc the sizeof(Mystruct_t) will be pointing to someplace in memory that you don't own.

The dot operator allows access to a field in a structure. The arrow operator dereferences the structure first, then allows access to the field. It's just a shortcut. You are using them properly with the Mystruct_t and Mystruct_t *, but as mentioned above, the Mystruct_t * is just uninitialized.

-Lee

idelovski
Sep 12, 2008, 12:34 PM
You need to allocate space for that pointer.

@implementation MyClass
-(MyClass*) init {
self = [super init];
if ( self ) {
yesPointer = malloc (sizeof(Mystruct_t)); // NEW
noPointer.subVar = 123; // works just fine
yesPointer->subVar = 123; // compiles, but program chrashes here. mem not allocated?
}
return self;
}

Plus you can check if yesPointer is not NULL before you use it later. I'm new to ObjC, but I suppose pointers are pointers.

gnasher729
Sep 12, 2008, 12:41 PM
This code compiles just fine. But it crashes where the comments indicate.

Can anyone out there help me assign a value to "yesPointer->subVar" without it crashing?

(and I'd also love to know why I need to use a "." for the noPointer variable, and a "->" for the yesPointer variable :) )

Your code does exactly what I would have expected it to do.

Your "noPointer" variable is a struct that can hold data. When any NSObject is allocated, all memory within the object is filled with zeroes, so all elements of "noPointer" initially contain zeroes, and you can change them any way you like.

The "yesPointer" variable is a pointer which can hold the address of data (or pointer to the data, it's the same thing). The data itself must be somewhere else. When any NSObject is allocated, all memory within the object is filled with zeroes, and that makes all pointers in the object "null pointers". A null pointer points nowhere, and if you try to store data into the object that the null pointer points to, your application _will_ crash. It is your responsibility to find an object, or create an object, and store the address of that object into "yesPointer". For example, you could write

yesPointer = malloc (sizeof (Mystruct_t));

This allocates memory for an object with the size of a Mystruct_t, returns a pointer to that memory, and stores that pointer into yesPointer.

The "." syntax means: I have a struct on the left side; use an element of that struct. The "->" means: I have a pointer to a struct on the left side; take the struct that it points to, and use an element of that struct.

lee1210
Sep 12, 2008, 12:41 PM
You need to allocate space for that pointer.

@implementation MyClass
-(MyClass*) init {
self = [super init];
if ( self ) {
yesPointer = malloc (sizeof(Mystruct_t)); // NEW
noPointer.subVar = 123; // works just fine
yesPointer->subVar = 123; // compiles, but program chrashes here. mem not allocated?
}
return self;
}

Plus you can check if yesPointer is not NULL before you use it later. I'm new to ObjC, but I suppose pointers are pointers.

This won't crash anymore, but unless you free elsewhere this will leak. i would think dealloc would be a good choice of where to do your free. I don't think there's an advantage to using alloc and free here instead of using an instance variable. The object is allocated on the stack anyway, so the memory is all coming from the same place.

-Lee

whooleytoo
Sep 12, 2008, 12:50 PM
So a pointer in C is a variable that holds a memory address, and at that memory address there is the data type declared. So a Mystruct_t * that is not set to the address of a structure instance (i.e. &nopointer) or assigned the result of a malloc the sizeof(Mystruct_t) will be pointing to someplace in memory that you don't own.

The dot operator allows access to a field in a structure. The arrow operator dereferences the structure first, then allows access to the field. It's just a shortcut. You are using them properly with the Mystruct_t and Mystruct_t *, but as mentioned above, the Mystruct_t * is just uninitialized.

Precisely -

yesPointer->subVar is just a shortcut for (*yesPointer).subVar.

Also init methods generally return an id, not a defined type; I don't know if that will cause any issues, but I think it's good practice to adopt that convention.

whooleytoo
Sep 12, 2008, 12:53 PM
This won't crash anymore, but unless you free elsewhere this will leak. i would think dealloc would be a good choice of where to do your free. I don't think there's an advantage to using alloc and free here instead of using an instance variable. The object is allocated on the stack anyway, so the memory is all coming from the same place.

-Lee

Nope, if you use malloc, the memory is allocated on the heap, not the stack.

lee1210
Sep 12, 2008, 12:58 PM
Nope, if you use malloc, the memory is allocated on the heap, not the stack.

damnit, that's what i meant. Objects and malloc'd things both end up on the heap, so there's no advantage to malloc'ing.

-Lee

MarcVanH
Sep 12, 2008, 12:59 PM
Wow, what a great forum!

Thanks everyone. I swear I fiddled with malloc but couldn't get it to work.

Also, to confirm, I do not need to free the allocated memory?

kainjow
Sep 12, 2008, 01:02 PM
No you still need to free it.

Override dealloc in your class and do it there:
- (void)dealloc {
free(yesPointer);
[super dealloc];
}

whooleytoo
Sep 12, 2008, 01:03 PM
Wow, what a great forum!

Thanks everyone. I swear I fiddled with malloc but couldn't get it to work.

Also, to confirm, I do not need to free the allocated memory?

No, I can confirm you do! :)

The alternatative, just use a struct, rather than a struct pointer as an instance variable; and then you don't need to allocate or free it.

lee1210
Sep 12, 2008, 01:06 PM
No, I can confirm you do! :)

The alternatative, just use a struct, rather than a struct pointer as an instance variable; and then you don't need to allocate or free it.

Seconded. That was the point i ended up bumbling through above... just use an instance variable, and you don't have to free it. When your object is destroyed, the memory for that struct goes with it. Otherwise you have to deal with the free as kainjow demonstrated. It's not so bad, but there's no advantage to the approach. Note that if you pass the pointer around to this struct (whether it is an instance variable or pointer), bad things can happen once the free happens or the object is destroyed. Just be cautious about this.

-Lee