C/C++ Pointer Declaration Syntax – It makes sense
hamberg.no
hamberg.no
Once you add references into the mix and it gets really confusing. I'm not really sure why they reused the ampersand to represent a reference type. Were there no other symbols left? It doesn't even follow the same rule from C. "int a" means that a is of type int. "int &a" does not mean that &a is of type int. Blah.
The cruft is ever building, but it started out very intuitive. Oh well.
In C# code I constantly see the "anti-pattern" of new-ing up a variable at declaration, only to immediately overwrite it on the next line with the results of some function. I can't help but think that the person writing it has no concept of what "new" is actually doing (with regards to the heap and the GC, for example).
If you want to learn pointers, you have to learn a language that provides them. So C, or like I said, Forth or Pascal. C is fine as a first language, if you don't mind the pain. C++ is a horrible language, no matter when you learn it. Never cripple the minds of beginners with it.
About your C# anecdote, does the language allow something like the following?:
type variables = someFunction ();
Nowadays I usually program in Haskell for my day job. There lots and lots of nice concepts to be understood there. You could even use pointers, if you are desperate enough. does the language allow something like the following?:
yes, exactly. The problem is you see stuff like this: var obj = new SomeType();
obj = GetSomeType();
Basically creating 2 objects, the first of which is immediately garbage since it no longer has any roots. Anyone who understands pointers/references and memory allocation should understand that "new" is actually creating a new live object on the heap, rather than just initializing a pointer to null (or a stack-based value type to zero).These days C# is actually a decent language to code in, it has a nice functional aspect to it with linq/lambdas, and you can still go down to C style pointers/"unsafe" code if necessary (with lots of restrictions of course). It's really the Microsoft dependency that kills it more than anything else.
var obj = new SomeType();
obj = GetSomeType();
You could almost be tempted to write an automatic converter to var obj = GetSomeType();
but you can't in general, because you don't know whether `new SomeType()' doesn't have side-effects. But if it has some that are relied on, that's probably a bug.I know that there's a free .net runtime called Mono. What about the compiler side of free C#?
What about "references"? int& a, b; or int &a, &b; ?
So, I think that I'll stick to int* pa; int* pb;
i.e. one variable per line.
Additionnaly it makes it easier too lookup for a variable's type when reading the source code because one does not need to walk thru a list to find the variable, it is always after the type. I actually use "auto" to make it even easier to find variable declarations, going as far as grouping such declarations to some extend to make them easier to find.
Styles vary.
It does make sense that the dereferenced pf is a float, but that doesn't stop it from being confusing. Being a pointer is, to me, part of the type. A pointer is a type after all, so having the * bind to the type would make a lot more sense to me.
Additionnaly it makes it easier too lookup for a variable's type when reading
It would also be easier to grep for types too (not that I've ever found myself doing that, but I'm sure people occasionally do)
The "right-left" rule is a completely regular rule for deciphering C declarations.
It can also be useful in creating them.
please help: http://blog.gitorious.org/2011/12/08/private-repositories/
type* ident;
The reasoning in K&R (also presented in the article) has always bothered me. When the compiler builds a symbol table, it has "ident" as the identifier, and "type*" as its type. When the compiler emits type related error messages, it uses that identifier and type to communicate with you.Imagine they'd have used this instead, for example:
int. a; // declares a pointer to an int named a
*a = 42; // dereference the pointer
Yea, looks weird on first sight (because we're used to current style), but the difference is glaringly obvious: it's visually apparent that the two do completely different things. It doesn't need to be the dot, by the way. My point is that any symbol that's different would have been better than using the same one.?
* == "at location"
& == "the address of"
There are cases where this doesn't work well (or at all), but it works for many of the cases that initially look like jibberish to a C noob.
For declarations, just put variables on separate lines.
void (*func)(int, int) = f1;
where f1 is void f1(int a, int b);
Another aspect that might be confusing, but which becomes apparent once you see it through this light, is typedefs.At the beginning it might seem that typedefs alias some A to some B.
typedef int myint; // OK
typedef myint int; // WRONG
Which could lead to confusing which side is the one you are defining and which is the produced.But typedefs basically work as variable decl but they declare a type, so:
typedef void (*myfunc)(int, int);
this will declare myfunc as a type which is a pointer to a function expecting two integers and returning void. int typedef x;
typedef what;
struct { int x; } typedef *x, y;
enum { x } typedef;
Storage specifiers are pretty weird. This nonsense is allowed at function or parameter list scope, but not file scope: void f() { enum { x } register; }
void f(enum { x } register);
I've never understood why Ritchie designed C's declaration syntax to have so many dumb edge cases. int *a, *b, *c; // a, b and c are pointers to int
The reason is that I am used to reading variable declarations as MyType myVar1, myVar2, myVar3; and I always read “int” as the type “integer pointer”. I therefore wanted the following int* a, b, c; // a is a pointer to int, b and c are ints
to mean that a, b and c all were of type int, i.e. pointers to int. and I therefore found it slightly annoying to repeat the asterisk for every variable. This also meant that the symbol * had two slightly different meanings to me: (1) It declares a pointer or (2) it dereferences a pointer.I usually don’t declare a whole lot of pointers in one line, but still, this is a (minor) annoyance I have briefly discussed with few fellow programmers over the years.
Today I started reading C Traps and Pitfalls by Andrew Koenig and after reading one sentence, in chapter two, the pointer declaration syntax suddenly makes sense:
[…] Analogously,
float *pf;
means that *pf is a float and therefore that pf is a pointer to a float.
Of course! If I instead of looking at it as a variable a of type int, we read it as a – i.e. “a dereferenced” – it makes sense. That is indeed an int, and that also means that * always means “dereference”. <scope> <type> <name>; // variable declaration
typedef <name> = <type> // typedef declaration
(with <scope> = "static", "auto", or nothing)
That would be a first step. A second one would be to use parentheses to denote grouping. Such that extraneous parentheses does not screw up the whole type declaration: // the following two lines are equivalent
[]*int p; // array of pointers (but this is not clear)
[](*int) p; // Ah, now this is more obvious.
int*[] p; // alternate syntax, with postfix notation
(int*)[] p; // (personally, I prefer the prefix one)
*int[] p; // mixing postfix and prefix does no good.
Same rule for const: const *int i; // constant pointer to mutable int.
*const int i; // mutable pointer to a constant int.
*(const int) i; // again, we can clarify.
const (*int) i;
(By the way, I think we should make const the default, and use a "mutable" keyword instead. But that's another fight.)Functions could be declared in simpler ways:
bool (int i, float x) f;
(int i, float x) bool f; // alternate syntax
(int i, float x) -> bool f; // alternate syntax 2
bool <- (int i, float x) f; // alternate syntax 3
C doesn't do currying by default, so I don't really care wether the return type goes before or after the arguments. But the name of the function should definitely be on the right of its own type, so we still follow the golden rule. Function definition would be equally simple: bool (int i, float x) f =
{
return 2 * i + x;
}
Note the similarity with int i = 42;
Now you want a pointer to a function? Easy: you just prefix (or postfix, depending on your ultimate choice) the type with a star: *(bool (int i, float x)) fp = f;
// Note: this one is ambiguous without precedence rules:
*bool (int i, float x) fp = f;
// And this one clearly denotes a function wich returns
// a pointer to bool
(*bool) (int i, float x) fp = f;
Structure (and class) declaration could also use a bit of makup: typedef Foo = struct {
int i;
float x;
bool b;
};
There, the syntax of types is much nicer, and easier to parse (it doesn't really matter for C, but C++ could really use some love).Also worthy of mention is, this syntax above is less restricted than the ANSI one. However, the semantic restrictions still hold. This declaration for instance would be syntactically valid, but semantically bogus:
void (int i, struct { float f; bool b; }) f;
It could work if C were ducked typed. :-) > You can probably hack together a nice language around C.
That's been done at least twice already -- for some (very debatable) values of 'nice'. See C++ and Objective-C. void (int i, struct { float f; bool b; }) f;
Apart from f being in the wrong place (and bool not being defined without including stdbool.h), this is a valid C declaration.I'm paraphrasing, but I believe the explanation is that `int a* = 5;` is a mnemonic, i.e. that dereferencing the pointer a will evaluate to 5.
Must have read that section five or six times over the years but only a few months ago did it sink in.
int* a, b, c;
Just use: int* a; int* b; int* c;
The type is really int* but C legacy treats b and c as int in int* a, b, c;