Monday, February 7, 2011

Do dereferencing null or uninitialized pointers in Objective C cause runtime errors?

I recently a interesting "discussion" with a Objective C developer about what happens when we dereference null or uninitialized pointers in Objective C?

First a little recap: A pointer references a location in memory, and obtaining the value at the location a pointer refers to is known as dereferencing the pointer. A null pointer is a variable that makes reference to something that is not initialized. Uninitialized pointer is pointing to some memory location but the pointer value is not assigned.

From my experience in C programming, dereferencing these types of pointers will almost certainly cause a runtime error resulting in a segmentation fault when the compiled program is executed. I'm pretty certain that this applies to Objective C too as the fundamentals are the same. However, my opponent insists that the Objective C language takes care of null and uninitialized pointers by making them point to Nil by default.

To proof that dereferencing null or uninitialized pointers in Objective C will cause runtime errors, I found a simple Objective C sample file online and modified it to compile with gcc in Mac OS X. Attached below are 3 files that are identical except for 1 line of code with the following differences:
  • hello.m: with normal initialized pointer
  • hello-null.m: with pointer pointing to NULL
  • hello-uninit.m: with uninitialized pointer

The screenshot below shows the programs compiled from hello-null.m and hello-uninit.m causing segfaults.

The test environment is a default install Mac OS X 10.6.6 Snow Leopard and the gcc compiler is from Xcode 3.2.5 and iOS SDK 4.2.

There you have to it: Dereferencing null or uninitialized pointers in Objective C will cause runtime errors resulting in segmentation faults.



// hello.m
#import <Foundation/Foundation.h>
#import <stdio.h>


@interface Number: NSObject
{
        @public
        int number;
}
- (void)printNum;
@end

@implementation Number: NSObject
- (void)printNum
{
        printf("%d\n", number);
}
@end

int main(void)
{
        Number *myNumber = [Number new]; // equal to [[Number alloc] init]

        myNumber->number = 6;

        [myNumber printNum];

        return 0;
}



// hello-null.m
#import <Foundation/Foundation.h>
#import <stdio.h>


@interface Number: NSObject
{
        @public
        int number;
}
- (void)printNum;
@end

@implementation Number: NSObject
- (void)printNum
{
        printf("%d\n", number);
}
@end

int main(void)
{
        Number *myNumber = NULL;

        myNumber->number = 6;

        [myNumber printNum];

        return 0;
}



// hello-uninit.m
#import <Foundation/Foundation.h>
#import <stdio.h>


@interface Number: NSObject
{
        @public
        int number;
}
- (void)printNum;
@end

@implementation Number: NSObject
- (void)printNum
{
        printf("%d\n", number);
}
@end

int main(void)
{
        Number *myNumber; // uninitialized

        myNumber->number = 6;

        [myNumber printNum];

        return 0;
}



--- Update ---

It occurred to me that other people might think that my examples were biased towards the segfault results as they write to null and uninitialized pointers, instead of just dereferencing the pointers. So I retested by modifying the examples to only dereference the null and uninitialized pointers, and I get the same results: segmentation faults.

Attached below is a modified version of hello-uninit.m that do not write to the dereferenced pointer. The hello-null.m file is modified accordingly.


// hello-uninit2.m
#import <Foundation/Foundation.h>
#import <stdio.h>

@interface Number: NSObject
{
        @public
        int number;
}
- (void)printNum;
@end

@implementation Number: NSObject
- (void)printNum
{
        printf("%d\n", number);
}
@end

int main(void)
{
        Number *myNumber; // uninitialized


        // myNumber->number = 6;
 
        if( myNumber->number ) {
                [myNumber printNum];
        }

        return 0;
}



--- The End ---

No comments: