15

I'm programming in C++ right now, and I love using pointers. But it seems that other, newer languages like Java, C#, and Python don't allow you to explicitly declare pointers. In other words, you can't write both int x and int * y, and have x be a value while y is a pointer, in any of those languages. What is the reasoning behind this?

TylerH
  • 20,799
  • 66
  • 75
  • 101
wrongusername
  • 18,564
  • 40
  • 130
  • 214
  • 1
    most people would say that they can be dangerous, because of the ability to mess up and create seg faults and or memory leaks. – Jim Nov 02 '10 at 04:37
  • 7
    What are you doing with pointers that you love? – GManNickG Nov 02 '10 at 04:47
  • @GMan--I think my favorite thing is allowing a class `A` to contain pointers to another class `B`, so that if an object of `B` is actually a class `C` inherited from `B`, then `A` can call `C`'s functions instead of `B`'s virtual functions. I guess other languages might be implemented differently to avoid that need for pointers though. Also, you can make two classes include pointers to each other so that both can access each's functions, but you can't do that with values because it would create an infinite loop with class `A` containing class `B` containing class `A`... – wrongusername Nov 02 '10 at 05:00
  • Well pointers aren't bad by themselves.. they have been *used* badly.. that's all :) – liaK Nov 02 '10 at 05:10
  • 4
    Umm, [false premise](http://msdn.microsoft.com/en-us/library/y31yhkeb.aspx). – Ben Voigt Nov 02 '10 at 05:57
  • think like this: These other languages do all object allocation on the heap (instead of the stack) and then you will see that everything is a pointer, just one you don't need to free() and can't use for point arithmetics (they are usually called references when talking about a GC language). Primitives are still on the stack though. C++ allows you to store objects in the stack as well as in the heap. – Hoffmann May 27 '14 at 19:55

7 Answers7

20

Pointers aren't bad, they are just easy to get wrong. In newer languages they have found ways of doing the same things, but with less risk of shooting yourself in the foot.

There is nothing wrong with pointers though. Go ahead and love them.

Toward your example, why would you want both x and y pointing to the same memory? Why not just always call it x?

One more point, pointers mean that you have to manage the memory lifetime yourself. Newer languages prefer to use garbage collection to manage the memory and allowing for pointers would make that task quite difficult.

Steve Rowe
  • 19,411
  • 9
  • 51
  • 82
  • Well, I don't mean that `x` and `y` should point to the same memory address forever, just that you can't declare `x` as storing a value and `y` as storing a memory address. I dunno though, I prefer to be able to always explicitly choose between declaring a variable with a value or a reference. – wrongusername Nov 02 '10 at 04:42
  • 14
    Of course, nothing good comes for free. C++ pointers are powerful and dangerous, and in some of the other languages, while you can't shoot yourself in the foot, you have a hard time aiming down at all. There are things to be learned from how higher level languages handle pointers that can be applied to C++. – ssube Nov 02 '10 at 04:43
  • 13
    "pointers mean that you have to manage the memory lifetime yourself" This is one of the most widespread and frustrating misconceptions about C++. You **can** manage dynamically allocated object lifetimes manually, but you don't **have** to (and you definitely shouldn't, most of the time). Facilities like smart pointers and containers can be used to ensure automatic lifetime management. Considering only lifetime management, this approach is actually better than garbage collection, because the lifetimes are also deterministic. – James McNellis Nov 02 '10 at 06:06
  • @James: +1, but both have advantages. Reference counting has a miserable time handling cycles. And it's possible to provide language support for deterministic lifetime even in environments that also use garbage collection. – Ben Voigt Nov 02 '10 at 06:09
  • @James, smart pointers are still "you" managing the memory. "You" are delegating it to code but ultimately it's still someone on the developer's end who decides how to handle it. It isn't automatic. It takes discipline to get it right. – Steve Rowe Nov 02 '10 at 07:50
  • 3
    @Steve: So then when you use GC, it's still *you* managing memory since *you* are the one making sure you used a GC language? – GManNickG Nov 02 '10 at 09:16
  • 5
    @GMan: even in a GC language, to get rid of an subobject, you'll have to set the pointer in the parent to null. There are plenty of frameworks (especially GUI frameworks) that are prone to leaks for exactly this reason. Somewhere with the framework, one pointer survives, and an entire set of interconnected objects is leaked. – MSalters Nov 02 '10 at 09:37
  • @FredOverflow: Weak pointers are not without problems of their own. Often there needs to be some distinction between internal references and external users. – Ben Voigt Nov 02 '10 at 17:57
  • @GMan, no. Not usually. In a language like Python or C#, you can't manage the lifetime. You have no choice. You can't make a mistake. With smart pointers you have to use them explicitly every time. – Steve Rowe Nov 03 '10 at 01:30
  • @Steve: Circular references are possible, aren't they? – GManNickG Nov 03 '10 at 03:06
  • @GMan: Yes, but most GC systems will notice and still sweep them. – Steve Rowe Nov 03 '10 at 04:33
13

I'll start with one of my favorite Scott Meyers quotes:

When I give talks on exception handling, I teach people two things:

  • POINTERS ARE YOUR ENEMIES, because they lead to the kinds of problems that auto_ptr is designed to eliminate.

  • POINTERS ARE YOUR FRIENDS, because operations on pointers can't throw.

Then I tell them to have a nice day :-)


The point is that pointers are extremely useful and it's certainly necessary to understand them when programming in C++. You can't understand the C++ memory model without understanding pointers. When you are implementing a resource-owning class (like a smart pointer, for example), you need to use pointers, and you can take advantage of their no-throw guarantee to write exception-safe resource owning classes.

However, in well-written C++ application code, you should never have to work with raw pointers. Never. You should always use some layer of abstraction instead of working directly with pointers:

  • Use references instead of pointers wherever possible. References cannot be null and they make code easier to understand, easier to write, and easier to code review.

  • Use smart pointers to manage any pointers that you do use. Smart pointers like shared_ptr, auto_ptr, and unique_ptr help to ensure that you don't leak resources or free resources prematurely.

  • Use containers like those found in the standard library for storing collections of objects instead of allocating arrays yourself. By using containers like vector and map, you can ensure that your code is exception safe (meaning that even when an exception is thrown, you won't leak resources).

  • Use iterators when working with containers. It's far easier to use iterators correctly than it is to use pointers correctly, and many library implementations provide debug support for helping you to find where you are using them incorrectly.

  • When you are working with legacy or third-party APIs and you absolutely must use raw pointers, write a class to encapsulate usage of that API.

C++ has automatic resource management in the form of Scope-Bound Resource Management (SBRM, also called Resource Acquisition is Initialization, or RAII). Use it. If you aren't using it, you're doing it wrong.

Community
  • 1
  • 1
James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • 1
    References can be 'null', for example when you make a reference to a dereferenced invalid pointer. See: http://codepad.org/JEPQpv2v – Paul Nov 02 '10 at 05:58
  • 8
    @Paul: The behavior of your code snippet is undefined. "A null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by dereferencing a null pointer, which causes undefined behavior" (C++03 8.3.2/4). – James McNellis Nov 02 '10 at 06:00
  • 1
    @James McNellis: Accessing a null pointer is also undefined behavior, but that's beside the point. The point was, is that it is completely possible to have an 'invalid' reference (i.e. 'null'/black hole of undefined behavior/etc). It may be reaching a little bit, but it is definitely fun to debug when encountered... – Paul Nov 02 '10 at 06:10
  • 1
    @Paul: Well, there are a lot of things that result in undefined behavior. When you write a function that takes a reference (like `f()` in your example), you can rightly assume that the reference will be valid because if it isn't, your caller has already screwed up the program. Yes, you can have bugs with respect to this issue, but usually they are far and few between (and in my experience, they are often obvious enough that they get caught in code reviews). – James McNellis Nov 02 '10 at 06:35
  • [In other news, while I think this answers the question in the title, I'm not so sure that this is exactly what the OP was looking for. Hopefully it is still useful.] – James McNellis Nov 02 '10 at 06:35
  • 4
    @Paul: Wrong. How can you, within the C++, conclude that a reference is invalid? You cannot, because in a well-defined program you can *never* have a bad reference. You defeat yourself when you say it's possible, because you can no longer reliably *observe* the state of your program, which is required to conclude a reference is invalid. (In other words, the problem lies with the guy who invoked UB, not with references. References cannot be null; UB can be entered.) – GManNickG Nov 02 '10 at 08:04
  • @GMan: When in the realm of undefined behavior, anything can happen. Specifically, *null references* can happen. When you restrict yourself to well-defined programs, you defeat the purpose, because the program **as a whole** is either well-defined or exhibits UB. If I write a function that accepts a pointer and dereferences it, you cannot pass me an invalid pointer. Well you can, but then the program is no longer well-defined, and we're excluding those, remember. – Ben Voigt Dec 10 '10 at 20:29
  • The situation is identical for pointers which are dereferenced without a null check and references... in both cases the caller must pass a valid address in order to avoid UB. In summary "References can be null, but only in a program which is UB. Pointers can be null when dereferenced, but again only in a program which is UB." – Ben Voigt Dec 10 '10 at 20:29
  • @BenVoigt Right, since anything can happen unicorns can come into existence too in such a program. Language spec says null references can't happen. That's defined behavior. Undefined behavior with dereferencing a null pointer just implies that you no longer can know that the reference would in fact reference. Language ensures though that it won't be NULL. – iheanyi Aug 25 '14 at 14:58
6

Pointers can be abused, and managed languages prefer to protect you from potential pitfalls. However, pointers are certainly not bad - they're an integral feature of the C and C++ languages, and writing C/C++ code without them is both tricky and cumbersome.

EboMike
  • 76,846
  • 14
  • 164
  • 167
4

A true "pointer" has two characteristics.

  • It holds the address of another object (or primitive)
    • and exposes the numeric nature of that address so you can do arithmetic.

Typically the arithmetic operations defined for pointers are:

  1. Adding an integer to a pointer into an array, which returns the address of another element.
  2. Subtracting two pointers into the same array, which returns the number of elements in-between (inclusive of one end).
  3. Comparing two pointers into the same array, which indicates which element is closer to the head of the array.

Managed languages generally lead you down the road of "references" instead of pointers. A reference also holds the address of another object (or primitive), but arithmetic is disallowed.

Among other things, this means you can't use pointer arithmetic to walk off the end of an array and treat some other data using the wrong type. The other way of forming an invalid pointer is taken care of in such environments by using garbage collection.

Together this ensures type-safety, but at a terrible loss of generality.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • I don't see how you lose generality. Not having pointers just forces you to call a spade a spade: if you want an array of items, you declare it as an array of items and use integer indices, which *can* be numerically manipulated. (Note that in C and C++, pointer arithmetic is formally allowed ONLY between pointers that point into the same memory block, i.e., array). – zvrba Nov 02 '10 at 10:09
  • @zvrba: The fact that managed languages don't let you form a reference to a subobject is a HUGE loss of generality. Not being able to type-pun to char* is a HUGE loss of generality. Also, my answer stresses that both pointers need to be into the same block. Nonetheless, pointers to different objects have an undefined but **stable** ordering, in managed environments this guarantee is no longer made, the garbage collector is permitted not only to move objects but to change their order. – Ben Voigt Nov 02 '10 at 13:11
  • What do you mean by "loss of generality"? And unsafe casts (e.g., to char*) are not an inherent property of pointers, it's just a C feature. – zvrba Nov 02 '10 at 13:39
  • 1
    @zvrba: As I said, pointers expose the numeric nature of the underlying address. The ability to turn a pointer into an integer and an integer into a pointer is quite sufficient to setup type-punning (what you called an unsafe cast, but many uses of type-punning are actually perfectly safe). – Ben Voigt Nov 02 '10 at 15:10
  • Uh, C runs also on machines with non-linear memory models. There, casting to integer and back doesn't have much sense. Pointer is an object that can be used to reach another object. That is common across all languages having the pointer datatype (e.g. Pascal, Ada). All other properties you assign to pointers come from your own personal, arbitrary definition. – zvrba Nov 02 '10 at 17:14
  • @zvrba: I haven't used Ada enough to remember, but I don't think Ada calls its indirect memory access a "pointer" (maybe "access type" like VHDL?). They are actually much more similar to "object references" in managed languages. As my answer says, it's useful to divide indirect memory access methods between those that just "can be used to reach another object" and those that expose the numeric address, the first usually being called a reference and the second a pointer. And non-linear memory models aren't non-numeric, e.g. addresses in segmented memory can be expressed as numbers just fine. – Ben Voigt Nov 02 '10 at 17:53
  • But that's just it: addresses need not be numeric -- in segmented memory models, it's meaningless to do *any* operation over two pointers into different segments. (Maybe you have different understanding of "numeric".) – zvrba Nov 03 '10 at 07:41
3

I try to answer OP's question directly:

In other words, you can't write both int x and int * y, and have x be a value while y is a pointer, in any of those languages. What is the reasoning behind this?

The reason behind this is the managed memory model in these languages. In C# (or Python, or Java,...) the lifetime of resources and thus usage of memory is managed automatically by the underlying runtime, or by it's garbage collector, to be precise. Briefly: The application has no control over the location of a resource in memory. It is not specified - and is even not guaranteed to stay constant during the lifetime of a resource. Hence, the notion of pointer as 'a location of something in virtual or physical memory' is completely irrelevant.

Paul Michalik
  • 4,331
  • 16
  • 18
1

As someone already mentioned, pointers can, and actually, will go wrong if you have a massive application. This is one of the reasons we sometimes see Windows having problems due to NULL pointers created! I personally don't like pointers because it causes terrible memory leak and no matter how good you manage your memory, it will eventually hunt you down in some ways. I experienced this a lot with OpenCV when working around image processing applications. Having lots of pointers floating around, putting them in a list and then retrieving them later caused problems for me. But again, there are good sides of using pointers and it is often a good way to tune up your code. It all depends on what you are doing, what specs you have to meet, etc.

ha9u63a7
  • 6,233
  • 16
  • 73
  • 108
1

Pointers aren't bad. Pointers are difficult. The reason you can't have int x and int * y in Java, C#, etc., is that such languages want to put you away from solving coding problems (which are eventually subtle mistakes of yours), and they want to bring you closer to producing solutions to your project. They want to give you productivity.

As I said before, pointers aren't bad, they're just diffucult. In a Hello World programs pointers seems to be a piece of cake. Nevertheless when the program start growing, the complexity of managing pointers, passing the correct values, counting the objects, deleting the pointers, etc. start to get complex.

Now, from the point of view of the programmer (the user of the language, in this case you) this would lead to another problem that will become evident over the time: the longer it takes you don't read the code, the more difficult it will become to understand it again (i.e. projects from past years, months or event days). Added to this the fact that sometimes pointers use more than one level of indirection (TheClass ** ptr).

Last but not least, there are topics when the pointers application is very useful. As I mention before, they aren't bad. In Algorithms field they are very useful because in mathemical context you can refer to a specific value using a simple pointer (i.e. int *) and change is value without creating another object, and ironically it becomes easier to implement the algorithms with the use of pointers rather than without it.

Reminder: Each time when you ask why pointers or another thing is bad or is good, instead try to think in the historical context around when such topic or technology emerged and the problem that they tried to solve. In this case, pointers where needed to access to the memory of the PDP computers at Bell laboratories.

PlainOldProgrammer
  • 2,725
  • 5
  • 22
  • 30