1. Welcome to the new MacRumors forums. See our announcement and read our FAQ

Pointers in C... can anyone help me understand?

Discussion in 'Mac Programming' started by ravenvii, Feb 3, 2013.

  1. macrumors 604


    So I don't understand pointers in C.

    Why use them instead of the variables themselves?

    I understand how to create/use pointers, but don't understand what the point is? From Googling, it seems pointers are crucial to linked lists and other data structures... but why? Couldn't the variables themselves simply be used instead of something pointing to variables?

    Or another scenario that I might find easier to understand: if I wrote a function, say, determining if a word is a palindrome -- you pass an char array (a string) to it, and it'll return whether the word is a palindrome or not -- should I be passing a variable to the function, or should I be passing a pointer? Why? What's the difference?

    Many thanks for your time!
  2. LPZ
    macrumors 65816

    Well, this will likely make things worse. But when you pass the name of an array to a function in C, you are actually passing a pointer to the array, not the values in the array. In contrast, if you pass the name of an int, then you are actually passing the value of the integer variable with that name.
  3. chown33, Feb 3, 2013
    Last edited: Feb 3, 2013

    macrumors 603

    C FAQs (good info on pointers vs. arrays, Section 6):

    Pointer tutorials in C:

    If you think you can write linked-list code without pointers, I suggest trying it. A simple singly-linked list of ints in a struct should be enough to illustrate the point. Example struct WITH pointer:
    struct linked_int {
      struct linked_int * next;
      int theIntValue;
    First, consider how you'd write a function that adds one int to the head of a linked-list using pointer.

    Then consider how you'd change the struct to not use a pointer. You can't just remove the *, because a struct can't contain itself. So exactly how would you link structs together?

    There is a way to use an array of structs to make an index-linked list. It's pretty rare in the real world, because of its limitations. Index-linking gains you nothing in C, because it only works with arrays of fixed length.

    One of the reasons for structures like linked-lists (or their many relatives, such as trees, graphs, etc.) is they can be of arbitrary length. You don't have to know the max length up front, they can just grow as much as free memory allows. And if they overflow to disk, or you use a linked struct on disk (look up B-Trees), they can grow as large as whatever the storage medium is.
  4. Moderator


    Staff Member

    In the broadest of strokes.

    You use a pointer when it isn't the value of the variable that matters, but it's location in memory and vice-versa.

    In the case of the palindrome or linked-list you want to take a bunch of values that exist in memory and (potentially) re-organize them. For this, using pointers lead to far more effective code.

    Another key reason in C is when you want to modify the contents of memory. i.e. you use scanf to fill a buffer, you have to give scanf the location of the memory it's supposed to use....

  5. macrumors regular

    Wow. This thread just took me back 15 years. :)
  6. macrumors 604


    Actually, that makes sense.

    But, just out of curiosity, if I pass, say, strArray[2] to a function, is that a pointer, or the value stored?

    I didn't realize struct relates to data structures in C (haven't gotten there yet, my only experience with data structures is via Java classes). I should've known -- C isn't a OOP language :) -- which means you couldn't have "Node" objects that contains an object and another Node.

    So you'll have to have a structure that contains something and something-that-points-to-the-next-something.


    Thanks guys!
  7. macrumors 603

    It depends on how strArray is declared. Show the declaration and we can tell what strArray[2] means. Without knowing the type of the array, one can't determine the type of the array's elements. This applies for all arrays.

    An array can hold pointers as elements. That is, one can have an array of pointers. In such a case, strArray[2] is a pointer.

    A common example of an array of pointers to strings is the argv parameter passed to main(). Maybe you haven't done anything with those parameters yet. If you have, look at how main()'s args are declared.
  8. macrumors 603

    A pointer is just an address. Like the street address to you mom's house, you can write it down in your address book. But your mom's house itself is likely far to big and heavy (and not zoned for) to try to stuff into your pocket (unless you have aircraft hangers for pockets).
  9. macrumors 68040


    Java hides the pointers, which may be leading to some of the difficulty here. There are not stack-local objects in Java. Any time you have a variable whose type is an object, it's a pointer to the object that's being stored. new Object() will give you a pointer to the Object that you store in your variable. When you pass an object to a function, it's always passing the pointer. This is all hidden in Java, so going from Java to a language with programmer-visible pointers can be a little confusing. I think this is evident in your confusion about why you can't just "use a variable", because that's what you do in Java. It happens that the variable is a pointer, but that's hidden from you. In Apple's Foundation code, which is C based, they use typedefs to conceal pointers in many cases, calling these refs. A way to think about Java is that you only have these refs, you never have a stack-local object.

    This may not help at all, but this realization was major for me long ago.

  10. macrumors regular

    Yet another way of looking at it.

    Think of C as a rather low-level language. It came from the times when programmers still was thinking about how computers actually do things. Other languages, say Java, is higher level. They try to hide how the computer actually does things by adding concepts like object oriented programming. There are even higher level types of languages, say Prolog or F#, where the how things are done are hidden even more. C was also designed in times when computers where slow and had small memorys so efficiently running programs where important.

    First, low-level or high-level languages are not really bad or good in themselves. Simply different tools, used for different things.

    In c, you simply have to learn what pointers are and how they work. Both on the left side of a an = sign and on the right. You need to understand that every variable has an adress, easily found by the & sign. Pointers are simply one of the basic foundations in c. If you ever want to program in c, you have to have them well under your belt. They crop up just about everywhere, and sometimes in confusing or surprising forms.

    In order to learn what and how pointers are, it is good to follow the curriculum of a well-though out course. One way of doing that is going to the actual source, the K&R c programming language book. It is available as free download I believe. It takes a bit of hard thinking to understand what l-values really are, but it places you on a firm foundation as far as the language goes. The book is slightly outdated as there are a few new concepts that has been added since it was written, but these are mainly relevant for quite advanced usages.

  11. ytk
    macrumors regular

    All of these answers do a good job of explaining what pointers are, but perhaps don't really explain WHY you need to use them.

    Here's why: C is only a single step up from assembly language. Granted, it's a big step, but you wouldn't be too far off if you thought of C as sort of a “meta-assembler”, as compared to a higher level language like Python, Ruby, or Java. In those languages, you generally don't need pointers, because the language takes care of managing memory and handling more complex data types such as strings for you. By comparison, C pretty much hands you a chunk of memory and says “Good luck!”

    There are some macros and functions built into the language to simplify this task, but by and large you're responsible for handling memory yourself. The first simplification, and the one you're already familiar with, are variables. When you declare a variable, you're essentially delegating management of that tiny chunk of memory to the C compiler. You're saying, “Whenever I talk about the variable ‘foo’, figure out what part of memory you assigned to that variable and grab whatever is in it—I don't care how you do it.” The C compiler responds by allocating this memory on what is called the “stack”, based on the type of variable and how long you need it for (that is what is called the “scope” of the variable). The stack is nice because it is handled automatically, but there are two problems with it: You need to know in advance how much memory you plan to use, and there isn't that much stack memory available.

    And then there's the “heap”. You know all of that RAM you have installed in your computer? That's the heap. In the old days, before protected memory, this was literally true, but modern operating systems actually abstract it away from the C compiler and give each process its own virtual heap, so to speak, in order to prevent different programs from trampling all over each other (this was a very common problem under MacOS 9 and below, which is why a single program crashing would often bring down the system).

    The heap is good for storing large chunks of data, but you can't declare how much heap you need in advance of running the program. Instead, you request heap memory at runtime by using the malloc() statement, which is so primitive that it doesn't even know or care what type of data you're requesting. Hence, you will see statements like “ptr = malloc(500 * sizeof(int) )”, which requests enough memory to store 500 integers, however large an integer happens to be. How do you reference this memory? The malloc statement returns a pointer to it. Where is this pointer stored? Why, in a stack variable! So you can think about it as using a stack variable, which is your most basic, primitive sort of variable, to store an address to your heap memory. That way, you can decide how much heap memory you need at runtime (and even change how much you need dynamically), but still reference it the same way as if you were using a stack variable.

    Okay, so hopefully that's clear, but then if you're not using malloc() to request heap memory, why bother using pointers at all? The answer goes back to the fact that C is such a low-level language. In higher level languages, the entire process described above is taken care of for you, and you can simply throw around more complex data types like strings and arrays as if they were plain old variables. Not so in C. In C, you can only pass a few variables at a time to a function, and each variable can only contain a single number. So what do you do if you need to operate on an array, or a string (both of which are technically the same thing—a list of numbers)? Well, you pass a pointer to the beginning of the list, and tell the program that instead of interpreting the number being passed as a literal number, interpret it as a memory address. The function receiving the pointer can then go to that memory address (called “dereferencing” the pointer), and find the data it's supposed to be working on.

    Really, a pointer is just a number, because every variable in C is just a number. The only difference is how that number is interpreted. It's entirely possible (if generally inadvisable) to interpret a pointer as an integer, and print the literal value of the pointer (as opposed to the value it points to), perform math on it, etc. It's likewise possible to dereference a variable declared as a plain old integer, and grab whatever piece of memory happens to be at that location. C is extremely permissive in this regard, and doesn't especially care what type of data is in what variable. Generally, the type is inferred from context, and the compiler will usually let you mix data types with little more than a warning (although doing so is almost always the result of a mistake, and you can avoid the warning by explicitly telling the compiler that you know what you're doing by “casting” the variable to a different type).

    To sum up, the real reason you need to use a pointer in C is if you want to operate on any data stored in a range of memory, like an array or a string. C can only work on a single number at a time, so it doesn't really have any concept of ranges of memory. This is why trying to copy a string from one variable to another using the = operator doesn't work in C, for example.
  12. macrumors 68020


    Same here, but I was also confused by pointers :(
  13. macrumors 6502a

    Passing structs to functions are another important use of pointers in C. Let's say you create a struct and want to pass the struct to a function. It's generally the best idea to pass a pointer to the struct instead of the struct itself.

    typedef struct _s
        char name[32];
        int i;
        int a;
    } Str;
    void func_with_ptr(Str *s)
       int c = s->a;
         (do stuff)
    void func_with_str(Str s)
       int c = s.a;
    void main()
        Str tt = {"aaa", 1, 2};
    Function parameters are always by-value. When calling func_with_ptr(), the compiler puts a pointer value on the stack and then calls the func_with_ptr() function. But when you call func_with_str(), the compiler has to reserve enough space on the stack to hold the entire structure. Then it copies all of the "tt" struct onto the stack, then calls func_with_str with a copy of the structure.

    You've added the overhead of extra stack space to make a copy of the tt structure on the stack. And you've added the extra overhead of the compiler needing to make a copy of the structure to pass to the function.

    This is a small example, but if the functions need to pass the same structure to other functions or if you increase the size of the structure you'll see that passing without a pointer can rapid increase the memory requirements and slow down your program.

    Passing structs with pointers is so common that C has a special syntax for accessing struct members when you have a pointer. You use "s->a" instead of "(*s).a".
  14. macrumors 6502a

    I just want to add one thing. In C, an array is a list of pointers.
  15. macrumors newbie

    Mastering C Pointers

    There is an old book (1990) called Mastering C pointers (ISBM 012697408X) that made it all click for me. I struggled with pointers until a friend turned me onto the book. Or, you could switch to Java...
  16. macrumors 68040


    ... and have pointers all around that you're not aware of and don't understand.

    No. Often arrays are accessed using a pointer to the base of the array. The only time an array is a list of pointers is when you have an array of pointers. Otherwise you have the pointer to your base and offsets. The binary [] operator gives you a convenient way to take the base of an array and an offset, and get back the value stored at the memory address represented. a[4] is the same as *(a+4).

  17. macrumors regular

    Try machine/assembly language then. Makes C look like cake.
  18. macrumors 604


    Regarding passing arrays to functions vs. passing a pointer, are you sure about that? Because the program I wrote, I passed an array to a function and it works perfectly. I understand that if you want to pass an array to a function and have it MODIFIED, a pointer is must, otherwise the modifications gets killed as soon as the compiler exits the function. But in the case of an array as a constant, passing an array works perfectly fine.

    Snippets of my code:
    int is_palindrome(const char word[]);
    char input[20];
  19. macrumors 603

    All arrays passed to functions are passed as pointers.

    See Section 6 of the C FAQs, which I linked to earlier:
  20. Moderator


    Staff Member

    You're still passing the array as a location in memory, i.e. a pointer. Whether you allow that chunk of memory to be modified is something else.

  21. gnasher729, Feb 5, 2013
    Last edited: Feb 5, 2013

    macrumors G5


    Please don't. It's wrong. It's so wrong, it couldn't possibly be wronger.

    1. An array contains elements of identical type, stored in contiguous memory locations.

    2. If a function parameter is declared in a way that looks like an array declaration ("array of T") then the function parameter is actually a pointer to T.

    3. In many contexts, an array is automatically converted to a pointer to the first array elements. This doesn't happen when the array is the argument of the sizeof operator (so sizeof gives the size of the array, and not the size of a pointer), and when the array is the argument of the address "&" operator (so taking the address of an array gives a pointer to an array, not a pointer to a pointer).

    Rule 2: The parameter of the function is_palindrome _looks like_ an array, but it is in fact a pointer to const char.

    Rule 3: In the call is_palindrome (input), the array "input" is automatically converted to a pointer to the first element of the array. Which is handy, because that's a pointer to char, which can be passed to a function that accepts a pointer to const char.

    Believing that will just muddle your brain. Very often when someone doesn't understand something about pointers, the cause of the misunderstanding is that they were told that pointers are numbers. A pointer is a pointer, not a number. You can cast a pointer value to an integer value and vice versa, with sometimes interesting results, but they are not the same.
  22. macrumors 604


    So the word[] part of the prototype is just window dressing, like the const part?

    And if the argument is automatically converted to a pointer to the first element of the array, why does the 'array' work as expected in the function itself? i.e. I was able to access all elements of the array in the manner one would expect?
  23. macrumors regular

    Nop. They mean exactly what you say. The function is passed an array of characters, and the function promises to not modify the characters. If your function tries to modify the characters the compiler should flag an error. And inside the function the parameter will behave exactly like an array, with the addition that you have promised not to change the content of the array.

    The first part of the answer is that this is what the compiler is required to do with your code. If you pass a const char [] to a function, the code has to behave as if there is an array passed allowing you to index the array. Arrays is one special case which is passed "by reference", in contrast to simple types that are passed "by value" (hint, check this using google).

    The second part is that the argument is not really "automatically" converted into a pointer. It might be passed in just about any way. The automatic conversion, if any, is done when you access the elements of the array. This difference might be moot in some environments, but in a highly optimising compiler and in some environments, the difference may actually be quite large.

    A lot of programmers assume that what you write in C is exactly what the compiler outputs. This used to be case in old times, but not necessarily anymore. The whole idea of optimising is that you write code in a way, and the compiler may outguess you and create a different program but with exactly the same result. This might include inlining whole functions, meaning that instead of calling a function it might be inserted in the calling code. This may allow the compiler to do even more optimising, removing a lot of intermediate steps.

    ... code snippet
    int x2 (int x)
    { return x * x ; }

    int z = x2(5);

    ... end code snippet

    a smart compiler might remove the full function, changing this into
    int z = 25;

    Similar things may happen behind the scenes with references to arrays in function calls. Exactly what the compiler does is unimportant as long as the code "behaves" exactly as the specification says. Some compilers will pass the address of the first element, other compilers may do completely different things. The important point is that you as a programmer will not notice the difference in code behaviour.

    It is actually a very good idea to add that const word. It tells the compiler that you are not planning to modify the array content, no side effects are intended. A good compiler will check this for you and create an error if you violate this. A good tool for this checking is called lint. In a small program it might not matter, but a software project may contain code in thousands of source code files written by dozens of programmers over many years. Unintended side effects can make the program behave irrationally. Checking by lint is then a very good idea.

  24. ytk
    macrumors regular

    Respectfully, I disagree. I've explained pointers quite a few times over the years, and I've found that the clearest understanding comes once your realize that a pointer IS simply a number. The only difference is what you do with the number.

    Think of the computer's memory as a bank of lockers (for simplicity, let's say we only have an 8-bit data type, and an 8-bit address space). So each locker contains a single byte. If I tell you to go to locker X, and interpret the number there as a value, you simply open the locker and get the value. But if I tell you to go to locker X and interpret the number as a pointer to a value, you open that locker and interpret the number there as a location identifying another locker that has the value you really want.

    In memory, both an int and a pointer look exactly the same to the computer. There is no special “status bit” attributed to pointers that identifies them as being different from any other value in memory. The only thing that's different about them is the significance they hold to your program, and it's your program's (and the C compiler's) job to keep track of that significance, not the computer's. As far as the computer is concerned, a pointer is just a number. Trying to conceive of some magical difference between pointers and numbers is misleading and confusing.

    D'oh! The function is passed a pointer to an array. You can't pass an array in C.
  25. macrumors 604


    I thought I read somewhere that const is only there to reassure the user that the function doesn't modify what's passed to it, but doesn't actually do anything? Maybe that's just with older compilers?

    Anyway, thanks guys -- this has been very educational! :)

Share This Page