Fun with C99 Syntax
The C99 language added some pretty neat features to the ANSI C we know and love (now known as C89). I used a construct called compound literals in my iPad Dev Camp presentation, and it seemed new to a lot of people. Here’s a summary of some lesser know features about C99 that are worth knowing. And, since Objective-C is a strict superset of C, all this applies to Objective-C, as well. Best of all, as of recent Xcode (3.0? 3.1?), C99 is the default C dialect for new projects, so you don’t need to do anything to start taking advantage of these.
Structure Initialization
Structure initialization received a lot of love in C99. In C89, you could initialize a structure variable like this:
NSPoint point = {0, 0};
The caveat here is that the structure elements must be provided in the order that they are listed in the definition. In this case, NSPoint
is declared as like:
typedef struct _NSPoint {
CGFloat x;
CGFloat y;
} NSPoint;
So, when we initialize a variable, x
comes first, followed by y
. This can be a drawback because if the order in the definition ever changed, all existing code that depended on the order would break. As of C99, you can initialize a structure by specifying the structure element names:
NSPoint point = { .x = 0, .y = 0};
This not only decouples the order of the definition from the order of the initialization, but it’s more readable. As an added benefit, any structure elements not initialized are set to zero. This means you only need to fill out the portions of the structure that are relevant. And if new elements to the structure are added in later versions, they get initialized to a known value.
The benefit of this becomes even more clear for nested structures like NSRect
:
typedef struct _NSRect {
NSPoint origin;
NSSize size;
} NSRect;
To initialize an NSRect
in straight C89 looked something like this:
NSRect rect = {{0, 0}, {640, 480}};
Because this is kind of awkward, there’s a function to make this a bit easier:
NSRect rect = NSMakeRect(0, 0, 640, 480);
But, honestly, that’s not much of an improvement in readability. NSMakeRect
is more useful in structure assignment, but we’ll see an alternate way to do this below. With C99, we can initialize this variable like:
NSRect rect = {
.origin.x = 0,
.origin.y = 0,
.size.width = 640,
.size.height = 480,
};
Again, we can order the structure elements however we please; we’re not required to list them in the order of the definition. We have some flexibility in how we assign the nested structures, too. This is also legal:
NSRect rect = {
.origin = {.x = 0, .y = 0},
.size = {.width = 640, .height = 480},
};
As as this:
NSRect rect = {
.origin = NSMakePoint(0, 0),
.size = NSMakeSize(640, 480),
};
And even this:
NSRect rect = {
.origin = otherRect.origin,
.size = NSMakeSize(640, 480),
};
So, as you can see, we get a lot more flexibility on how to initialize structures.
Compound Literals for Structure Assignment
This new syntax of initializing structure variables is great, but it doesn’t help us much for assigning to existing structure variables. The example I used in my iPad Dev Camp talk was setting up a AudioStreamBasicDescription
structure, colloquially known as ASBD
. This structure is used to describe an audio format for Core Audio. In this case, I had an ASBD
instance variable. Because it’s an instance variable, you cannot set its value using the initialization syntax above. Thus, the traditional way to initialize one of these it to clear out it to zero with memset
, and then set the fields you need, one by one:
memset(&_dataFormat, 0, sizeof(_dataFormat));
_dataFormat.mFormatID = kAudioFormatLinearPCM;
_dataFormat.mSampleRate = SAMPLE_RATE;
_dataFormat.mBitsPerChannel = 16;
// And on and on...
Using new C99 syntax known as a compound literal, you can set an existing structure variable like this:
_dataFormat = (AudioStreamBasicDescription) {
.mFormatID = kAudioFormatLinearPCM,
.mSampleRate = SAMPLE_RATE,
.mBitsPerChannel = 16,
// And on and on...
};
It looks similar to a cast plus an initialization. And under the hood, the compiler is making a anonymous variable. So you can think of the above as equivalent to:
AudioStreamBasicDescription anon = {
.mFormatID = kAudioFormatLinearPCM,
.mSampleRate = SAMPLE_RATE,
.mBitsPerChannel = 16,
// And on and on...
};
_dataFormat = anon;
Again, the nice thing here as that unset fields are initialized to zero, so you get the equivalent of the memset
. Plus, you don’t have to repeat the variable name over and over again.
Oh, and as a quick shorthand for setting the entire structure to zero, you can do something like this:
_dataFormat = (AudioStreamBasicDescription) {0};
Compound Literals with Primitives
Compound literals can be applied to primitive types, too. Most of the time this is not much use:
int i = (int) {3};
But because these are anonymous variables, you can take the address of them:
int * iPointer = &(int) {3};
This can be useful for some Core Audio APIs, such as AudioUnitSetProperty
. Typically you create a variable for the sole purpose of taking the address of it:
UInt32 maxFramesPerSlice = 4096;
AudioUnitSetProperty(converterAudioUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
0,
&maxFramesPerSlice,
sizeof(UInt32));
Using compound literals, we can do this inline without an extra variable:
AudioUnitSetProperty(converterAudioUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
0,
&(UInt32) {4096},
sizeof(UInt32));
Conclusion
While C99 was a relatively minor update to C89, there are quite a few gems buried away to make our code more flexible and readable, as you can see.