29 ms·
A gentle introduction to pointers using C (2019)
- enriquto 6y ago> Pointers are variables that hold a reference to something in memory. This sentence is extremely confusing or meaningless. I would rather say: "A pointer is an integer that indicates a position in the RAM." Thus, if a pointer "p" indicates a position in memory, then "p+1" is a pointer that indicates the next position in the memory. The value on that cell of memory is often written as p[1].
- efxz 6y agoFor a noob like me, Your explanation is even more confusing. Sorry.
- speedgoose 6y agoYour version isn't very true though. Most operating systems have virtual memory.
- Koshkin 6y agoSome things should be left out until the kids grow up a little. (Otherwise you would have to describe gruesome details of swapping and memory mapping and how dereferencing a pointer might trigger reading from disk etc.)
- frabert 6y agoA newcomer can assume virtual memory = physical memory without any problem. Hell, anyone who's not doing kernel dev can do that. I do it all the time.
- speedgoose 6y agoI usually prefer to not mention something immediately rather than teaching something wrong.
- enriquto 6y agoThis is like explaining general relativity to people who don't know yet newtonian mechanics.
- julian_t 6y agoYou need a bit more than that if people are going to understand you... what is a "position", and what does "next position" mean?
- pingyong 6y agoI've always explained it to people like this: Imagine a big cabinet, like a bunch of safe-deposit boxes in a bank. Each box has an unique number written on it, and the boxes are simply numbered 0 to 99. Now when you open a box, you find a piece of paper in it with a number between 0 to 99. It tells you which box to open next - so it points to another box. That's a pointer. Seems to work pretty well, at least for conveying the basic idea of pointers and dereferencing.
- enriquto 6y agoBut why do you need to mention that this number is inside a box? It seems strangely contrived. Even if the number was outside the box it would be a pointer, it has nothing to do with being in the box.
- pingyong 6y agoBecause usually if I want to store something I put it inside the box? Not sure how that is contrived.
- enriquto 6y agoWhen you explain floating point numbers you also put them into boxes? No need for that, you can explain them intrinsically, just like pointers. Moreover, in practice, it is clear to think that you already have your pointer in your hands (e.g. in a register), no need for your pointer to come from the RAM that it is used refers to. This is an unnecessary recursivity.
- pingyong 6y ago>When you explain floating point numbers you also put them into boxes? That makes no sense. If I was explaining floating point numbers with boxes, for whatever reason, I'd put the values inside the boxes, yes. The analogy just doesn't work here, but the analogy for memory makes perfect sense. >This is an unnecessary recursivity. No, it's not. Think of a linked list for example. Traversing it makes perfect sense with my analogy: You open the first box, look at the number that's inside, open the box corresponding to that number, look at what number is inside that box, open the next corresponding box, etc. - it all makes immediate and intuitive sense. If you just start with "open box x" (because x you somehow already know magically because it's already in a register), you haven't really explained much. Or think of pointers to pointers. With my analogy: Easy! It's just another box which contains the value of the next box. If this concept isn't explained, a pointer to a pointer makes no sense because the pointer value has no corresponding box, hence no address.
- Koshkin 6y agoWait till they start learning about C++. A notion of ‘reference’ there that is separate from that of ‘pointer‘ confuses things even more.
- pjmlp 6y agoNot sure why it should be confusing it goes all the way back to Algol and the languages derived from it on the Pascal/Mesa/Modula/ML family branches.
- AnimalMuppet 6y agoIt took me a minute to recall what you're referring to. To save everyone else the trouble: Pascal had pointers. And it also had var parameters, which are essentially pass-by-reference. (At least, I presume that's what pjmlp is, um, referring to...)
- pjmlp 6y agoNot only, in Ada, Modulas and Oberons you can have them as local variables as well, and in the variants with GC support, they split further into traced and untraced references.
- Koshkin 6y agoBecause in C these notions coincide.
- pjmlp 6y agoReferences are just implicit pointers, it is so hard to get them? Not sure what is so hard, then again I learned them in Basic and Pascal variants before getting to learn C.
- Koshkin 6y agoOK, first of all, the syntax and the semantics of a pointer and a reference are completely different from each other. Departing from C, one must start thinking of a pointer as a variable containing a value that is a reference, the latter now always being a constant. That's pretty subtle, I must say. Add to that a very important distinction between lvalue, rvalue, and "universal" references, and then one needs to stay very much current as a C++ coder to remember all that.
- scoutt 6y ago> A pointer is an integer that indicates a position in RAM, ROM, NVRAM, flash, etc. A pointer stores a number that represents an address, without making reference where is located. Can be 0 (NULL). "p+1" can be the next byte in RAM, or the next 4KB if p is of type "4KB struct".
- blondin 6y agoyes, i find OP's definition very clear and by saying number instead of integer you made it even better. for example i was re-reading some math stuff yesterday that made me realize that sometimes we want natural number instead of integer which can be negative...
- SAI_Peregrinus 6y agoNULL doesn't strictly have to be 0. It usually is, but it could be defined differently depending on the architecture.
- enriquto 6y ago> A pointer stores a number Why not "is" instead of "stores" ?
- scoutt 6y agoA variable that is a pointer occupies space in memory. In that memory (the pointer of the pointer) is stored the number. char* ptr = (char*) 0x80000000; By doing `&ptr` you can know the address of the pointer `ptr`. In that address, the number 0x80000000 is stored.
- necheffa 6y agoTrouble is pointers don't have to be integers. The standard lets you address by fruit and veggies if you have such a machine.
- enriquto 6y agoHowever they are implemented internally is a detail. Formally they are "affine" integers, in that you can compute the sum of an integer and a pointer to obtain another pointer, and compute the difference of two pointers to obtain an integer. I agree that "a pointer is an integer" is an over-simplification. But it is a very useful one when you are learning C. Then you will have time later to enter into the gory details, once you fully master the simple case.
- jcelerier 6y ago> and compute the difference of two pointers to obtain an integer. This is only a legal operation if those two pointers are part of the same object / allocation. It's UB (see C11 6.5.6 Additive operators ; C++ is similar) to do for instance char* ptr1 = malloc(10); char* ptr2 = malloc(10); ptr2 - ptr1; so they don't really behave like any algebraic definition of integers in that regard. Even just comparing them with (ptr1 < ptr2) is UB.
- leni536 6y agoIn C++ comparing them is unspecified, and theoretically can even change from invocation to invocation. But it's not UB. http://eel.is/c++draft/expr.rel#4.3.sentence-1 http://eel.is/c++draft/expr.rel#4.3.sentence-1 std::less is blessed by the standard to work with any two pointers, so relational containers work with pointers.
- bubblethink 6y agoThe other annoyance is the unsinged v/s signed mess in going from size_t to ptrdiff_t.
- 6y ago
- ChrisSD 6y agoThat ends up being misleading though as it ignores optimizing compilers. A pointer isn't simply an integer address of memory. It's tagged (by the compiler) with information about e.g. which allocation unit it came from and what extent it has (i.e. how far you can move the pointer forwards or backwards). The long and short of it is that doing `p+1` can be UB. Also two pointers can point to the same actual memory address but not be equal. Pointers are, in practice, a complex abstraction. It can be hard to understand all the subtleties, especially if you're used to the simplicity of memory addresses in assembly.
- melvinroest 6y agoHere's my explanation. A pointer stores a memory address. But I hear you say: can't an int or long also do that? All you need is the number of the memory address. Well, the magical thing about a pointer is that it can use a special operator! What? (I hear you say) Yes! Here is what it does: it allows you to obtain the value that is stored at that memory address. What does a pointer store again? (a memory address) But it can also obtain a value at that memory address. You can't do that with an int, I've tried [0]. We call this operator the dereference operator [1]. It looks like a star * That's the one. That's the dereference operator. One of the confusing things is that when you declare a pointer, you also use a * Example int *a; //or int * a; //or int* a; In those 3 examples, the * is not a dereference operator. It simply indicates that it is a pointer. In this case we have an int pointer. I always like to view it as: we have an int! And this is how many times we're allowed to use the dereference operator: one time How many times are you allowed to use it on this pointer? int *****super_pointer That's right, five times! So when you have int val = 10; int *a = val; Then what is the value of a? It's 10. What do pointers store? Addresses. So what address is this? Address 10! But when it comes to memory addresses in computers, there is a tricky part to it. In normal operating systems, low addresses are reserved for special things, I think it's the kernel in this case. So if you dereference that particular address, the OS/runtime environment won't allow you to. They will give you an error called a segmentation fault. So you need to get a valid address of the variable val, but how do you do that? We have another operator for that, which I like to call the address-of operator. So when you have int val = 10; int *a = &val; // & is the address-of operator Then the variable a has the valid memory address we want! There's much more to learn, but I'll leave it at this for now. --- I recommend people who start learning pointers/programming to use GDB with the TUI graphical interface. One could also print the values, but it's easier to see what really happens. For that there are two steps you need to do: - Watch a GDB with TUI tutorial: https://www.youtube.com/watch?v=AxsSXMPD3N4 https://www.youtube.com/watch?v=AxsSXMPD3N4 - And then also display all the cpu registers. The reason for that: your variable values will be visible in there (in hexadecimal). All the GDB commands: https://sourceware.org/gdb/onlinedocs/gdb/TUI-Commands.html https://sourceware.org/gdb/onlinedocs/gdb/TUI-Commands.html (look for the regs option) [0] https://stackoverflow.com/questions/35379191/cannot-cast-variable-type-to-its-pointer-type-e-g-int-to-int-i-guess-its-a https://stackoverflow.com/questions/35379191/cannot-cast-var... [1] Side note: I used to have trouble with the word dereferencing. If you have to, here is why it's called that way: https://stackoverflow.com/questions/35372786/what-does-the-de-prefix-in-dereference-mean-is-there-a-linguistic-explanation https://stackoverflow.com/questions/35372786/what-does-the-d...
- greypowerOz 6y agoas a C noob, this example is humorously confusing... declare an array, fair enough, makes sense. Declare a char *pointer,(a "reference to something in memory") ok if you say so. Assign the char pointer the ARRAY name.... erm, ok.. so ... that would make the char pointer the 'first char in the array' then? lol no you silly old duffer, it sets the char pointer to the memory address of the first char in the array.. not the contents of that location, obviously ;)
- scoutt 6y ago> that would make the char pointer the 'first char in the array' then? That would make the char pointer to point to the 'first char in the array'.
- naiveprogrammer 6y agoAnd why is that? I am a certified C noob and this really confuses me. Why `alphabet_pointer = alphabet;` points exactly to the first element in the array? Why not the whole array? Why not the last? Can we have a pointer that accepts a whole array? Thanks
- leetcrew 6y agoa c array is literally just a sequence of objects in memory (possibly with padding, but you can ignore that for a while). you can think of pointing "exactly" to the first element as being equivalent to pointing to the whole array. you need to know where the array begins and its size to do anything with it. but once you know where it starts, you can access the next element by adding sizeof(char) (or whatever the element type happens to be) to the pointer.
- wmkn 6y ago> possibly with padding What padding?
- 6y ago
- jasode 6y agoA meta comment on tutorials to teach pointers: If pointers are so "simple", why are they so confusing?!? Because pointers are one of those concepts that's easier to use than to explain. Some concepts like pointers (and also monads and also English words like "a" and "the") are easier to teach by examples instead of by definitions. E.g. Consider the English words "a" and "the": a 5-year old can fluidly use those connecting words when talking but a 50-year old adult that's been speaking English for decades will have a tough time defining what the words "a" and "the" _mean_ without giving a confusing and circular explanation. Attempted explanation: the "a" is the indefinite or general signifier while "the" is the definite or specific signifier -- what's the problem?[1] How did small children learn how "a" and "the" work without being given a formal definition? Because of numerous examples. By working through enough examples, the lightbulb moment happens ("oh gee, is that all pointers is?!?") and the learner is able to derive his own definition. But the irony is that the learner now has The Curse of Knowledge[2] and he/she now thinks he can write a "better" tutorial to explain pointers but new learners reading that tutorial will still say "I don't understand your explanation at all." [1] riff on : https://stackoverflow.com/questions/3870088/a-monad-is-just-a-monoid-in-the-category-of-endofunctors-whats-the-problem https://stackoverflow.com/questions/3870088/a-monad-is-just-... [2] https://en.wikipedia.org/wiki/Curse_of_knowledge https://en.wikipedia.org/wiki/Curse_of_knowledge
- melvinroest 6y ago> If pointers are so "simple", why are they so confusing?!? Because pointers are one of those concepts that's easier to use than to explain. I disagree (the rest of your comment I fully agree with, FYI). What I personally found difficult is to understand that: when you dereference an invalid memory address, then the dereferencing operation is working, but the OS/runtime environment won't allow you to do it! Wait, what? Why?! Who cares my value is wonky, why is my program giving an error? Just obtain the wonky value! (is what I thought when I first did it) The reason it's so difficult because at this stage you're still using printf for debugging, while you really should use a debugger, to really understand what's going on under the hood. Preferably, one also learns a modicum of x86 to really see it immediately. The concept of dereferencing the wrong values isn't explained a lot. How to troubleshoot segfaults isn't explained at all in beginner tutorials, not even in my own (I just wrote an explanation for fun as a comment in the same thread [1]). Also FWIW, when I heard the concept of pointers I found them intuitive immediately. I tend to think and reflect a lot, and the explanation of referencing and dereferencing almost sound like some proverbial life wisdom to me. I found their use a lot harder, in part because I found code syntax hard. In another part, because I used to find "doing things" and "building things" (of any kind) an insurmountable task. --- With that said, I do agree with the rest of your explanation. I suppose it also depends on who teaches pointers to you. I got a classroom explanation first, a few examples and then some exercises. Then suddenly, I had to write a 500 line C program. Then all the complexity storms at you like a rabid zombie army. But if you're used to writing small C programs all the time and picked up on pointers that way, but then suddenly have to explain it. Then, I can see how that's hard. [1] https://news.ycombinator.com/item?id=23550040 https://news.ycombinator.com/item?id=23550040
- Jataman606 6y agoThe point where I actually understood everything about pointers was when i realized that they just hold memory address. After that everything like pointer arithmetic and dereferencing pointers become clear as day.
- Koshkin 6y agoIt is easy to believe that you understand something while in reality things are more complicated. What is an address? Is it like an integer value? (Is it really? Or can it be converted to one? Even if so, how many bits wide is it?) Or, if not, what is its structure? Back in the day, a question would be is it a 'far' or a 'near' pointer or we are living in a flat address space? Also, how is pointer arithmetic affected by alignment?
- megous 6y agoYeah, you may use a pointer in C, but it will compile to an instruction that fetches some data from a location in memory relative to the currently executed instruction.
- EvanAnderson 6y agoI feel like spending some time programming in assembler, even just Z80 or 6502, would give most people an understanding of pointers in very short order.
- Koshkin 6y agoTrue, but you do not need to know assembler to have a general idea of what computer memory looks like; but more importantly, C's notion of a pointer is more complex as it is tied to the data type and its 'sizeof' and is further complicated by association with arrays.
- Koshkin 6y agoA pointer monad, anyone?
- mrweasel 6y agoI wish someone would do a gentle introduction of header files. I sort of understand pointers, at least on a basic level. Headers files are apparently much easier, so they get the briefest of introduction in most "Learn C" tutorials and books, but I don't understand how they work or how to use them.
- gnulinux 6y agoThere is no magic in .h files, you can think them as plaintext. The actual magic happens in .c files. Since C requires objects to be declared before using them, you need to declare objects used in multiple units together. Let's say you have a function `int f(int)`. You want to use this function both in apple.c and pear.c. How would you do it. Well you need to write a f.h such as int f(int); then in apple.c #include "f.h" int apple() { int x = f(10); // rest of the code } You can think this as a copy paste of f.h such that, apple.c is equivalent to: int f(int); int apple() { int x = f(10); // rest of the code } but what "is" f. To "define" f you need another .cpp file that implements f, let's call it f.c: #include "f.h" int f(int x) { return x*x; } now you compile f.c, apple.c, pear.c. These will create f.o, apple.o and pear.o object files. Then you link these together to create your application. e.g. if f.h is in include/ dir: gcc -c apple.c -I include/ -o apple.o gcc main.c -I include/ apple.o pear.o f.o -o main Here, assume that main.c defines int main().
- umanwizard 6y agoImagine you have a function f in f.c: void f() { puts("hello, world!"); } and you want to call it in main.c: int main() { f(); } this is an error because the compiler treats each .c file separately, so when it’s compiling main.c, it isn’t looking at f.c, so it has no way to know the function f exists. So you need to “declare” it at the top of main.c. A declaration is similar to a definition, except it has no code, and just tells the compiler that the function exists: void f(); But now imagine f.c had many functions. It would be extremely cumbersome to repeat all those declarations in every file that wants to use them. So you can collect all the declarations in some file f.h, and include it from main.c or anywhere else you want to use functions from f.c. When the compiler sees #include "f.h", it operates as if the contents of f.h were copied and pasted at that point. You might ask “why not just include f.c from main.c instead?” Because then you would have two _definitions_ of f in your program (one in f.c and one in main.c), which is illegal, as opposed to two declarations, which is fine.
- billpg 6y agoIs adding "(2019)" really necessary? Are the contents no longer valid or only meaningful when set in its historical context?
- tomhoward 6y agoIt's long been the convention here, and it's useful for people to know how recently it's been published. With an article title like this, it could easily be anything from 1 to 30 or more years old, and showing the date makes it easier for readers to know if they've already seen it before clicking through.
- augustk 6y agoI learned pointers by drawing boxes and arrows which I think is the best high-level explanation. Basically a variable is a container which holds a value from a predetermined set of values (also known as a type). For instance, an integer variable x holding the value 37 can be visualized as a box with the name x: x +--+ |37| +--+ A pointer variable p can be visualized as a box which "contains" an arrow pointing to another variable of a certain type, for instance p x +--+ +--+ | -|--->|37| +--+ +--+ which correspond to the C declarations int x = 37; int *p = &x;
- bear8642 6y agoAnd that's the way they're shown in K&R2
- prosaic-hacker 6y agoNot a simple general intro to pointers A useful gentle intro to the C type feature of pointers to arrays. The C language has more than one way of accessing arrays. Other languages don't. In the classic 80s teaching language Pascal if you started with pointers it was because you where working with linked lists. Most people I have taught programming to conflated the pointer access in C and the linked list concepts to their disadvantage. I taught linked lists first and left the point abstraction intact. Later I crossed the the layer of abstraction to explain memory access details. I have had more success (less learning difficulties) this way.
- jpm48 6y agoTo quote K & R "In C, there is a strong relationship between pointers and arrays, strong enough that pointers and arrays should be discussed simultaneously" ... however they also say "Pointers have been lumped with the goto statement as a marvelous way to create impossible to understand programs". I use both these quotes in my lectures.
- pjmlp 6y ago> The C language has more than one way of accessing arrays. Other languages don't. Like this? procedure ArrayDemo; var data : array [1..20] of Integer; ptr : ^Integer; i : Integer; begin ptr := @data[low(data)]; for i:=low(data) to high(data) do begin WriteLn(ptr^); ptr := ptr + Sizeof(Integer) end end;
- rramadass 6y agoThis article and most of the comments here are just over complicating a straightforward idea. I simply don't get why people always think "pointers" are difficult to understand; it is manipulating them to do advanced stuff that gets tricky. But nevertheless it gets easier with time and experience since you start picking up on the common idioms. For anybody beginning C, my advice is to not make an "imaginary dragon" out of "pointers" but just study examples and write your own programs using them and soon they will seem "obvious".
- globular-toast 6y agoI agree. I really can't understand what is complicated about them. I find the same with programmers who find the DAG in git difficult to grasp. It's just linking stuff together. It's how the the web works. It's how linked lists work. What's difficult about it. I think exposure to assembly demystifies all of this stuff about C. Most of C is just making stuff really convenient that you would do anyway in assembly. A pointer is just a memory address, but in C it also has a type. The only thing the type is for is so the compiler knows how many bytes to add/subtract when you increment/decrement the pointer. That's it. In assembly you'd just have to remember how many bytes to increment/decrement yourself. Reading Knuth also helps with this. It's one of the reasons he uses a synthetic assembly language rather than a high-level language. There are no pointers. There is just memory, and you need some way to find stuff in memory, otherwise it is just noise.
- dnautics 6y agoIMO I don't think the problem in C is pointers, it's that pointers are arrays, in the sense that they are syntactic sugar. This is incredibly confusing, and probably part of the reason why most modern systems languages wisely make arrays more e.g. {pointer, length}, sometimes introduce slices, where length is runtime, and make you do some syntactic work to pull out the pointer from the array.
- kevin_thibedeau 6y agoPointers aren't arrays because sizeof works differently and pointer arithmetic on a pointer-to-array doesn't work "right". The more confusing part is the variety of ways pointers get used: allocated memory, opaque object handles, extra return values, modifiable input values, array indexing, function pointers, etc.
- zokier 6y agoI recommend anyone saying that pointers are simple, just integer memory locations, to read this article: https://www.ralfj.de/blog/2018/07/24/pointers-and-bytes.html https://www.ralfj.de/blog/2018/07/24/pointers-and-bytes.html The fact that in C pointers point to specifically objects and not just simply to a random place in memory makes all the difference and is source of oh so many bugs
- easterncalculus 6y agoSeeing why pointers are used is especially important. Understanding that functions in C pass by value and what that means gives context to pointers as a useful tool instead of a longer and more complicated way to do things. Students become a lot more frustrated if they don't see a reason behind learning something, especially if there's any sense of elitism going on. Additionally, a lot of these terms like that pointers now introduce (address, reference, etc) are being introduced without any sort of context, usually they're brand new to students. If you've never seen (and may never see) assembly then you miss out on a lot of what's going on at the beginning and eventually when you get it, as well. It's an abstracted view of what's going on.
- dceddia 6y agoI remember pointers being very hard to grok when I was first learning about them. And then once I was finally starting to understand, I saw a char and my head exploded. It took some time & practice for it to become second nature, but eventually it did. It's interesting how most languages have this concept, even if they don't call it "pointers". In some ways that reduces complexity and learning curve, but in other ways it leaves invisible footguns lying around. JavaScript comes to mind as one where references and referential equality are very important to understand, especially in modern frontend dev with its focus on immutability, but there's no syntax like C's `*` and `&` to lend clues about what's going on. I ended up writing a visual guide to "pointers for JS devs" [0] to help explain the concept, because I think it's really easy to get bitten by reference bugs when your mental model doesn't match what's happening. [0]: https://daveceddia.com/javascript-references/ https://daveceddia.com/javascript-references/
- wolfi1 6y agoTed Jensen's tutorial helped me a lot back in the day: https://pdos.csail.mit.edu/6.828/2014/readings/pointers.pdf https://pdos.csail.mit.edu/6.828/2014/readings/pointers.pdf
- DougBTX 6y agoComing back to C after several years, I now think one of the hard things about pointers in C is the C syntax for pointers, not just the idea of pointers in general. Eg, sometimes the type declaration wraps around both sides of the variable name: int a[2]; int (*ap)[2] = &a; // pointer to array of int It makes it harder to see what’s what.
- kahlonel 6y agoI agree that it is confusing. Here [1] is something that can help new C programmers to understand these kind of declarations. It gets easier after a while. [1]. http://c-faq.com/decl/spiral.anderson.html http://c-faq.com/decl/spiral.anderson.html
- zzo38computer 6y agoYes, I think the confusing syntax of types is one of the actually bad things in the C programming language.
- vector_spaces 6y agoWhat I find most annoying about tutorials on pointers (as someone relatively new to C picking it up for work and fun) and memory management is that every single one of them seems to discuss the trivial case of a hard coded char or int array. Which like, is great for the absolute basics, but not for just about every serious piece of software where in practice what you need is to build stuff like e.g. dynamic accumulators of strings -- and virtually no tutorials or books I've read talk through best practices for doing this. So I've sort of kludged together my own probably wrong set of patterns for this. Reading the source code of others on github helps a bit but I have yet to find a good example of how to e.g. read strings from say a CSV file and parse it into an array of arrays of strings Does anyone have any pointers here? (sorry)
- rhn_mk1 6y agoPerhaps that's because C pointers are usually (always?) exactly that - a hard coded char array, which starts at memory offset 0. Depending on the circumstances, it may be easier to think about it as int array, or some other type, but the easiest on-ramp for pointers is still char[] = 0x0.
- clarry 6y ago> how to e.g. read strings from say a CSV file and parse it into an array of arrays of strings > > Does anyone have any pointers here? (sorry) I think this kind of thing isn't discussed too often because there are so many different choices that you can make (and once you understand the language & your constraints, you can certainly make those choices). I agree it would be useful to hilight all the possibilities. Let me try to illuminate: First, do you really need an array of arrays of strings? That's possible, but not all that common. If you don't really need such a thing, then you do something like strtok or strsep: implement a function that you call in a loop to extract fields one by one. Now you just need a single buffer large enough to hold a single field (or one row if you prefer to make it a little simpler). Your function locates the terminating comma, replaces it with a NUL, and returns a pointer to the start of the buffer (and stores the start of the next field -- that is, the address of the byte after the NUL -- somewhere for the next call). This approach is popular among C programmers because it usually means you don't need dynamic allocation, as long as there's a reasonable maximum field size. Or if a maximum field size cannot be imposed, then you get away with dynamic allocation (and re-allocation, when needed to grow) of a single buffer. What next? Ok, maybe you really do require "all fields at once." Your average C programmer still won't allocate arrays of arrays of strings. Instead, they read the entire row, extract fields out of it (as above), and store the pointers to the start of each field somewhere. If the caller knows how many fields they want to deal with (very often you do!), then they can provide you a fixed length array for these pointers. Otherwise, you can dynamically allocate a buffer to hold the pointers. Either way, now you need two buffers (and depending on your needs, one or both or neither may be statically sized): one for the row, one for the pointers. Now the buffer containing pointers is exactly like argv in main(), while the fields are in one contiguous buffer spliced by NUL bytes. Ok, maybe you really do require all rows and all fields at once. At this point C programmers will hate you because you're forcing them into dynamic allocation (or an unreasonable fixed size limit on the file). Otherwise, you do as above, but you also terminate lines with NULs and now you have some choices as to how to lay out your row + field pointers. For example, you could allocate one array for row pointers, which point to field pointers (which could be allocated separately for each row, or, perhaps preferraby, in a single flat array containing pointers to all the fields). If you expect the caller to iterate (rather than random-access) rows, you could use a single mixed type array containing row metadata + field pointers. Another very common approach would be to allocate field pointers + data for each row separately and then link rows in a linked list. Again this favors iteration but you're less likely to need to resize huge arrays. Sometimes you deal with memory that is best to keep immutable; in that case, splitting the data into rows and fields with NUL bytes is not an option and you must take a different approach. Either duplicate the data (more mallocs, required if you also need to do things like unescaping) or keep it where it is and store pointer + length tuples. See, there are lots of choices, and lots of variables, depending on how much memory you want to use, can you use fixed size buffers, what kind of access pattern the caller expects, does the caller expect to be able to free individual rows, etcetra. There is no best practice but generally C programmers gravitate towards using iteration & fixed size buffers unless more is required (this is simple and lets the caller persist data if they want to, but doesn't force them to deal with dynamic memory if they don't want or need to). If something more specific is required, then you'll know what is required and implement something that supports just that. The Python style divide-and-conquer where you first read an entire file, then chop it into lines, then chop lines into columns, etc. is not very popular.
- stereoisomer 6y agoI think people get scared of pointers because the numbers are big and are in hexadecimal. But they don't have to be. I didn't understand pointers until I thought of them as house numbers in postal addresses. In a world where everyone lives on the same street, you can send anyone a letter by mailing a letter with their house number written on the envelope. You don't even need to know their name -- thus SPAM mail is born. The same analogy holds for phone numbers. The pointer is like a phone number or house number. The person on the other end is the value you get when you dereference a pointer.