
Pointers in C (2010) - prabhupant
https://boredzo.org/pointers/
======
_kst_
I checked the section "Interlude: Arrays" because the relationship between
pointers and arrays in C is a major sticking point.

It claims that, given a declaration

    
    
        int array[] = { 45, 67, 89 };
    

the expressions "array", "&array", and "&array[0]" are all equivalent. They
are not. "&array" and "&array[0]" both refer to the same memory location, but
they're of different types.

In the next section:

"By the way, though sizeof(void) is illegal, void pointers are incremented or
decremented by 1 byte."

Arithmetic on void pointers is a gcc-specific extension (also supported by
some other compilers). It's a constraint violation in standard C.

I don't think this is the "best" article on pointers in C.

I usually recommend section 6 of the comp.lang.c FAQ,
[http://www.c-faq.com/](http://www.c-faq.com/)

~~~
emmelaich
The C faq presents as supposedly common questions and answers without saying
what a pointer is.

In contrast I really like the posts definition.

> _A pointer is a memory address._

Not perfect but for concision and accuracy it cannot be beat.

~~~
Stratoscope
Well, it's concise, but it leaves out something important. A C pointer has
both a memory address _and_ the type of whatever it believes it points to. The
latter is what makes C-style pointer arithmetic possible, because if you know
the type, you know its size too.

~~~
vram22
Right. That's why, when you have an array of structs, say each 8 bytes in
size, and a pointer ptr to the start of it, each time you do:

    
    
      ptr++;
    

it increments the address stored in ptr, not by 1, but by 8 (bytes) - so as to
now point to the next struct in the array. Same if you do:

    
    
      ptr--;
    

except in that case it decrements the address by 8.

And if you did:

    
    
      ptr += 2;
    

it would increment the address in ptr to point to 16 bytes further ahead in
memory than it was earlier, for the same reason. ptr will now point to the
struct which is two items further ahead in the array. So you can access that
struct with the expression:

    
    
      *ptr

------
macintux
I don’t have my copy at hand, but _Expert C Programming: Deep C Secrets_ has
the best coverage of pointers and arrays I’ve seen.

In particular, it supplies a useful algorithm for decoding all pointer
declarations such as functions that return function pointers.

[https://www.goodreads.com/book/show/198207](https://www.goodreads.com/book/show/198207)

~~~
aasasd
I'm guessing that the algorithm may be the spiral rule:
[http://c-faq.com/decl/spiral.anderson.html](http://c-faq.com/decl/spiral.anderson.html)

~~~
palotasb
The spiral rule is wrong. It breaks for things as simple as arrays of arrays.

    
    
        //        +---------+
        //        |   +--+  |
        //        |   ^  |  |
            int /*|*/ aa[2][3];
        //   ^    |      |  |
        //   |    +------+  |
        //   +--------------+
    

The correct result is "aa is a (2-element) array of a (3-element) array of
ints".

To get the correct interpretation, you have to know that the spiral has to
avoid the "int" element after passing through "[2]". This defeats the purpose
of the spiral, since the line sometimes goes through the element and sometimes
it doesn't. For example if it were "int * paa[2][3]" instead, the correct
order is { [2]; [3];* ; int }. Note how the star is first avoided and comes
after [3]. A 2-array of a 3-array of pointer to int.

How would you know when the spiral "avoids" the element on the left and when
it doesn't? Well, you need to know the declaration grammar to know that [] and
() bind stronger than the thing on the left, so you need to process those
first. But if you know this, drawing a spiral is redundant, because you
already parsed the thing.

I think the spiral rule is inherently wrong and should not be reposted as a
helpful cheat-sheet for parsing C/C++ declaration syntax.

~~~
palotasb
A good exercise is to then guess why the spiral rule seems to work most of the
time even though it is inherently wrong.

The reason is that some types that are syntactically valid are forbidden by
C/C++. You cannot have a function returning a function. A function returning
an array. An array of functions. You can only have a pointer to these
(function returning a pointer to function/array or an array of pointer to
functions), and then they need to be correctly parenthesized. Then the spiral
rule works because you only have two elements in a parenthesis and you can
just go right and then go left... unless you have arrays of arrays which are
legal, and then it doesn't work.

But the more correct rule would be to go right and parse all [] and () inside
the current parenthesis level, then go parse the * -s (including
const/volatile) on the left. Then repeat for outer parenthesis levels.

~~~
aasasd
Aha, so the final working algorithm is "the spiral rule but with a rightward
detour on arrays of arrays."

~~~
kazinator
The working algorithm is that the postfix operators in declarators (just like
in expressions) have higher precedence than the unary ones (just like in
expressions), but are overridable with parentheses (like in you know what).

The declared identifier is, first and foremost, the clump of high precedence
postfix things that are on it:

    
    
         a[3][4][5];  // a is an array of array of array
    
         b(int);      // b is a function
    
         c(int)[3];   // c is an array of functions: nonexistent
    

Then we consider the unaries:

    
    
         *** whatever;  // whatever of pointer to pointer to pointer
    

Then the declaration specifiers:

    
    
         int whatever;   // whatever of/to int

------
aasasd
Ah, boxes again.

For me, all confusion about C's pointer-happiness cleared up when I finally
realized that C (and Asm, I guess) works with heap memory as a big blob of
bytes, and it's programmer's job to keep the blob's contents from getting
messed up―with some thinly-veiled help from the language and the compiler.
Everything else, including variables, is just syntactic sugar when it points
to the heap.

(With the clarification that afaik variables are different when they're on the
stack or registers, becoming first-class 'indivisible' entities).

~~~
DonHopkins
My favorite proof that C arrays and strings are actually just syntactic sugar
for pointer arithmetic is that the following are all valid and equivalent:

    
    
        char theLetterC = "ABC"[2];
    
        char theLetterC = *("ABC" + 2);
    
        char theLetterC = *(2 + "ABC");
    
        char theLetterC = 2["ABC"];
    

"You can't prove anything about a program written in C or FORTRAN. It's really
just Peek and Poke with some syntactic sugar." -Bill Joy

~~~
danlugo92

        char theLetterC = 2["ABC"];
    

Wait, what? And I thought I was good with pointers...

Edit: I think I got it, is it this?:

"ABC" \+ 2 == 2["ABC"] == *(2 + "ABC")

Right?

~~~
aasasd
I'm not a C man, but afaiu, "ABC" is syntactic sugar for a char pointer to
that string. So it works like any pointer with regard to index access.

------
kccqzy
The title claims everything, but certainly it doesn't cover everything. It
mentions const but doesn't mention volatile. It also doesn't mention restrict
which is more confusing than const/volatile. It didn't mention the strict
aliasing rule. It actually didn't even mention NULL.

~~~
dang
We uneverythinged the title above.

------
User23
No C pedantry thread would be complete without a link to one of the finest
resources on the subject: [https://blog.regehr.org/](https://blog.regehr.org/)

------
Apocryphon
I've always been partial to Dennis Kubes' five minute guide:

[https://news.ycombinator.com/item?id=4389691](https://news.ycombinator.com/item?id=4389691)

------
maxxxxx
I always wonder what makes pointers so difficult for some. So far I have
always been able to explain it to people by drawing a linear memory space and
then showing how different types are allocated. It seems to me that pointers
are one the easier concepts in programming.

~~~
aasasd
My guess is, people are trying to keep variables the primary concept, and
explain pointers on top of that. Hence, ‘boxes’ again crop up in an
explanation of pointers.

For me, everything became much more clear when I realized that the heap memory
stands on its own as a concept, being a big pile of bytes instead of
‘boxes’—and implicit allocation/deallocation of variables is just a thin veil
on top (muddying the matter somewhat since vars can themselves be stored in
memory and point to it).

Guess I had to deal with OOP before I grokked all the pointer-juggling going
on behind the scenes.

~~~
maxxxxx
I visualize memory as a big range and variables occupy space on them. The
pointer is just the first byte of that space. Once you realize that C is
basically placing stuff on memory things become pretty clear I think.

------
edoo
Pointers in C are an incredibly simple concept. So simple it might take you
years to master them.

------
ktpsns
When graphical gimmics steal the show:
[https://boredzo.org/pointers/boxes.png](https://boredzo.org/pointers/boxes.png)
visualizes both pointers and their target (an integer) as 3d boxes. So the
sizeof(int) is the edge length of this target?

Normally we see such guiding images with 2d boxes. There's nothing wrong with
3d because clearly, integers are also not 2d (they are not even 1d). However,
don't associate the size of theses boxes with the memory size of an element.
This overstresses the analogy and suggests the whole thing is a vector space,
but it isn't.

------
ananonymoususer
Some of the cited examples are missing underscores. E.g. " _ptr a " should be
"_ptr_a".

------
mchobbes
This is why people think programming is difficult. That same article could
have been written in a significantly less convoluted way, and had a much
broader reach, particularly for people who aren't already at least moderately
familiar with C.

------
em3rgent0rdr
"Everything you need to know about pointers in C"

 _everything_?

~~~
dang
[https://news.ycombinator.com/item?id=18587411](https://news.ycombinator.com/item?id=18587411)

------
keyle
I joked the other day to a co-worker, currently working full time in Python,
that you get used to the list comprehension and other nice things of Python so
much, that you'll never be able to go back to gnarlier languages like Java/C.
It's just too nice. You can get python to run really fast nowadays and if you
can't, you still got nim.

~~~
n4r9
C# and Java 8 (I believe) provide functional patterns. e.g. in C# the ToLower
function which takes a string and returns a string can be used like:

    
    
      var lowercaseIds = ids.Select(ToLower)

~~~
Traubenfuchs
You believe correctly, but naturally, it's a lot more verbose:

    
    
      var lowercaseIds = ids.stream().map(String::toLowerCase);
    

and an additional

    
    
      .collect(Collectors.toList())
    

if you don't want a Stream instance. (var is Java 10, though I believe it
should not be used in production code, ever)

~~~
Merad
> var is Java 10, though I believe it should not be used in production code,
> ever

Is that your opinion of var in general, or something about Java's
implementation of it? If the former, I'm really curious why, as a C# dev who's
used it for many years.

~~~
Traubenfuchs
Unless you are using a constructor to fill the var, it makes
reading/exploring/understanding source code more difficult. If the value comes
from a method, enjoy chasing it down in your VCS. In your IDE you only need to
hover it, but it's still an unnecessary extra step.

Sometimes I explore source code on GitHub, excessive use of var makes reading
it very uncomfortable.

Turning it around, what are the benefits of var? Slightly increased typing
speed. More time is spent reading source code than writing it and var makes
reading harder and writing easier, so it's not worth the trade in my opinion.

~~~
Merad
I'd actually argue that var makes it _easier_ to read and understand code.
Most of the time, an explicit type on a variable declaration is just noise,
because it's a repetition of what we already know from the same line of code.
The main place where this doesn't apply is when the variable is initialized
from a method call, but even then I tend to find that it's usually quite easy
to infer the result type based on context (variable or method name), even
without using IDE features. It's definitely different and requires some
getting used to, but I personally see a lot more positives than negatives from
it.

RE your sibling comment about dynamic: I primarily use it with json. It's
useful for things like one-off error responses where you just need to grab a
message or code to return/throw and there's really no benefit from introducing
a class for that one usage. I did once work on a project where that was
attempting to re-use some awful legacy code in a new app, and they leaned on
dynamic a lot to make the legacy code work without to re-architect it
correctly. It was about as terrible as you're imagining.

