7 ms·
IMO, pointers are less difficult to comprehend than other abstractions, like lambdas are. If you know how to walk down a street and stop at the right street nu
by blunte 6y ago
IMO, pointers are less difficult to comprehend than other abstractions, like lambdas are.
If you know how to walk down a street and stop at the right street number, then you have used pointers. And if you've ever observed that one tall building may "cover" a range of street numbers, such as 200-220, then you should understand how to move from one 4-byte "value" to the next in an array in memory.
Anyway, many more analogies... probably better than this one.
Maybe unions could make using pointers a bit more challenging, but again, tall buildings next to short buildings and so on. We do this kind of pointer calculation in real life.
- atoav 6y agoWhat pointers basically are is not particularly hard to grasp. What is harder to grasp it what can be done with them and how you can shoot yourself in the foot with them in non-obvious ways. I think I only understood much of it once I learned Rust, because you realize: Ah, that thing I once did in C is something that maybe ahouldn't be possible at all without extra steps. Even if I were to nwver use Rust again, this definitly helped to understand how to use pointers more safely.
- chasil 6y agoI think that pointers are a tool to control a CPU's indirect addressing modes from a higher-level language. Exposure to an assembler makes pointers easy to understand.
- qsort 6y agoWhat's difficult to understand about pointers isn't the concept of a pointer itself, or even * and &, it's the fact that working with pointers requires you to simultaneously understand different abstraction levels. While it's not unique to pointers, and it's in fact the case for most nontrivial programming tasks, what's unique about C is that pointers are so pervasive you can't really do anything if you don't understand how to work with them. IME languages like Python aren't any easier than C to work with (ignoring UB issues of course), but it's certainly the case that you can probably kinda sorta get your job done with Python even without understanding the first thing of what you're doing, and that's not happening if you write in C.
- bluetomcat 6y agoIn C, pointers require you to think deeply about the ownership and lifetime of any "allocated object" at runtime. How long does it live, who is responsible for the deallocation, how many pointers does your program hold to that object (dangling issues). Ultimately, it can lead to a cleaner design if these issues are taken seriously up-front.
- qsort 6y agoI don't disagree with that, but most cases fall within a pretty clear pattern: - typedef struct { ... } foo - foo *foo_create() - void foo_destroy(foo *) - a bunch of functions that take foo* as their first arg which is kind of the same as a class and only more error-prone. I say this as someone who actually _likes_ C, but the manual memory management model is very often unnecessary, confusing, repetitive. There was an idea some time ago of a language extension that would extend the concept of automatic storage duration to allow an explicit destructor to be called when the variable goes out of scope, like <close> variables in some languages. I genuinely think things like that would make the language a bit more ergonomic without fundamentally changing its nature.
- bluetomcat 6y agoThat's why I tend to always prefer automatic and static storage to dynamic allocation wherever possible, especially in cases where you don't have "N" items or "N" cannot possibly exceed a certain small value. Also, allocation/deallocation of the certain object need not be defined within its module. It should be up to the caller to decide whether to allocate the object on the stack, statically or dynamically depending on the caller's situation: foo f; foo_init(&f); foo_destroy(&f); ... foo *g = malloc(sizeof(foo)); foo_init(g); foo_destroy(g); free(g);
- unwind 6y agoThat is of course fine, with the drawback that it requires a public definition of the foo type. I would write the allocation as foo * const g = malloc(sizeof *g); to avoid repeating the type name and "lock" the allocation to the variable. If the type on the left hand side ever would change, this still does the right thing.
- cassepipe 6y agoPointers aren't that complicated but C syntax is misleading imho until someone comes along and tells you that the declaration syntax "follows use", which does not seem like the greatest idea. Then you get used to it and forget about it but when you don't know the principle behind declaration syntax, it does not help you reason about the language.
- young_unixer 6y agoYup. I spent a ton of time trying to make sense of pointer syntax (* operator/modifier), until I realized that it didn't really make sense (except for the simplest cases) and then I could get on my life.
- cassepipe 6y agoThere's actually a logic to it, although not an intuitive one at all, and it's that declaration should follow use. That is `int *ptr;` is a pointer to an int and to get the int you have to use * on it.
- jstimpfle 6y agoThe logic is that by declaring `int *ptr;` you effectively say that `*ptr` is an `int`. Only the first token (which must be a type) is syntactically special, the rest is normal expression syntax. Same goes for arrays, `int *x[30]` just says that `*x[30]` is an `int` (well, technically it's out of bounds by 1), thereby we're declaring an array of 30 int-pointers. Of course, that hasn't been entirely true since function declaration changed with ANSI C in the late 80s or so.
- kaba0 6y agoBut int-pointer is the type of it. It makes no sense that it “sticks” to the variable name.
- Koshkin 6y agoThe reason the asterisk naturally "sticks to" the name is simply the possibility to write a series of declarations like this: int a, *b, (*c)(int);
- ZoomZoomZoom 6y agoPointers seem easy until you understand the pointers you're working with do not correspond with the basic hardware model you have in mind: https://www.ralfj.de/blog/2018/07/24/pointers-and-bytes.html https://www.ralfj.de/blog/2018/07/24/pointers-and-bytes.html https://www.ralfj.de/blog/2020/12/14/provenance.html https://www.ralfj.de/blog/2020/12/14/provenance.html https://www.ralfj.de/blog/2019/07/14/uninit.html https://www.ralfj.de/blog/2019/07/14/uninit.html
- jhgb 6y ago> If you know how to walk down a street and stop at the right street number, then you have used pointers. And if you've ever observed that one tall building may "cover" a range of street numbers, such as 200-220 I see that as a European, I have virtually no chance to understand pointers using street numbers. :) (Fortunately I've never had problems either with lambdas or with pointers.)
- ricardobayes 6y agoLike my professor used to say, if you ever saw a pidgeon, you know what vectors are.
- mytailorisrich 6y agoC is an abstraction over assembly, really (benefits being that it is simpler by being more abstract and portable across CPU types). I've always thought that an introduction to CPUs (can take a simpler one as example) and how they work, how memory is (usually) organised, and to assembly would go a long way in helping understand many programming issues and C. My experience is that C or programming concepts are often taught in a very abstract/mathematical way, which can be hard to grasp compared to a more practical approach. If you take a concrete example where memory is effectively an array and indices are addresses (which holds true for most cases and, in any case is a good example) then understanding pointers becomes basically common sense and notations are simply conventions of the language you're using.
- Arch-TK 6y agoTreating C as an abstraction over assembly is a surefire way to step into all the thousands of sharp edges C has. In fact I would hazard a guess that the majority of bugs found in software written in C are a result of programmers treating it as a portable assembler instead of a language for programming an abstract machine. So many incorrect assumptions arise as a result of telling people to treat C as a portable assembler that I think it's safe to call it an extremely bad bit of advice.
- mytailorisrich 6y agoI was discussing pointers. I only commented in passing about C being portable over assembler. You could rephrase this as it being a language for programming an abstract machine and it would not change anything about my comment (nor would it change the fact that C is an abstraction over assembly) Thank you for your reply...
- Arch-TK 6y agoThe fact that you didn't use the term "portable assembler" when saying: "If you take a concrete example where memory is effectively an array and indices are addresses (which holds true for most cases and, in any case is a good example)" doesn't change the fact that the statement makes links to how actual machine memory operates on what you personally think is a usual machine. This is really not a good idea because it encourages people to think of pointers as all existing in the same place and encourages erroneous thinking such as subtracting two pointers to different objects or confusion surrounding why the numbers you get when you `int a; char b; printf("%p, %p\n%p, %p\n", &a, &a+1, &b, &b+1);` differ in separation. Also subtler errors arise such as people assuming that all pointer types are effectively equal as long as you convert back to the correct pointer type when it comes to using the pointer (really subtle issues like people assuming that you can convert `int *` to `void *` safely). The "very abstract" way C is taught actually prevents people from making such assumptions by not priming them to make them. The fact that people get complacent and start to lean on their understanding of (what they think are) real machines to write C is the result of the bugs I mentioned in the previous response.
- augustk 6y agoFor anyone who wants to learn about pointers I can recommend studying a language simpler than C like for instance Oberon where pointers are more restricted. Having a look at Oberon can also broaden your view even if you know pointers in C. https://www.miasap.se/obnc/oberon-report.html http://people.inf.ethz.ch/wirth/Oberon/PIO.pdf
- mumblemumble 6y agoPointers are like swazzles[1]. The construction is very simple. The principle behind how they work is very simple. Learning how to use one well enough that you can (a) consistently make it do what you want, and (b) not injure yourself in the process, though, is no mean feat. [1] https://www.atlasobscura.com/articles/swazzle-punch-and-judy https://www.atlasobscura.com/articles/swazzle-punch-and-judy
- chowells 6y agoWell, you've demonstrated that memory addresses aren't that hard. But you've also demonstrated how easy it is to get undefined behavior in C programs. C's pointers aren't memory addresses. Ok, they tend to be represented as such at run time, but that's not what the spec actually says the are. And as far as compiler authors are concerned, they can do anything they want as long as it's within spec. Further, the spec even requires some additional behaviors pure memory addresses aren't capable of. See https://www.ralfj.de/blog/2020/12/14/provenance.html https://www.ralfj.de/blog/2020/12/14/provenance.html for examples of the extra requirements. Compared to that mess, lambdas are trivial. They're just functions.