Re: would C be easier to read if...
- From: John Bode <john_bode@xxxxxxxxxxx>
- Date: Fri, 4 Apr 2008 08:31:49 -0700 (PDT)
On Apr 3, 1:23 pm, "Bartc" <b...@xxxxxxxxxx> wrote:
"Robert Smith" <some...@xxxxxxxxxxxxx> wrote in message
news:rd5Jj.5608$n8.2434@xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
some of the syntax wasn't overloaded so much...
Was just musing that if pointer de-referencing and pointer-to-type had
seperate syntax (ie use a character other than '*' for one of them) it
would make things much easier to read. You wouldn't get stuff like:
pthread_create(&thread, NULL, (void *(*)(void*))ThreadProc, (void
*)parameter);
I've got more serious problems with understanding C declarations, for
example:
What on earth does (void *(*)(void*)) mean? It's some sort of cast, so the
type is:
void *(*)(void*)
My guess is it's a function returning type void*, and maybe taking a single
parameter of void*, but what about the (*) in the middle?!
That indicates that it's a pointer to a function.
A cast expression is basically a declaration without the identifier;
for example, when you see
(void *(*)(void*))ThreadProc
substitute
void *(*ThreadProc)(void*);
which reads as
ThreadProc -- ThreadProc
*ThreadProc -- is a pointer
(*ThreadProc)() -- to a function
(*ThreadProc)(void *) -- taking a void* parameter
*(*ThreadProc)(void *) -- and returning a pointer
void *(*ThreadProc)(void *) -- to void
And * does apparently seem to change position. Unless I've got these wrong:
int *a a is pointer to int (* on left)
(int *) pointer to int (* on right)
*a dereference pointer to int (* on left again).
In my case a type declaration that reads linearly from left to right would
help tremendously, because that's what I'm familiar with. Just having the
word 'function' in a function declaration would make things so much clearer!
Maybe C's syntax will get easier with use, I don't know, but since I stay
well clear of anything complicated, probably not.
C's declaration syntax follows a "declaration mimics use" paradigm;
IOW, an object or function's declaration should look as much like how
it's referenced in the executable code as possible. This is probably
best explained with some examples.
Let's say we have a regular int identifier named "foo". Here's how it
would be referenced in the code:
foo = blah();
bar = foo;
printf("%d\n", foo);
So, the declaration of "foo" is pretty simple:
int foo;
Now let's pretend "foo" is a pointer to int. Using the same examples
as above:
*foo = blah();
bar = *foo;
printf("%d\n", *foo);
In each case above, we use the dereference operator "*" to get to the
integer value pointed to by foo. So, our declaration of foo is:
int *foo;
Now let's pretend "foo" is an array of pointers to int:
*foo[i] = blah();
bar = *foo[i];
printf("%d\n", *foo[i]);
And the declaration:
int *foo[N];
In the declarations above, "foo", "*foo", and "*foo[N]" are all called
declarators; the declarator introduces the name of the thing being
declared, and provides additional type information. In the
declaration
int *foo[N];
the int-ness of foo is provided by the type specifier "int", while the
pointer-ness and array-ness are provided by the "*" and "[]" in the
declarator. Note that the "*" and "[]" operators are bound to the
identifier, not the type specifier. Cast expressions seem to violate
this rule, but they really don't; again, think of the cast as a
declaration without an identifier, and mentally substitute the thing
being cast for where the identifier would go.
A few more examples:
int (*foo)[N]; // foo is a pointer to an array of int
...
(*foo)[i] = blah();
bar = (*foo)[i];
printf("%d\n", (*foo)[i]);
int (*foo)(void); // foo is a pointer to a function returning int
...
blah = (*foo)(); // can also be written simply as foo();
printf("%d\n", (*foo)());
int (*foo[N])(void); // foo is an N-element array of pointers to
// functions returning int
blah = (*foo[i])();
printf("%d\n", (*foo[i])());
When you see a particularly hairy declaration, like
char *(*(*(*foo)[N])(double bar))[M];
start by finding the leftmost identifier, and then work your way out,
remembering that [] and () bind before * (i.e., *x[] is an array of
pointer, (*x)[] is a pointer to an array, *x() is a function returning
a pointer, and (*x)() is a pointer to a function):
foo -- foo
*foo -- is a pointer
(*foo)[N] -- to an N-element array
*(*foo)[N] -- of pointers
(*(*foo)[N])() -- to functions
(*(*foo)[N])(double bar) -- taking a double
parameter
*(*(*foo)[N])(double bar) -- returning a pointer
(*(*(*foo)[N])(double bar))[M]; -- to an M-element array
*(*(*(*foo)[N])(double bar))[M]; -- of pointers
char *(*(*(*foo)[N])(double bar))[M]; -- to char
Alternately, you could go the other way, and work from the outside
in. Start by substituting everything between the outermost () with X:
char *X[M];
X is an M-element array of pointer to char.
Now we can start decomposing X:
X = (*Y)
char *(*Y)[M];
Y is a pointer to an M-element array of pointer to char
Y = Z(double bar)
char *(*Z(double bar))[M];
Z is a function returning a pointer to an M-element array of pointer
to char
Z = (*G)
char *(*(*G)(double bar))[M]
G is a pointer to a function returning a pointer to an M-element array
of pointer to char
G = H[N]
char *(*(*H[N])(double bar))[M]
H is an array of pointers to functions returning a pointer to an M-
element array of pointer to char
H = (*foo)
char *(*(*(*(*foo)[N])(double bar))[M]
foo is a pointer to an N-element array of pointers to functions
returning a pointer to an M-element array of pointer to char.
.
- References:
- would C be easier to read if...
- From: Robert Smith
- Re: would C be easier to read if...
- From: Bartc
- would C be easier to read if...
- Prev by Date: Re: C elements of style
- Next by Date: Re: Not a number problem
- Previous by thread: Re: would C be easier to read if...
- Next by thread: Re: would C be easier to read if...
- Index(es):
Relevant Pages
|