Consideration on pointer declarations

S

Starman

This is something I've pondered on for some years. The pointer symbol
* is tied to the variable, not to the type. So in all the code I've
seen so far I found:

char *pc;

however I would (and I tend to) write:

char* pc;

I know the second is not syntactically correct because
char *pc1, *pc2; // works
char* pc1, pc2; // does not work.

But I find the second syntax much more natural and easier to read
through.

This syntax has always given me problems in particular when
deciphering other people's code, especially when it comes to pointers
to pointers like often found in function parameters.
Since the * symbol is also used for dereferencing a pointer, I often
get confused and I have to stop and think the code through (and lose
quite a bit of time).

I was wondering if others think it the same way.
 
S

Shiyou Wang

Starman said:
This is something I've pondered on for some years. The pointer symbol
* is tied to the variable, not to the type. So in all the code I've
seen so far I found:

char *pc;

however I would (and I tend to) write:

char* pc;

I know the second is not syntactically correct because
char *pc1, *pc2; // works
char* pc1, pc2; // does not work.

But I find the second syntax much more natural and easier to read
through.

This syntax has always given me problems in particular when
deciphering other people's code, especially when it comes to pointers
to pointers like often found in function parameters.
Since the * symbol is also used for dereferencing a pointer, I often
get confused and I have to stop and think the code through (and lose
quite a bit of time).

I was wondering if others think it the same way.

I had the same feeling at the begining, but now I become used to this
convention. And I agree it would be more natural to tie the * with type.
 
B

Ben Bacarisse

Starman said:
This is something I've pondered on for some years. The pointer symbol
* is tied to the variable, not to the type. So in all the code I've
seen so far I found:

char *pc;

I would say this the more common form (some people consider a third:
'char * pc;' by lets forget about that, yes?).
however I would (and I tend to) write:

char* pc;

I know the second is not syntactically correct because

It is perfectly correct syntax. What you probably mean is that the
layout does not mirror the syntax, where the * is part of what is
called a declarator (the *pc bit).
char *pc1, *pc2; // works
char* pc1, pc2; // does not work.

Well, it works, it just does not do what was probably intended. Both
forms can be confusing for some people so you will see advice
suggesting that one declare only one variable per declaration. The
trouble with this is that you will have to read and understand code
that does not do this. You'll have to get comfortable with it.
But I find the second syntax much more natural and easier to read
through.

This syntax has always given me problems in particular when
deciphering other people's code, especially when it comes to pointers
to pointers like often found in function parameters.
Since the * symbol is also used for dereferencing a pointer, I often
get confused and I have to stop and think the code through (and lose
quite a bit of time).

I was wondering if others think it the same way.

I don't know. I feel as if it made sense from the start, but it was
too long ago for me to be really sure.
 
E

Eric Sosman

Starman said:
This is something I've pondered on for some years. The pointer symbol
* is tied to the variable, not to the type. So in all the code I've
seen so far I found:

char *pc;

however I would (and I tend to) write:

char* pc;

I know the second is not syntactically correct because
char *pc1, *pc2; // works
char* pc1, pc2; // does not work.

But I find the second syntax much more natural and easier to read
through.

This syntax has always given me problems in particular when
deciphering other people's code, especially when it comes to pointers
to pointers like often found in function parameters.
Since the * symbol is also used for dereferencing a pointer, I often
get confused and I have to stop and think the code through (and lose
quite a bit of time).

Maybe it will make more sense if you consider pointer
variable declaration not as an isolated phenomenon, but as
part of the larger scheme of the way C declares things. For
example, how would you suggest writing

double (*fptr)(double, double);

.... in a "pointerness belongs with the type, not with the
name" style?
 
J

John Bode

This is something I've pondered on for some years. The pointer symbol *
is tied to the variable, not to the type. So in all the code I've seen
so far I found:

char *pc;

however I would (and I tend to) write:

char* pc;

I know the second is not syntactically correct because char *pc1, *pc2;
// works
char* pc1, pc2; // does not work.

But I find the second syntax much more natural and easier to read
through.

This syntax has always given me problems in particular when deciphering
other people's code, especially when it comes to pointers to pointers
like often found in function parameters. Since the * symbol is also used
for dereferencing a pointer, I often get confused and I have to stop and
think the code through (and lose quite a bit of time).

I was wondering if others think it the same way.

Once I really understood what "declaration mimics use" meant with respect
to C declarators, the pointer declaration syntax made much more sense.

If you have a pointer to a char, you access the char value by using the
dereference operator, like so:

c = *pc;

Thus, the type of the *expression* "*pc" is char, so the declaration is
typically written as

char *pc;

Even though the type of pc is "pointer to char", the "pointerness" is
tied to the declarator, not the type specifier.

Similarly, if you have an array of pointers to int, you would access a
specific int value by subscripting the array and dereferencing the
specific entry:

i = *api[k];

Thus, the type of the expression "*api[k]" is int, so the declaration is
written as

int *api[N];

Again, while api has type "N-element array of pointer to int", the
"pointerness" and "arrayness" are tied to the declarator, not the type
specifier.

Think of it this way: you have no problem associating array subscripts
with the declarator, do you (such as "char foo[N];")? But the same issue
applies; the type of foo is "N-element array of char", but we don't
declare it as

char[N] foo;

because again, declaration mimics use.
 
B

Bartc

Eric Sosman said:
Starman wrote:


Maybe it will make more sense if you consider pointer
variable declaration not as an isolated phenomenon, but as
part of the larger scheme of the way C declares things. For
example, how would you suggest writing

double (*fptr)(double, double);

... in a "pointerness belongs with the type, not with the
name" style?

In a different language that might look like:

ref function(double,double)double [pointer to function taking
(double,double) returning double]

And you can declare six of the things with:

ref function(double,double)double a,b,c,d,e,f

C's unique type declaration syntax doesn't really help in separating things
out (the type, and the name being declared, are all mixed together).

(BTW it's taken me nearly half an hour to create this post, mostly scouring
the web for an executable version of cdecl that works on my machine, or that
didn't say 'syntax error' to nearly everything thrown at it. In the end I
had to fire up another machine with a known working cdecl, just to
understand your example. There must be something wrong if an utility is
needed just to untangle type declarations! And it's not just me otherwise
cdecl wouldn't exist.)
 
S

Starman

Well, it works, it just does not do what was probably intended.

yes of course this is what I meant, it does not work in the sense
that I would have expected to declare 2 pointers when instead I
declare
1 pointer-to-char and 1 char.
 
B

Bartc

Starman said:
yes of course this is what I meant, it does not work in the sense
that I would have expected to declare 2 pointers when instead I
declare
1 pointer-to-char and 1 char.

White space has no significance in C so that char* s is the same as char *s.
But char* does look like the * is part of the char type. And is even more
confusing because char* can sometimes represent a complete type, as in
(char*)p.

Using typedefs however can help out:

typedef char *string;

string pc1,pc2; /* Both char * */
 
L

luser-ex-troll

    Maybe it will make more sense if you consider pointer
variable declaration not as an isolated phenomenon, but as
part of the larger scheme of the way C declares things.  For
example, how would you suggest writing
double (*fptr)(double, double);
... in a "pointerness belongs with the type, not with the
name" style?

In a different language that might look like:

ref function(double,double)double  [pointer to function taking
(double,double) returning double]

And you can declare six of the things with:

ref function(double,double)double a,b,c,d,e,f

C's unique type declaration syntax doesn't really help in separating things
out (the type, and the name being declared, are all mixed together).

(BTW it's taken me nearly half an hour to create this post, mostly scouring
the web for an executable version of cdecl that works on my machine, or that
didn't say 'syntax error' to nearly everything thrown at it. In the end I
had to fire up another machine with a known working cdecl, just to
understand your example. There must be something wrong if an utility is
needed just to untangle type declarations! And it's not just me otherwise
cdecl wouldn't exist.)

You should (re-)read /Deep C Secrets/, aka the Coelocanth
(sp?) book. It's got a whole chapter on this and a
detailed explanation of the cdecl program.

It also has great stuff on pointer/array (un)equivalence,
and a story about a NASA failure due to (X=Y) instead of
(X=normalize(Y)). Overcompensating Feedback Loop Bug.

lxt
 
B

Ben Bacarisse

No, let's not. My preferred style is to line things up
vertically, one declaration per line, each line commented with a
description of the variable. (Quite often I'm not that fussy in
practice.) Here is an example (horizontal space trimmed for
posting).

DELIVERY {
AGENT_R agent_r; /* Agent receiving delivery */
PORT_NO port; /* Port receiving delivery */
size_t seqno; /* Expected inport serial number */
INPORT * inport; /* Pointer to agent inport struct */
};

This may seem anal rententive, but I have found that it is very
good practice, both for writing code and for reading it later.
Thus, your coding experience will be happier if every variable
has a clear, well defined meaning. Putting one declaration per
statement has the happy side effect of never missing one of those
asterisks.

Yes, I often do this as well but I am not fond of the lone *. I still
glue it to the name when lining things up. Maybe this is because I
seem to have a lot of function pointers in structures and it seems to
look clearer with all those brackets (I think I'd use typedefs now if
I re-wrote it).

In the end you probably have to know what all the syntax means because
you are likely to come across a lot of the possible variations. So,
yes, you are right. Let's not forget about any of them!
 
K

Keith Thompson

Bartc said:
White space has no significance in C so that char* s is the same as
char *s. But char* does look like the * is part of the char type. And
is even more confusing because char* can sometimes represent a
complete type, as in (char*)p.

Using typedefs however can help out:

typedef char *string;

string pc1,pc2; /* Both char * */

Hiding a type's "pointerness" behind a typedef is generally a bad
idea, unless it's intended to be opaque (i.e., code that uses the type
doesn't need to know that it's a pointer). Furthermore, a string is
not a char*; a string is a contiguous sequence of characters. A char*
can point to a string; it can't be a string.
 
B

Bartc

Eric Sosman said:
Bartc said:
Eric Sosman said:
Maybe it will make more sense if you consider pointer
variable declaration not as an isolated phenomenon, but as
part of the larger scheme of the way C declares things. For
example, how would you suggest writing

double (*fptr)(double, double);

... in a "pointerness belongs with the type, not with the
name" style?

In a different language that might look like:

ref function(double,double)double [pointer to function taking
(double,double) returning double]
Language bloat is also an issue; note
that your example introduces two brand-new keywords.

Two extra keywords might (or would have been) worth it, for intuitive type
declarations (my example originated from Algol68 where it looks more like
ref proc(real,real)real)
I don't think I understand your objection. What's wrong with
mixing things together?

Er, nearly everything? :)

It causes the OP's problem; it makes typespecs necessarily convoluted
instead of linear; where a typespec needs to exist without a name (as in a
cast), then it changes appearance (and is even more difficult to work out);
and playing around with macros for declaration syntax is harder simply
because types and names are not side-by-side.
For that matter, since the name-part is
"atomic" and must exist on its own somewhere, in what sense is it
"mixed?"

I mean that the name can be in the middle of the type declaration.
 
Q

qarnos

I was wondering if others think it the same way.

I guess I'll take this opportunity to expose my weird coding style
when it comes to declarations, particularly wrt pointers.

I always have my specifiers and declarators spaced out with tabs so
the declarations read like a table. I always have the '*' associated
with the type, since that seems more natural to me and easier to read.

I never have multiple declarators in a declaration, so there is no
confusion over the intention of the declaration. This also makes it
easy to comment the code (especially for doc generators such as
doxygen), with the comments also aligned in tabular form.

Here's an example of my style (usenet TAB gods willing):

void foobar(void)
{
int * int_ptr; /*!< pointer to int */
char * char_ptr; /*!< pointer to char */
struct foo * struct_ptr; /*!< pointer to foo */
}

Obviously, in the real world I make the comments a bit more
informative!

You might think this style is weird (and you're probably right), but I
find the table layout makes it much easier to lookup an identifier and
find its type/description.
 
J

JosephKK

yes of course this is what I meant, it does not work in the sense
that I would have expected to declare 2 pointers when instead I
declare
1 pointer-to-char and 1 char.

Somewhere in K&R and some other places they go to great lengths to
explain how tightly various type modifier symbols like "*", ".", "->",
and "&" bind to either the left or right but some of the logic keeps
escaping me.
 
B

Ben Bacarisse

JosephKK said:
Somewhere in K&R and some other places they go to great lengths to
explain how tightly various type modifier symbols like "*", ".", "->",
and "&" bind to either the left or right but some of the logic keeps
escaping me.

Of those only * occurs in types. An operator table (K&R has one)
explains how *, . etc bind in expressions.

In types you have *, [] and (). All you need to remember is that
(just as in expressions) the postfix ones [] and () bind more tightly
than *. Some confusion comes from the fact that ()s are then use to
alter the meaning -- i.e. there are two sorts of (). One means
"function taking ..." and the other is syntactic.

The upshot of that is that a declaration is read from the name
outwards, always going right if you can (because there is a '[' or
'(') and going left when you reach the end of the declarator (not the
declaration) or a closing syntactic ')'.
 
J

JosephKK

JosephKK said:
Somewhere in K&R and some other places they go to great lengths to
explain how tightly various type modifier symbols like "*", ".", "->",
and "&" bind to either the left or right but some of the logic keeps
escaping me.

Of those only * occurs in types. An operator table (K&R has one)
explains how *, . etc bind in expressions.

In types you have *, [] and (). All you need to remember is that
(just as in expressions) the postfix ones [] and () bind more tightly
than *. Some confusion comes from the fact that ()s are then use to
alter the meaning -- i.e. there are two sorts of (). One means
"function taking ..." and the other is syntactic.

The upshot of that is that a declaration is read from the name
outwards, always going right if you can (because there is a '[' or
'(') and going left when you reach the end of the declarator (not the
declaration) or a closing syntactic ')'.

Yes that is so. I still can get confused with typedefs of structs,
expecially complex ones with embedded typedef'd unions. Any helpful
words of wisdom there?
 
G

Guest

[...] I still can get confused with typedefs of structs,
expecially complex ones with embedded typedef'd unions.  Any helpful
words of wisdom there.

Same answer to any programming problem (or indeed any other sort
of problem) in which complexity threatens to overwhelm you. Modularise
(or divide and conquer).

Hmm. Except I re-rwad your post. You say *typedefed unions*
confuse you. I use typedefs to reduce confusion.

Initially I thought you were confused by

typedef struct
{
char name [1024];
int serial_number;
int version;
int drive_type;
union drive /* syntax of union not-checked */
{
Sublight slow;
HyperDrive fast;
};
} Starship;

and I was going to say lift the union out.

typedef union
{
Sublight slow;
HyperDrive fast;
} StarshipDrive;

typedef struct
{
char name [1024];
int serial_number;
int version;
int drive_type;
StarshipDrive drive;
} Starship;

If I've misunderstood gould you give an example of what confuses
you?
 
J

JosephKK

On Thu, 12 Mar 2009 03:17:54 -0700 (PDT),
[...] I still can get confused with typedefs of structs,
expecially complex ones with embedded typedef'd unions.  Any helpful
words of wisdom there.

Same answer to any programming problem (or indeed any other sort
of problem) in which complexity threatens to overwhelm you. Modularise
(or divide and conquer).

Hmm. Except I re-rwad your post. You say *typedefed unions*
confuse you. I use typedefs to reduce confusion.

Initially I thought you were confused by

typedef struct
{
char name [1024];
int serial_number;
int version;
int drive_type;
union drive /* syntax of union not-checked */
{
Sublight slow;
HyperDrive fast;
};
} Starship;

and I was going to say lift the union out.

typedef union
{
Sublight slow;
HyperDrive fast;
} StarshipDrive;

typedef struct
{
char name [1024];
int serial_number;
int version;
int drive_type;
StarshipDrive drive;
} Starship;

If I've misunderstood gould you give an example of what confuses
you?
Not quickly. May be quite a while. When i get one i will bring it
around.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

complex declarations 23
Array of structs function pointer 10
Function declarations 14
Lexical Analysis on C++ 1
C Declarations 1
pointer addresses 15
pointer question 19
pointer to NULL terminated array of pointer 8

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top