07-27-10 - 2d arrays

Some little things I often forget and have to search for.

Say you need to change a stack two dimensional array :

int array[7][3];

into a dynamically allocated one, and you don't want to change any code. Do you know how? It's :

int (*array) [3] = (int (*) [3]) malloc(sizeof(int)*3*7);

It's a little bit cleaner if you use a typedef :

typedef int array_t [3];

int array[7][3];

array_t array[7];

array_t * array = (array_t *) malloc(7*sizeof(array_t));

those are all equivalent (*).

You can take a two dimensional array as function arg in a few reasonable ways :

void func(int array[7][3]) { }

void func(int (*array)[3]) { }

void func(int array[][3]) { }

function arg arrays are always passed by address.

2-d arrays are indexed [row][col] , which means the first index takes big steps and the second index takes small steps in memory.

(* = if your compiler is some fucking rules nazi, they are microscopically not quite identical, because array[rows][cols] doesn't actually have to be rows*cols ints all in a row (though I'm not sure how this would ever actually not be the case in practice)).


won3d said...

I thought that the C spec says that array elements can't be padded beyond struct padding. I'm guessing that this applies recursively, so arrays of arrays cannot contain padding.

cbloom said...

"I thought that the C spec says that array elements can't be padded beyond struct padding. I'm guessing that this applies recursively, so arrays of arrays cannot contain padding."

Yeah I dunno, maybe padding was a bad example.

What I have gathered from some random web searching is that the sub-arrays of a 2d array are not gauranteed to be adjacent in memory. Maybe they are allowed to be in different segments or god knows what.

In particular for

int x[3][7]

you would expect that

&x[1][0] - &x[0][6] == 1

but apparently that is not gauranteed, and furthermore


is undefined.

but in all real worlds it's fine.

cbloom said...

But worrying about the standard is pointless and annoying and it angers me that GCC makes me do it so often these days.

won3d said...

It isn't pointless, and clearly you are doing it anyway because you are working on a cross-platform library. It is weird to me that you rail against this, since your job would be simpler if the de jure and de facto standards were aligned. Granted, with all the lock-free stuff you're in implementation-defined territory, anyway.

And now for a tangent.

Some of this shows through in your complaints about volatile, which MSVC interprets as something like volatile in Java, rather than the original C use (signal handling, memory-mapped hardware registers). No doubt, the Java-ish semantics are much more applicable for dealing with concurrency, but hopefully you're not deferring lots of pain for when you need to start supporting C++0x and atomic<> or whatever.

Granted, C++0x might be ABI-breaking, so you'll have a fun time supporting that when it happens. Hey, maybe if you time it right it will be someone else's problem and you can take a baby step towards being one of the selfish assholes of the world you claim to despise but secretly envy. ;)

cbloom said...

" Granted, with all the lock-free stuff you're in implementation-defined territory, anyway."

Actually all I have to do is work on signed integers and I'm in undefined behavior.

Or do anything with floats and I'm in non-standard-defined behavior.

Or worry about calling conventions, or alignment, or struct packing.

Or alias types. Or take the address of a pointer and treat it as void **.

Obviously having a standard is good. But it's annoying because on the one hand, it isn't nearly comprehensive enough to actually give me well defined cross platform behavior, and yet it is pedantic about shit that I know will never be a problem on any current platform.

The most annoying things about GCC are not about standard compliance that in any way affects portability - they're just syntactic things that are actually completely pointless because it knows exactly what you're trying to write. One is the failure to inherit some typedefs from parent classes unless you "use" them; another is the need to stick "typename" in random odd places.

It would be so much better if the C standard actually *did* give me portability, which would require exposing some information about the platform.

It would be so awesome if C actually gave me standardized #ifdef things to check like

#if __littleendian__


#if __has_sign_extend_rightshift__

then I wouldn't have to make masses of my own #defines for that shit.

Hell, the C standard should specify a standard syntax for things like "force inline" and calling conventions and so on - then leave it optional whether the compiler does it, but if it does then give me a standard syntax.

The result right now is that I have fucking

#if __PS2__


#if __GCC__

all over my code, and then I am also fighting pedantic standards-compliance junk all the time which doesn't actually help my portability at all.

It would also be nice if I could put in a flag somewhere that says "yes I know I am assuming that pointers are ints and I won't work on segmented architectures or anything weird like that so please don't give me any errors about that"

cbloom said...

Better than

#if __has_sign_extend_rightshift__

would be




and then I can do things that assume those to be the way I know they are, and then if I ever try to compile on a platform that can't do it, it gives me a compile time warning.

This is how good code is written, it is for example how the STL is designed.

You use certain attributes of the architecture, and you clearly document what you need from the architecture so that you get a compile-time error if you try to use it without those requirements.

cbloom said...

I also mean "worrying about the standard is pointless" because there is no compiler that actually supports it right. Even pedantic GCC has bugs in its standard compliance, and god help me when I'm on MSVC or whatever other weird thing (and I often have to use GCC from many years ago on other platforms). (especially since I do templates)

So I wind up having to compile & run on every platform anyway, even if I knew the standard perfectly in my head I would have to remember each case where there's a difference from it.

But I'm just venting because I'm frustrated. You can ignore me now.

old rants