<p><a href="https://en.wikipedia.org/wiki/Pointer_(computer_programming)" rel="noreferrer">Pointers</a> are variables that store a memory address. They allow the programmer to directly deal with memory which can be used to pass variables by reference or even for performance reasons.</p>
<p>However, common programming advice includes not using pointers while coding for reasons such as dangling pointers, memory leaks, or just the mental overhead that comes from using pointers. I don't even have to mention the <a href="https://en.wikipedia.org/wiki/Null_pointer" rel="noreferrer">'Billion Dollar Mistake'</a> that comes from null pointers.</p>
<p>There are of course languages such as C and C++ that feature pointers. However, even newer languages such as Go and [unsafe] Rust as well as lower level assembly languages allow programmers to directly use pointers. In Java, all objects are of reference types, but the user can't access the memory addresses directly.</p>
<p>So my question basically is, what are the pros and cons of allowing direct access to pointers and memory addresses?</p>

<language-design><pointers>
What are the advantages of allowing users to directly handle memory addresses with pointers?
CC BY-SA 4.0
2023-06-01T10:28:07.117Z
Pointers in Go are more similar to nullable references. You can't do pointer arithmetic.
alephalpha
CC BY-SA 4.0
2023-06-01T14:32:12.677Z
Note that you still *can* access memory addresses of objects in Java (and other JVM languages) using `sun.misc.Unsafe`.
Seggan
CC BY-SA 4.0
2023-06-01T14:35:06.567Z
Even very high level languages like python do allow raw pointer accesses though you have to jump through a lot more hoops
mousetail 'he-him'
CC BY-SA 4.0
2024-11-28T20:09:17.157Z
Note that the first sentence in your question is a falsehood, and you should not reason about a thing by starting with a falsehood. Pointers are not variables that store an address. Pointers are *values* which *are* an address, and have a special operator "dereference" that *produces* a variable. A *variable* of *pointer type* is a variable which stores an address. This is not a hairsplitting distinction!
Eric Lippert
CC BY-SA 4.0
2024-12-06T10:36:36.043Z
@EricLippert, could you pad that line of thinking out a little more? If I understand correctly, you're refering to the distinction between fields (a place for storage) and the contents of those fields (the "value", or the data stored at that place). In computing, there is no reality of either one without the other. But if the word "pointer" refers to the content, and the word "field" refers to place, I struggle to understand why a deref of a pointer (i.e. content) "produces a variable" (i.e. a field), rather than producing more content?
Steve
CC BY-SA 4.0
2024-12-08T19:18:30.927Z
@Steve: You can store to a dereferenced pointer; it's a variable.
Eric Lippert
CC BY-SA 4.0
2024-12-08T20:14:02.910Z
@EricLippert, are we talking about the l-value/r-value distinction? Otherwise, I'm not following why the field/variable containing the initial (un-dereferenced) pointer-value could not also be stored to?
Steve
CC BY-SA 4.0
2024-12-11T00:12:34.327Z
@Steve: Who said that the pointer value must be stored in a variable? Pointers are *values*; they don't have to be stored in a variable any more than any other value must be stored in a variable. If method `m()` returns a pointer -- a value -- then `m() + 4` is a *value*, and `*(m() + 4)` is a *variable*.
Eric Lippert
CC BY-SA 4.0
2024-12-11T11:31:49.157Z
@EricLippert, I follow. A pointer-value to main memory doesn't have to be stored in main memory itself, it can be stored in a register (or maybe, elsewhere). Whereas a pointer-value when dereferenced must, by definition, be a field in main memory. In C, registers themselves are not explicitly addressable or assignable by the programmer - they're compiler-managed. What confused me was the terminology "produces a variable" - it's obvious now that what this means is akin to "bring into scope a field that is guaranteed to be explicitly addressable". (1/2)
Steve
CC BY-SA 4.0
2024-12-11T11:31:52.173Z
That is, the deref operator can take either an l-value or an r-value as an argument, and is certain to return an l-value as a result. (2/2)
Steve
<p>Because a lot of low level APIs work with pointers. For example in openGL you might use a function like this: <code>void glBufferData(GLenum target, GLsizeiptr size, const void * data, GLenum usage);</code> Note you need to pass data in as a void pointer and the other arguments define how this needs to be interpreted.</p>
<p>This style of ABI call is very typical of low level or OS interfaces. Syscalls by nature can only read data in a very primitive way so it's hard to pass any more complex types than pointers.</p>
<p>You could of course write an abstraction layer to avoid working with pointers, and most languages do do that, but you can't write an abstraction layer for every possible API, even those on obscure platforms and operating systems. Programmers might also want to interface with libraries that use such calling conventions.</p>
<p>For this reason, it's nice to at least have the option to use raw pointers, if only so you can wrap these dangerous functions in a safer interface. There would be no way to use these calls at all without such a feature.</p>

CC BY-SA 4.0
2023-06-01T12:02:38.657Z
2023-06-01T14:32:29.097Z
2023-06-01T14:32:29.097Z
mousetail 'he-him'
CC BY-SA 4.0
2024-11-22T17:23:24.997Z
There's no reason APIs have to be like this. If you were designing the API for C++ you could do it using references instead of pointers. It's mainly just due to the history of APIs being developed using C.
Barmar
CC BY-SA 4.0
2024-11-22T18:46:46.290Z
@Barmar True, but low level APIs are almost universally designed for C
mousetail 'he-him'
CC BY-SA 4.0
2024-11-22T19:33:24.443Z
A bit of chicken and egg -- they're designed for C because Unix applications have traditionally been written in C. And they don't bother redesigning for C++ because it can call C APIs. And then they'd have to maintain multiple APIs.
Barmar
CC BY-SA 4.0
2024-11-27T17:41:39.437Z
Partly chicken and egg, but also partly that a C++ ABI is a LOT more complex than the C one.
Peter Green
<p>High level abstractions are all well and good, until they aren't. Sometimes you need to drop down to a lower level. Either to interact with code that was written in a different language, because you don't want to accept the performance cost of the high level abstractions, or because you are the person <em>creating</em> the high level abstractions.</p>
<p>C has for better or worse became the lingua-franca for low level interaction between code written in different languages.</p>
<p>Language designers have broadly two choices to accommodate the requirements for lower-level programming and/or for interacting with existing code written in other languages.</p>
<ol>
<li>Provide low-level programming constructs within the language.</li>
<li>Provide an API for interacting with the language's data structures and functions from low level code written in another language (usually C or C++).</li>
</ol>
<p>I think which approach is taken depends very much on how the language is intended to be implemented. A language intended to be compiled to native code is likely to favour option 1, while a language intended to be interpreted is more likely to favour option 2.</p>
<p>If you are writing an interpreter in C or C++, you likely already have C or C++ data structures to represent the state of your running high level language program and functions corresponding to operations the program can perform. Offering an extension API is a matter of cleaning that up, putting it in a header and doing a little bit of magic to load the extensions. Loading extensions at runtime requires stepping outside of standard C and C++, but both windows and posix make it fairly easy.</p>
<p>Interacting with arbitrary C code from an interpreter requires either a bunch of platform-specific knowledge (and C++ is even worse) or depending on a third-party library that encodes that platform-specific knowledge. This goes against one of the biggest advantages of writing an interpreter, the ability to write it in simple portable code.</p>
<p>If you are writing a compiler that compiles to native code, then you have little choice but to deal with platform-specific arcana, and you probablly don't have C/C++ data types representing your program's runtime state. So option 1 starts to look like the easier option.</p>

CC BY-SA 4.0
2023-09-15T22:07:47.510Z
2024-11-27T18:45:41.453Z
2024-11-27T18:45:41.453Z
Peter Green
CC BY-SA 4.0
2024-11-26T22:04:10.687Z
Note that option #2 would oten require a means of creating real direct pointers to at least some portions of a program's state, even if those portions of the program's state exist in a universe that is semantically distinct from everything else.  A language that can interact with such a universe well enough to interface with arbitrary code in C or C++ could also allow direct access to that universe without using external C or C++ functions.
supercat
CC BY-SA 4.0
2024-11-27T17:37:47.867Z
With the "API" approach, the glue code that knows about both the "high level language world" and the "C or C++ world" is written in C or C++. The API will indeed to provide the C/C++ code with some form of access to the high level languages data structures and this may involve pointers, but those pointers don't have to be visible in the high level language.
Peter Green
CC BY-SA 4.0
2024-11-27T23:02:19.133Z
A language wouldn't *have* to provide such access, but a language that could support the semantics "reserve some temporary storage, copy data into it from a managed object, pass that address to an arbitrary imported C function, and then copy that storage back into a managed object" could just as easily and safely support an operation that was identical, except with the address being passed as a `long` to a method that could then `peek` and `poke` with addresses computed based on that `long`.  The same dangers that would exist with the latter approach would also exist when calling C code.
supercat
CC BY-SA 4.0
2024-11-27T23:04:52.097Z
For languages that have value types, like those designed for the .NET CLS, the callback function could receive a "fat pointer" type which combines an address with a reference to information about the allocation, and could automatically invalidate itself when the allocation's lifetime ends.
supercat
CC BY-SA 4.0
2024-11-27T23:42:26.820Z
Yup, the same dangers exist, furthermore to usefully call arbitrary C functions you need to be able to usefully build the parameters for them and interpret the results. So practically if you want your language to be able to call "arbitrary C functions" you need to support a reasonable subset of C programming constructs.
Peter Green
CC BY-SA 4.0
2024-11-27T23:47:46.680Z
Once you have some way of interacting with the outside world, you can support those constructs through a library though, for example JNA for Java or Ctypes for python without adding them to a core language.
Peter Green
CC BY-SA 4.0
2024-11-28T05:12:59.833Z
Sure, one could use an outside library, but having the functions built into the language implementation would allow code written purely in the host language to accomplish many tasks without having to rely upon a separately built helper functions.
supercat
<p>The only time you really <em>need</em> to use raw pointers is when interfacing with hardware that uses memory mapped registers. So pointers are often necessary when writing kernel code such as device drivers.</p>
<p>In almost all other circumstances, higher-level abstractions are adequate. However, if you're implementing these abstractions in the language itself, you may need to use pointers internally. For instance, if the implementation of C++ <code>std::vector&lt;T&gt;</code> is written in C++, it will likely need to use <code>new T[size]</code> to allocate the memory for the vector data. All the &quot;smart pointer&quot; templates wrap around an underlying &quot;dumb pointer&quot;.</p>
<p>For just about any other circumstance where you think you need a pointer, you can solve it using the <a href="https://en.wikipedia.org/wiki/Fundamental_theorem_of_software_engineering" rel="nofollow noreferrer">Fundamental theorem of software engineering</a>:</p>
<blockquote>
<p>We can solve any problem by introducing an extra level of indirection.</p>
</blockquote>
<p>In languages without pointers, this usually means using a wrapper object of some kind. For instance, in C if you want a function to modify its caller's variable, you have it take a pointer parameter, and the function uses <code>*param = new_value;</code>. Without pointers, you do this by passing a reference to a structured object, and you do <code>param.value = new_value;</code>; this is how you would do it in languages like JavaScript or Python.</p>
<p>However, these wrapper objects are usually not as efficient as raw pointers. We often sacrifice performance for memory safety. But that tradeoff may not be acceptable in low-level code, so that's still implemented in less type-safe languages. The internal implementations of Python tuples and lists make extensive use of C-style pointers and arrays for this reason.</p>

CC BY-SA 4.0
2024-11-22T17:36:41.670Z
2024-11-22T17:42:12.150Z
2024-11-22T17:42:12.150Z
Barmar
<p>The ability to pass around references to objects is fundamental in many programming languages.  I'm assuming the question is referring to the concept of &quot;pointers&quot; more broadly, encompassing not merely pointers that identify &quot;root-level&quot; objects or allocations that an implementation has created, but also pointers that may identify other kinds of regions of storage.</p>
<p>Pointer manipulations, often including the ability to convert pointers to integers, perform computations, and then convert the resulting integers back to pointers, provide a means by which human knowledge related to regions of memory and what may be done with them, can be applied to the process of instructing computers to do those things.</p>
<p>As a simple example, on systems that can only accommodate one program in memory at once (which used to be common in many microcomputer systems, and remains common in the embedded systems realm), any memory that isn't being used by the current program won't be used by anything at all until the current program exits.  Thus, it would be useful to allow a program to, on startup, identify all space that isn't used for anything else and treat it as a blob the program can use as it sees fit, adapting itself to the amount of space that happens to be available.</p>
<p>On a typical embedded systems compiler, this may be accomplished by instructing the linker to automatically generate a symbol that points to just past the last object which it placed near the bottom of storage, and another that points to the first object that it placed near the top.  Any storage between these two addresses then be used as the program sees fit.  The C Standard doesn't allow for the possibility that a programmer might know anything about the relationship between two symbols that--from the compiler's point of view--would have nothing special about them, but when using implementations that are designed to be suitable for systems programming, a programmer can write memmory-allocation functions which manipulate pointers to access storage within this area.</p>
<p>When enabling some compilers' optimizations, however, there can be a problem.  Suppose a program does something like:</p>
<pre><code>extern unsigned char heap_start[], heap_end[];

void heap_init(void)
{
  for (unsigned char *p=heap_start; p != heap_end; p++)
    *p=0;
  p[-1]=255;
}
int sneaky_test(int x)
{
  heap_start[x]=0;
  heap_init();
  return heap_start[x];
}
</code></pre>
<p>If <code>sneaky_test</code> happens to be passed a value one less than the displacement between <code>heap_end</code> and <code>heap_start</code>, then <code>heap_init</code> will set <code>heap_start[x]</code> to 255, and the <code>sneaky_test</code> function should return 255.  Some compilers, however, might recognize that because the pointer <code>p</code> used in the statement <code>p[-1]=255;</code> will always equal <code>heap_end</code>, that expression may be replaced with <code>heap_end[-1]=255;</code>, and since nothing that happens between <code>heap_start[x]=0</code> and <code>return heap_start[x]</code> sets any storage that could be derived from <code>heap_start</code> to anything other than zero, the compiler may generate code that always returns zero.</p>
<p>Unfortunately, the way free compilers, or compilers based upon them, have evolved, it is often much easier to determine the sequence of machine operations that would need to be done to perform a task, and even to write source code that will on any <em>given</em> version of the compiler yield a machine code program that, if inspected, would be found to perform that sequence of operations, than to write source code that would, <em>by specification</em>, produce a usable machine code program.  In most cases where a programmer might need to perform operations like the above, a compiler wouldn't happen to find &quot;optimizing&quot; transforms that would allow it to skip steps that are essential to the task at hand, even if it would have performed the transformations if it had found them, and thus code which would be usefully processed by today's compiler might fail with future compilers that &quot;optimize&quot; more aggressively.</p>

CC BY-SA 4.0
2023-09-16T19:36:54.057Z
2023-09-17T16:13:22.377Z
2023-09-17T16:13:22.377Z
supercat
CC BY-SA 4.0
2023-09-16T20:36:51.613Z
I don't fully understand this answer. What advantage of pointer-use is being described here?
David Young
CC BY-SA 4.0
2023-09-17T16:05:43.180Z
@DavidYoung: The ability to create and use pointers *whose addresses have no meaning understood by the language implementation* makes it possible to treat regions of address space that are understood by the programmer but not the implementation in the same way as one would treat storage that was e.g. allocated on an implementation-managed heap.  A program that uses such techniques can continuously track on how much memory is available, and avoid starting operations which the program wouln't have enough memory to complete.
supercat
CC BY-SA 4.0
2023-09-17T16:08:50.480Z
@DavidYoung: I think (now) third paragraph clearly describes a "pro" of a language allowing pointers to be manipulated in ways an implementation couldn't be expected to validate.  The rest of the answer concerns a limitation/downside, which requires a bit more background info to explain, but whose essence I guess could be described as "Constructs which manipulate pointers in ways whose implications an implementations can't fully understand may run afoul of implementations which are designed around the assumption that they can understand all implications of everything done with pointers."
supercat
<p>The crucial thing about pointers is that certain algorithms and data structures can't be expressed without them.</p>
<p>Any program that branches requires pointers.</p>
<p>And any data structure that is variable-length or recursive requires a system of indirection to express, of which pointers are a native implementation.</p>
<p>There are two different purposes or usages associated with pointers - pointers to code (which you call or jump with), and pointers to data (which you dereference with or follow).</p>
<p>There are few useful computer applications that can do without branching or variable-length data structures, so all languages have the concept of pointers <em>at least implicitly</em>.</p>
<p>A disadvantage of not allowing raw pointer access, is that the workings of such a language can't be explained in its own terms, and therefore such languages with concealed or implied workings are poor for educational use.</p>
<p>Another disadvantage of not allowing direct pointer access, is that patterns which fundamentally require indirection in a way which steps outside any facility offered by the language, end up being reproduced in a way that leads to source code that is laborious and non-standard, is inefficient to execute, and which are just as prone to crashes and implementation errors.</p>
<p>Some typical advantages of not allowing direct pointer access is enabling compiler optimisations. This is because the compiler writer controls all the patterns of how pointers can be used, and therefore can optimise in ways that might not be possible if the language user could use pointers freely.</p>
<p>Similarly, certain language features can be guaranteed to work, in cases where they might often be broken if direct pointer access were allowed.</p>
<p>Another perceived advantage to disallowing direct pointer access is safety. The thinking is that you can't get it wrong if you can't do it.</p>
<p>I'm a bit more sceptical of this last advantage though. It's more like saying an electrician can't shock himself if he doesn't have access to the wiring - the point is to achieve the benefits of what electricians do with lesser risk, not to simply avoid the risks by avoiding the activity altogether (and implicitly avoiding the benefits).</p>
<p>I think the problem sometimes just gets moved around this way. The &quot;safe&quot; language is clearly more safe against a particular fault, but is then less safe against other hazards (including how either complicated workarounds, or simplified and limited solutions, become more liable to contain faults).</p>

CC BY-SA 4.0
2023-09-16T10:39:24.640Z
2024-11-21T23:03:06.790Z
2024-11-21T23:03:06.790Z
Steve
CC BY-SA 4.0
2023-09-19T14:12:02.597Z
Any program that branches requires pointers??
chrysante
CC BY-SA 4.0
2023-09-19T15:17:58.050Z
@chrysante, indeed, *at least implicitly*. Many languages conceal this, but you can't fundamentally understand what computers do without pointers.
Steve
CC BY-SA 4.0
2023-09-19T15:32:02.973Z
Yes, probably all programming languages use pointers in their implementation, also the ones that don't expose pointers to the user. But I can easily write a program in assembly that branches but never references any memory. Okay, if I use the `ret` instruction on x86, that will use the stack pointer to read the return address, but the `jmp` instruction does not read or write memory as far as I know. Or does it? I guess this is kind of pedantic but it seems wrong to me that you "can't branch without pointers". (Edit: Oh, except if you consider the instruction pointer, well :))
chrysante
CC BY-SA 4.0
2023-09-19T16:10:11.933Z
@chrysante, to branch there must be a destination, and that destination must be stored as a pointer (to which the instruction pointer register will be set). Pointers aren't an implementation detail - they're a computer implementation of the general concept of indirection. A language which doesn't acknowledge them is limited in terms of even explaining its own implementation, let alone anything else.
Steve
CC BY-SA 4.0
2023-09-21T20:15:14.487Z
It’s *very typical* for an architecture to have an instruction pointer natively, but I have worked on a chip where there wasn’t one. Of course, I also designed abstractions to make it *look* like there was, because it’s a familiar programming model. But for example loops and branching didn’t necessarily involve pointers at all, if they didn’t need to fetch code from memory.
Jon Purdy
CC BY-SA 4.0
2023-09-21T23:45:27.057Z
@JonPurdy, how did that architecture express branching?
Steve
CC BY-SA 4.0
2023-09-22T03:54:52.337Z
@Steve: There were a few ways. If you wanted to emulate an instruction pointer, you would just fetch one page of code after another, and for dynamic branches, either compute which page to fetch, or fetch multiple and compute which one to queue up for execution. But several of the control operations had to do with code that wasn’t anywhere *addressable*, for example, async I/O or repeating a recent instruction cached in a register. You can hide those details too, it’s a design choice is all.
Jon Purdy
CC BY-SA 4.0
2023-09-22T07:40:23.640Z
@JonPurdy, it certainly sounds unconventional, but aren't we simply dealing with pointers to page-addressable code, rather than byte-addressable? It seems to me that even though implementation details can vary, there is something essential and irreducible about storage being addressable (at some level of granularity), and about the processor having to have some facility for expressing (and altering) the storage address from which it is currently fetching and executing instructions.
Steve
CC BY-SA 4.0
2024-11-21T22:10:02.657Z
@Steve: One approach to handle branching, which is in practice used by some kinds of scripting languages, is a "nested skips" counter.  When the counter is non-zero, "if" statements will increment it (without testing the condition) and "endif" will decrement it.  An "else" will zero the counter if it is precisely 1 and otherwise have no effect.  Such a language could be used with source code fed via stream without needing to keep track of position within code.  Looping can be handled (albeit not necessarily efficiently) by restarting the stream.
supercat
CC BY-SA 4.0
2024-11-21T23:02:21.333Z
@supercat, I must admit I don't quite understand the detail of what you describe, because my understanding of the `if` block is that it is itself a conditional branch to a storage address. You seem to be saying the if and endif statements in your language perform an unconditional operation, but that the else statement becomes the focal point for a conditional operation - in which case we've just moved the issue around, not purged any unnecessary concept.
Steve
CC BY-SA 4.0
2024-11-21T23:41:46.703Z
@Steve: I guess I left out a detail which is that encountering "ELSE" when the skip count is 0 sets the skip count to 1, as does executing an "IF" whose condition is false when the skip count is 0.  If a script processor sees "IF X THEN FOO ELSE BAR ENDIF", then if X is true, the skip count will be 0 while it's reading "FOO", and would be 1 while it was reading "BAR", so only "FOO" would execute.  Either way, it would get set to 0 after it encounters the "ENDIF".  If "X" had been false, then skip count would be 1 while reading "FOO" and 0 while reading "BAR", so only "BAR" would execute.
supercat
CC BY-SA 4.0
2024-11-21T23:45:24.330Z
@Steve: If one needs to handle "GOTO", one can restart reading the stream, and ignore everything until one encounters e.g. a line whose content matches the text after "GOTO" along with an appended or prepended colon.  That's how MS-DOS batch files work, btw (using a prepended colon).  Not exactly fast, but there's no need for any concepts related to file position other than a means of restarting a stream from the beginning.
supercat
CC BY-SA 4.0
2024-11-22T00:28:44.420Z
@supercat, you're just substituting more complicated and less familiar systems of addressing. It still fundamentally works so that a place in the code is identified by representation - a pointer equivalent - and something occurs by which execution proceeds to that place - a jump equivalent. I don't think this technique is specific to computing (which merely has a highly systematic and essential manifestation of it) or other mechanical automations.
Steve
CC BY-SA 4.0
2024-11-22T11:22:52.887Z
@Steve A pointer is a number, it can take on any value in a continuous range, and changing the value allows you to point to a different instruction. A goto label can only take discrete values, and referencing a value that hasn't been used as a label will result in an error, not a jump to a different instruction. The goto implementation described also doesn't need to know *where* the label occurs, the label remains valid when instructions are added before and after it; a pointer requires knowledge of a stable position or offset.
IMSoP
CC BY-SA 4.0
2024-11-22T12:47:18.620Z
So conditional execution can be implemented via conditional jumps (with absolute or relative pointers) o with conditional execution (as in conditional add, etc.). Conditional execution of a block could be done by setting an "don't execute" flag (e.g. after evaluating the condition). I think GPU's did that at some point (stream processing).
Pablo H
CC BY-SA 4.0
2024-11-22T13:22:06.187Z
@PabloH You don't have to look to specialist architectures or operations, the extremely widespread ARM architecture allows almost any instruction to be marked as conditional on a bunch of different flags; see e.g. https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/condition-codes-2-conditional-execution
IMSoP
CC BY-SA 4.0
2024-11-22T13:56:30.020Z
@IMSoP, if you allow pointers to represent values larger than the valid address space, that too would result in an error - that is what you're doing by allowing arbitrary strings but only defining some of them as valid places. You could enhance further with some crazy, non-sequential mapping. I also don't understand what you mean by "the implementation doesn't need to know where the label occurs" - it looks the location of label up on each jump. As I say, you're just proposing innovations from the familiar forms of indirection via addressing, to the less familiar.
Steve
CC BY-SA 4.0
2024-11-22T15:04:17.527Z
@Steve In the algorithm supercat described, the label doesn't represent a *location*, it represents a *search pattern*; you could implement it on an architecture that had no concept of addresses at all, just a sequential tape. There is no step where a location is "looked up", there are just conditions where the current line is either executed, skipped, or compared against a target label. It's equivalent in the sense that you can achieve the same things with it, but that's like saying running is equivalent to riding a bike.
IMSoP
CC BY-SA 4.0
2024-11-22T15:31:28.047Z
@IMSoP, there's really no fundamental difference between a "search pattern" and an "address" in this context. An address doesn't "represent a location", it represents information necessary to *find* a location. In a computer, each memory location is circuit-switched at the hardware level - that switching is the "finding" for native addresses. You are just introducing an explicit scheme in software where the finding itself is explicit in the software algorithm rather than in the underlying workings of the hardware. This is why I say, you're just swapping the familiar for the less familiar.
Steve
CC BY-SA 4.0
2024-11-22T16:30:42.603Z
@Steve I simply don't agree. An address represents a position in some sequence; every word, or every page, has an address, even if it's never used; a location can't change it's address. A label exists only when it's assigned to something; it can be moved, or swapped for a different label; it might not even be unique - in a linear scan, you can't tell the difference between "the only place this label occurs" and "the nearest place this label occurs". You can implement one on top of the other, but you don't have to.
IMSoP
CC BY-SA 4.0
2024-11-22T17:21:37.170Z
The question is about pointers that are exposed explicitly in the language. Of course pointers have to be used internally in the implementation of things like branches and references, but we don't make them visible to the programmer using the language, which is what adds memory safety.
Barmar
CC BY-SA 4.0
2024-11-22T17:36:40.280Z
@IMSoP, I just don't see why these differences are fundamental. Main memory addresses are sequential because nobody could see any other design choice to be sensible or desirable, not because it is mechanically impossible to have a computer with gaps in the addresses. When addressing with strings and line labels, there is incomplete coverage because it is unnecessary work for the programmer to write code that achieves complete coverage (when he actually jumps only to a very few key places), not because it is impossible to do so. (1/2)
Steve
CC BY-SA 4.0
2024-11-22T17:36:43.920Z
And the idea of stopping at the first/nearest case of duplicate addresses, you see that with segmentation and near/far pointers. (2/2)
Steve
CC BY-SA 4.0
2024-11-22T17:48:27.643Z
@Barmar, with "memory safety", a number of risks arise not so much from the use of pointers per se, but from safety being forgone in the name of simplicity or speed of execution. And a number of other risks, which would be called "referential integrity" issues in a database context, also carry over into the realm of custom addressing schemes which replace raw pointers, if the safety of the scheme is again forgone in the name of simplicity or speed of execution.
Steve
CC BY-SA 4.0
2024-11-22T23:03:57.207Z
It seems like if we follow your line of reasoning, we get to "all programming languages are equivalent to a Turing Machine, any differences are superficial". So I guess we might as well shut down this site? Surely the useful discussion comes from looking at the facilities and restrictions that arise from different designs and abstractions, not arguing which differences are "fundamental".
IMSoP
CC BY-SA 4.0
2024-11-23T04:17:27.470Z
@IMSoP, programming languages can differ because they make certain things easier or harder for the programmer, or for people who are allied or auxiliary to the same work (including educators or academics who have to handle and reproduce the conceptual content, or compiler writers and hardware designers whose conceptualisations need to be apt and internally consistent across a wider scope than just the language itself), not because different programming languages all have fundamentally different facilities or restrictions. (1/3)
Steve
CC BY-SA 4.0
2024-11-23T04:20:00.730Z
I'm sure we've had some sort of deeper discussion before about computing or programming languages, and there's some kind of difference in our broader intellectual behaviour or culture with which I do not just disagree, but to which I am positively hostile - a difference which is not specific to you or even to those found in the field of programming, but notable wherever study becomes rarefied. In a programming context, I find the words "(do/don't) need to know" (when talking about the knowledge of anything but a person) seems to be a strong indication that this difference is present. (2/3)
Steve
CC BY-SA 4.0
2024-11-23T04:27:25.157Z
As for this pointer thing, I see instruction sequences and jumps (and therefore a method of addressing the destination) as being fundamental to how certain behaviours would often be conceptualised and expressed in natural language, and the computer as merely automating (and intensifying the systematic, mechanical aspects of) this. So when you tell me it's not necessary, you're implicitly challenging the tenets of a practice far bigger and older than computer programming languages. Writing and following a series of instructions, was not first invented with the computer. (3/3)
Steve
CC BY-SA 4.0
2024-11-26T21:59:20.160Z
@Steve: A fundamental feature of pointers is that the work rquired to fetch or update something given a pointer to it is generally increases slowly as the universe of things the pointer could identify gets larger.  When identifying something sole via pattern-match string, doubling the size of the universe would generally double the amount of effort required to find it.  One could perhaps argue that in general the work required to decode an address increase would be proportional to the log of the size of the universe, and that a Bloom filter tree could achieve access and update times...
supercat
CC BY-SA 4.0
2024-11-26T22:01:15.433Z
...that were also proportional to the log of the size of the universe, but the latter would require that the time required to perform each step of the Bloom filter handling logic remained constant with respect to the size of the universe--an assumption that would be unlikely to hold if the expanding universe increased the cost of pointer-based accesses.
supercat
CC BY-SA 4.0
2024-11-26T23:37:23.010Z
@supercat, all you're doing is identifying the efficiency of the algorithm or mechanism associated with pointers as we know them. I don't doubt that there can be systems of addressing devised far less efficient than what we currently know. But there still has to be such a system, however inefficient and at whatever level, in order to implement basic operations of a general purpose computer.
Steve
CC BY-SA 4.0
2024-11-27T00:24:43.863Z
@Steve: The only differences between a Turing machine and a modern computer are that a modern machine has finite storage, but Turing machine can accomplish many practical tasks much faster.  Actually, I suspect that nowadays one could for a relatively modest cost build an arbitrarily expandable Turing machine and add storage to it faster than it would be able to access the further reaches, at least when trying to solve any practical problem.
supercat
# What are the advantages of allowing users to directly handle memory addresses with pointers?
Posted by: username_0 on 2023-06-01
[Pointers](https://en.wikipedia.org/wiki/Pointer_(computer_programming)) are variables that store a memory address. They allow the programmer to directly deal with memory which can be used to pass variables by reference or even for performance reasons.

However, common programming advice includes not using pointers while coding for reasons such as dangling pointers, memory leaks, or just the mental overhead that comes from using pointers. I don't even have to mention the ['Billion Dollar Mistake'](https://en.wikipedia.org/wiki/Null_pointer) that comes from null pointers.

There are of course languages such as C and C++ that feature pointers. However, even newer languages such as Go and [unsafe] Rust as well as lower level assembly languages allow programmers to directly use pointers. In Java, all objects are of reference types, but the user can't access the memory addresses directly.

So my question basically is, what are the pros and cons of allowing direct access to pointers and memory addresses?
**Tags:** language-design pointers
> **Comments:**
> **username_1**: Pointers in Go are more similar to nullable references. You can't do pointer arithmetic.  
> **username_2**: Note that you still *can* access memory addresses of objects in Java (and other JVM languages) using `sun.misc.Unsafe`.  
> **username_3**: Even very high level languages like python do allow raw pointer accesses though you have to jump through a lot more hoops  
> **username_4**: Note that the first sentence in your question is a falsehood, and you should not reason about a thing by starting with a falsehood. Pointers are not variables that store an address. Pointers are *values* which *are* an address, and have a special operator "dereference" that *produces* a variable. A *variable* of *pointer type* is a variable which stores an address. This is not a hairsplitting distinction!  
> **username_5**: @EricLippert, could you pad that line of thinking out a little more? If I understand correctly, you're refering to the distinction between fields (a place for storage) and the contents of those fields (the "value", or the data stored at that place). In computing, there is no reality of either one without the other. But if the word "pointer" refers to the content, and the word "field" refers to place, I struggle to understand why a deref of a pointer (i.e. content) "produces a variable" (i.e. a field), rather than producing more content?  
> **username_4**: @Steve: You can store to a dereferenced pointer; it's a variable.  
> **username_5**: @EricLippert, are we talking about the l-value/r-value distinction? Otherwise, I'm not following why the field/variable containing the initial (un-dereferenced) pointer-value could not also be stored to?  
> **username_4**: @Steve: Who said that the pointer value must be stored in a variable? Pointers are *values*; they don't have to be stored in a variable any more than any other value must be stored in a variable. If method `m()` returns a pointer -- a value -- then `m() + 4` is a *value*, and `*(m() + 4)` is a *variable*.  
> **username_5**: @EricLippert, I follow. A pointer-value to main memory doesn't have to be stored in main memory itself, it can be stored in a register (or maybe, elsewhere). Whereas a pointer-value when dereferenced must, by definition, be a field in main memory. In C, registers themselves are not explicitly addressable or assignable by the programmer - they're compiler-managed. What confused me was the terminology "produces a variable" - it's obvious now that what this means is akin to "bring into scope a field that is guaranteed to be explicitly addressable". (1/2)  
> **username_5**: That is, the deref operator can take either an l-value or an r-value as an argument, and is certain to return an l-value as a result. (2/2)  
---
## 5 answers
### Answer by username_3 on 2023-06-01. Score: 12
Because a lot of low level APIs work with pointers. For example in openGL you might use a function like this: `void glBufferData(GLenum target, GLsizeiptr size, const void * data, GLenum usage);` Note you need to pass data in as a void pointer and the other arguments define how this needs to be interpreted.

This style of ABI call is very typical of low level or OS interfaces. Syscalls by nature can only read data in a very primitive way so it's hard to pass any more complex types than pointers.

You could of course write an abstraction layer to avoid working with pointers, and most languages do do that, but you can't write an abstraction layer for every possible API, even those on obscure platforms and operating systems. Programmers might also want to interface with libraries that use such calling conventions.

For this reason, it's nice to at least have the option to use raw pointers, if only so you can wrap these dangerous functions in a safer interface. There would be no way to use these calls at all without such a feature.
> **Comments:**
> **username_6** (2024-11-22): There's no reason APIs have to be like this. If you were designing the API for C++ you could do it using references instead of pointers. It's mainly just due to the history of APIs being developed using C.  
> **username_3** (2024-11-22): @Barmar True, but low level APIs are almost universally designed for C  
> **username_6** (2024-11-22): A bit of chicken and egg -- they're designed for C because Unix applications have traditionally been written in C. And they don't bother redesigning for C++ because it can call C APIs. And then they'd have to maintain multiple APIs.  
> **username_7** (2024-11-27): Partly chicken and egg, but also partly that a C++ ABI is a LOT more complex than the C one.  
---
### Answer by username_7 on 2023-09-15. Score: 6
High level abstractions are all well and good, until they aren't. Sometimes you need to drop down to a lower level. Either to interact with code that was written in a different language, because you don't want to accept the performance cost of the high level abstractions, or because you are the person *creating* the high level abstractions.

C has for better or worse became the lingua-franca for low level interaction between code written in different languages.

Language designers have broadly two choices to accommodate the requirements for lower-level programming and/or for interacting with existing code written in other languages.

1. Provide low-level programming constructs within the language.
2. Provide an API for interacting with the language's data structures and functions from low level code written in another language (usually C or C++).

I think which approach is taken depends very much on how the language is intended to be implemented. A language intended to be compiled to native code is likely to favour option 1, while a language intended to be interpreted is more likely to favour option 2.

If you are writing an interpreter in C or C++, you likely already have C or C++ data structures to represent the state of your running high level language program and functions corresponding to operations the program can perform. Offering an extension API is a matter of cleaning that up, putting it in a header and doing a little bit of magic to load the extensions. Loading extensions at runtime requires stepping outside of standard C and C++, but both windows and posix make it fairly easy.

Interacting with arbitrary C code from an interpreter requires either a bunch of platform-specific knowledge (and C++ is even worse) or depending on a third-party library that encodes that platform-specific knowledge. This goes against one of the biggest advantages of writing an interpreter, the ability to write it in simple portable code.

If you are writing a compiler that compiles to native code, then you have little choice but to deal with platform-specific arcana, and you probablly don't have C/C++ data types representing your program's runtime state. So option 1 starts to look like the easier option.
> **Comments:**
> **username_8** (2024-11-26): Note that option #2 would oten require a means of creating real direct pointers to at least some portions of a program's state, even if those portions of the program's state exist in a universe that is semantically distinct from everything else.  A language that can interact with such a universe well enough to interface with arbitrary code in C or C++ could also allow direct access to that universe without using external C or C++ functions.  
> **username_7** (2024-11-27): With the "API" approach, the glue code that knows about both the "high level language world" and the "C or C++ world" is written in C or C++. The API will indeed to provide the C/C++ code with some form of access to the high level languages data structures and this may involve pointers, but those pointers don't have to be visible in the high level language.  
> **username_8** (2024-11-27): A language wouldn't *have* to provide such access, but a language that could support the semantics "reserve some temporary storage, copy data into it from a managed object, pass that address to an arbitrary imported C function, and then copy that storage back into a managed object" could just as easily and safely support an operation that was identical, except with the address being passed as a `long` to a method that could then `peek` and `poke` with addresses computed based on that `long`.  The same dangers that would exist with the latter approach would also exist when calling C code.  
> **username_8** (2024-11-27): For languages that have value types, like those designed for the .NET CLS, the callback function could receive a "fat pointer" type which combines an address with a reference to information about the allocation, and could automatically invalidate itself when the allocation's lifetime ends.  
> **username_7** (2024-11-27): Yup, the same dangers exist, furthermore to usefully call arbitrary C functions you need to be able to usefully build the parameters for them and interpret the results. So practically if you want your language to be able to call "arbitrary C functions" you need to support a reasonable subset of C programming constructs.  
> **username_7** (2024-11-27): Once you have some way of interacting with the outside world, you can support those constructs through a library though, for example JNA for Java or Ctypes for python without adding them to a core language.  
> **username_8** (2024-11-28): Sure, one could use an outside library, but having the functions built into the language implementation would allow code written purely in the host language to accomplish many tasks without having to rely upon a separately built helper functions.  
---
### Answer by username_6 on 2024-11-22. Score: 1
The only time you really *need* to use raw pointers is when interfacing with hardware that uses memory mapped registers. So pointers are often necessary when writing kernel code such as device drivers.

In almost all other circumstances, higher-level abstractions are adequate. However, if you're implementing these abstractions in the language itself, you may need to use pointers internally. For instance, if the implementation of C++ `std::vector` is written in C++, it will likely need to use `new T[size]` to allocate the memory for the vector data. All the "smart pointer" templates wrap around an underlying "dumb pointer".

For just about any other circumstance where you think you need a pointer, you can solve it using the [Fundamental theorem of software engineering](https://en.wikipedia.org/wiki/Fundamental_theorem_of_software_engineering):

> We can solve any problem by introducing an extra level of indirection.

In languages without pointers, this usually means using a wrapper object of some kind. For instance, in C if you want a function to modify its caller's variable, you have it take a pointer parameter, and the function uses `*param = new_value;`. Without pointers, you do this by passing a reference to a structured object, and you do `param.value = new_value;`; this is how you would do it in languages like JavaScript or Python.

However, these wrapper objects are usually not as efficient as raw pointers. We often sacrifice performance for memory safety. But that tradeoff may not be acceptable in low-level code, so that's still implemented in less type-safe languages. The internal implementations of Python tuples and lists make extensive use of C-style pointers and arrays for this reason.
---
### Answer by username_8 on 2023-09-16. Score: 1
The ability to pass around references to objects is fundamental in many programming languages.  I'm assuming the question is referring to the concept of "pointers" more broadly, encompassing not merely pointers that identify "root-level" objects or allocations that an implementation has created, but also pointers that may identify other kinds of regions of storage.
Pointer manipulations, often including the ability to convert pointers to integers, perform computations, and then convert the resulting integers back to pointers, provide a means by which human knowledge related to regions of memory and what may be done with them, can be applied to the process of instructing computers to do those things.
As a simple example, on systems that can only accommodate one program in memory at once (which used to be common in many microcomputer systems, and remains common in the embedded systems realm), any memory that isn't being used by the current program won't be used by anything at all until the current program exits.  Thus, it would be useful to allow a program to, on startup, identify all space that isn't used for anything else and treat it as a blob the program can use as it sees fit, adapting itself to the amount of space that happens to be available.
On a typical embedded systems compiler, this may be accomplished by instructing the linker to automatically generate a symbol that points to just past the last object which it placed near the bottom of storage, and another that points to the first object that it placed near the top.  Any storage between these two addresses then be used as the program sees fit.  The C Standard doesn't allow for the possibility that a programmer might know anything about the relationship between two symbols that--from the compiler's point of view--would have nothing special about them, but when using implementations that are designed to be suitable for systems programming, a programmer can write memmory-allocation functions which manipulate pointers to access storage within this area.
When enabling some compilers' optimizations, however, there can be a problem.  Suppose a program does something like:
extern unsigned char heap_start[], heap_end[];

void heap_init(void)
{
  for (unsigned char *p=heap_start; p != heap_end; p++)
    *p=0;
  p[-1]=255;
}
int sneaky_test(int x)
{
  heap_start[x]=0;
  heap_init();
  return heap_start[x];
}

If sneaky_test happens to be passed a value one less than the displacement between heap_end and heap_start, then heap_init will set heap_start[x] to 255, and the sneaky_test function should return 255.  Some compilers, however, might recognize that because the pointer p used in the statement p[-1]=255; will always equal heap_end, that expression may be replaced with heap_end[-1]=255;, and since nothing that happens between heap_start[x]=0 and return heap_start[x] sets any storage that could be derived from heap_start to anything other than zero, the compiler may generate code that always returns zero.
Unfortunately, the way free compilers, or compilers based upon them, have evolved, it is often much easier to determine the sequence of machine operations that would need to be done to perform a task, and even to write source code that will on any given version of the compiler yield a machine code program that, if inspected, would be found to perform that sequence of operations, than to write source code that would, by specification, produce a usable machine code program.  In most cases where a programmer might need to perform operations like the above, a compiler wouldn't happen to find "optimizing" transforms that would allow it to skip steps that are essential to the task at hand, even if it would have performed the transformations if it had found them, and thus code which would be usefully processed by today's compiler might fail with future compilers that "optimize" more aggressively.
> **Comments:**
> **username_9** (2023-09-16): I don't fully understand this answer. What advantage of pointer-use is being described here?  
> **username_8** (2023-09-17): @DavidYoung: The ability to create and use pointers *whose addresses have no meaning understood by the language implementation* makes it possible to treat regions of address space that are understood by the programmer but not the implementation in the same way as one would treat storage that was e.g. allocated on an implementation-managed heap.  A program that uses such techniques can continuously track on how much memory is available, and avoid starting operations which the program wouln't have enough memory to complete.  
> **username_8** (2023-09-17): @DavidYoung: I think (now) third paragraph clearly describes a "pro" of a language allowing pointers to be manipulated in ways an implementation couldn't be expected to validate.  The rest of the answer concerns a limitation/downside, which requires a bit more background info to explain, but whose essence I guess could be described as "Constructs which manipulate pointers in ways whose implications an implementations can't fully understand may run afoul of implementations which are designed around the assumption that they can understand all implications of everything done with pointers."  
---
### Answer by username_5 on 2023-09-16. Score: 1
The crucial thing about pointers is that certain algorithms and data structures can't be expressed without them.

Any program that branches requires pointers.

And any data structure that is variable-length or recursive requires a system of indirection to express, of which pointers are a native implementation.

There are two different purposes or usages associated with pointers - pointers to code (which you call or jump with), and pointers to data (which you dereference with or follow).

There are few useful computer applications that can do without branching or variable-length data structures, so all languages have the concept of pointers *at least implicitly*.

A disadvantage of not allowing raw pointer access, is that the workings of such a language can't be explained in its own terms, and therefore such languages with concealed or implied workings are poor for educational use.

Another disadvantage of not allowing direct pointer access, is that patterns which fundamentally require indirection in a way which steps outside any facility offered by the language, end up being reproduced in a way that leads to source code that is laborious and non-standard, is inefficient to execute, and which are just as prone to crashes and implementation errors.

Some typical advantages of not allowing direct pointer access is enabling compiler optimisations. This is because the compiler writer controls all the patterns of how pointers can be used, and therefore can optimise in ways that might not be possible if the language user could use pointers freely.

Similarly, certain language features can be guaranteed to work, in cases where they might often be broken if direct pointer access were allowed.

Another perceived advantage to disallowing direct pointer access is safety. The thinking is that you can't get it wrong if you can't do it.

I'm a bit more sceptical of this last advantage though. It's more like saying an electrician can't shock himself if he doesn't have access to the wiring - the point is to achieve the benefits of what electricians do with lesser risk, not to simply avoid the risks by avoiding the activity altogether (and implicitly avoiding the benefits).

I think the problem sometimes just gets moved around this way. The "safe" language is clearly more safe against a particular fault, but is then less safe against other hazards (including how either complicated workarounds, or simplified and limited solutions, become more liable to contain faults).
> **Comments:**
> **username_10** (2023-09-19): Any program that branches requires pointers??  
> **username_5** (2023-09-19): @chrysante, indeed, *at least implicitly*. Many languages conceal this, but you can't fundamentally understand what computers do without pointers.  
> **username_10** (2023-09-19): Yes, probably all programming languages use pointers in their implementation, also the ones that don't expose pointers to the user. But I can easily write a program in assembly that branches but never references any memory. Okay, if I use the `ret` instruction on x86, that will use the stack pointer to read the return address, but the `jmp` instruction does not read or write memory as far as I know. Or does it? I guess this is kind of pedantic but it seems wrong to me that you "can't branch without pointers". (Edit: Oh, except if you consider the instruction pointer, well :))  
> **username_5** (2023-09-19): @chrysante, to branch there must be a destination, and that destination must be stored as a pointer (to which the instruction pointer register will be set). Pointers aren't an implementation detail - they're a computer implementation of the general concept of indirection. A language which doesn't acknowledge them is limited in terms of even explaining its own implementation, let alone anything else.  
> **username_11** (2023-09-21): It’s *very typical* for an architecture to have an instruction pointer natively, but I have worked on a chip where there wasn’t one. Of course, I also designed abstractions to make it *look* like there was, because it’s a familiar programming model. But for example loops and branching didn’t necessarily involve pointers at all, if they didn’t need to fetch code from memory.  
> **username_5** (2023-09-21): @JonPurdy, how did that architecture express branching?  
> **username_11** (2023-09-22): @Steve: There were a few ways. If you wanted to emulate an instruction pointer, you would just fetch one page of code after another, and for dynamic branches, either compute which page to fetch, or fetch multiple and compute which one to queue up for execution. But several of the control operations had to do with code that wasn’t anywhere *addressable*, for example, async I/O or repeating a recent instruction cached in a register. You can hide those details too, it’s a design choice is all.  
> **username_5** (2023-09-22): @JonPurdy, it certainly sounds unconventional, but aren't we simply dealing with pointers to page-addressable code, rather than byte-addressable? It seems to me that even though implementation details can vary, there is something essential and irreducible about storage being addressable (at some level of granularity), and about the processor having to have some facility for expressing (and altering) the storage address from which it is currently fetching and executing instructions.  
> **username_8** (2024-11-21): @Steve: One approach to handle branching, which is in practice used by some kinds of scripting languages, is a "nested skips" counter.  When the counter is non-zero, "if" statements will increment it (without testing the condition) and "endif" will decrement it.  An "else" will zero the counter if it is precisely 1 and otherwise have no effect.  Such a language could be used with source code fed via stream without needing to keep track of position within code.  Looping can be handled (albeit not necessarily efficiently) by restarting the stream.  
> **username_5** (2024-11-21): @supercat, I must admit I don't quite understand the detail of what you describe, because my understanding of the `if` block is that it is itself a conditional branch to a storage address. You seem to be saying the if and endif statements in your language perform an unconditional operation, but that the else statement becomes the focal point for a conditional operation - in which case we've just moved the issue around, not purged any unnecessary concept.  
> **username_8** (2024-11-21): @Steve: I guess I left out a detail which is that encountering "ELSE" when the skip count is 0 sets the skip count to 1, as does executing an "IF" whose condition is false when the skip count is 0.  If a script processor sees "IF X THEN FOO ELSE BAR ENDIF", then if X is true, the skip count will be 0 while it's reading "FOO", and would be 1 while it was reading "BAR", so only "FOO" would execute.  Either way, it would get set to 0 after it encounters the "ENDIF".  If "X" had been false, then skip count would be 1 while reading "FOO" and 0 while reading "BAR", so only "BAR" would execute.  
> **username_8** (2024-11-21): @Steve: If one needs to handle "GOTO", one can restart reading the stream, and ignore everything until one encounters e.g. a line whose content matches the text after "GOTO" along with an appended or prepended colon.  That's how MS-DOS batch files work, btw (using a prepended colon).  Not exactly fast, but there's no need for any concepts related to file position other than a means of restarting a stream from the beginning.  
> **username_5** (2024-11-22): @supercat, you're just substituting more complicated and less familiar systems of addressing. It still fundamentally works so that a place in the code is identified by representation - a pointer equivalent - and something occurs by which execution proceeds to that place - a jump equivalent. I don't think this technique is specific to computing (which merely has a highly systematic and essential manifestation of it) or other mechanical automations.  
> **username_12** (2024-11-22): @Steve A pointer is a number, it can take on any value in a continuous range, and changing the value allows you to point to a different instruction. A goto label can only take discrete values, and referencing a value that hasn't been used as a label will result in an error, not a jump to a different instruction. The goto implementation described also doesn't need to know *where* the label occurs, the label remains valid when instructions are added before and after it; a pointer requires knowledge of a stable position or offset.  
> **username_13** (2024-11-22): So conditional execution can be implemented via conditional jumps (with absolute or relative pointers) o with conditional execution (as in conditional add, etc.). Conditional execution of a block could be done by setting an "don't execute" flag (e.g. after evaluating the condition). I think GPU's did that at some point (stream processing).  
> **username_12** (2024-11-22): @PabloH You don't have to look to specialist architectures or operations, the extremely widespread ARM architecture allows almost any instruction to be marked as conditional on a bunch of different flags; see e.g. https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/condition-codes-2-conditional-execution  
> **username_5** (2024-11-22): @IMSoP, if you allow pointers to represent values larger than the valid address space, that too would result in an error - that is what you're doing by allowing arbitrary strings but only defining some of them as valid places. You could enhance further with some crazy, non-sequential mapping. I also don't understand what you mean by "the implementation doesn't need to know where the label occurs" - it looks the location of label up on each jump. As I say, you're just proposing innovations from the familiar forms of indirection via addressing, to the less familiar.  
> **username_12** (2024-11-22): @Steve In the algorithm supercat described, the label doesn't represent a *location*, it represents a *search pattern*; you could implement it on an architecture that had no concept of addresses at all, just a sequential tape. There is no step where a location is "looked up", there are just conditions where the current line is either executed, skipped, or compared against a target label. It's equivalent in the sense that you can achieve the same things with it, but that's like saying running is equivalent to riding a bike.  
> **username_5** (2024-11-22): @IMSoP, there's really no fundamental difference between a "search pattern" and an "address" in this context. An address doesn't "represent a location", it represents information necessary to *find* a location. In a computer, each memory location is circuit-switched at the hardware level - that switching is the "finding" for native addresses. You are just introducing an explicit scheme in software where the finding itself is explicit in the software algorithm rather than in the underlying workings of the hardware. This is why I say, you're just swapping the familiar for the less familiar.  
> **username_12** (2024-11-22): @Steve I simply don't agree. An address represents a position in some sequence; every word, or every page, has an address, even if it's never used; a location can't change it's address. A label exists only when it's assigned to something; it can be moved, or swapped for a different label; it might not even be unique - in a linear scan, you can't tell the difference between "the only place this label occurs" and "the nearest place this label occurs". You can implement one on top of the other, but you don't have to.  
> **username_6** (2024-11-22): The question is about pointers that are exposed explicitly in the language. Of course pointers have to be used internally in the implementation of things like branches and references, but we don't make them visible to the programmer using the language, which is what adds memory safety.  
> **username_5** (2024-11-22): @IMSoP, I just don't see why these differences are fundamental. Main memory addresses are sequential because nobody could see any other design choice to be sensible or desirable, not because it is mechanically impossible to have a computer with gaps in the addresses. When addressing with strings and line labels, there is incomplete coverage because it is unnecessary work for the programmer to write code that achieves complete coverage (when he actually jumps only to a very few key places), not because it is impossible to do so. (1/2)  
> **username_5** (2024-11-22): And the idea of stopping at the first/nearest case of duplicate addresses, you see that with segmentation and near/far pointers. (2/2)  
> **username_5** (2024-11-22): @Barmar, with "memory safety", a number of risks arise not so much from the use of pointers per se, but from safety being forgone in the name of simplicity or speed of execution. And a number of other risks, which would be called "referential integrity" issues in a database context, also carry over into the realm of custom addressing schemes which replace raw pointers, if the safety of the scheme is again forgone in the name of simplicity or speed of execution.  
> **username_12** (2024-11-22): It seems like if we follow your line of reasoning, we get to "all programming languages are equivalent to a Turing Machine, any differences are superficial". So I guess we might as well shut down this site? Surely the useful discussion comes from looking at the facilities and restrictions that arise from different designs and abstractions, not arguing which differences are "fundamental".  
> **username_5** (2024-11-23): @IMSoP, programming languages can differ because they make certain things easier or harder for the programmer, or for people who are allied or auxiliary to the same work (including educators or academics who have to handle and reproduce the conceptual content, or compiler writers and hardware designers whose conceptualisations need to be apt and internally consistent across a wider scope than just the language itself), not because different programming languages all have fundamentally different facilities or restrictions. (1/3)  
> **username_5** (2024-11-23): I'm sure we've had some sort of deeper discussion before about computing or programming languages, and there's some kind of difference in our broader intellectual behaviour or culture with which I do not just disagree, but to which I am positively hostile - a difference which is not specific to you or even to those found in the field of programming, but notable wherever study becomes rarefied. In a programming context, I find the words "(do/don't) need to know" (when talking about the knowledge of anything but a person) seems to be a strong indication that this difference is present. (2/3)  
> **username_5** (2024-11-23): As for this pointer thing, I see instruction sequences and jumps (and therefore a method of addressing the destination) as being fundamental to how certain behaviours would often be conceptualised and expressed in natural language, and the computer as merely automating (and intensifying the systematic, mechanical aspects of) this. So when you tell me it's not necessary, you're implicitly challenging the tenets of a practice far bigger and older than computer programming languages. Writing and following a series of instructions, was not first invented with the computer. (3/3)  
> **username_8** (2024-11-26): @Steve: A fundamental feature of pointers is that the work rquired to fetch or update something given a pointer to it is generally increases slowly as the universe of things the pointer could identify gets larger.  When identifying something sole via pattern-match string, doubling the size of the universe would generally double the amount of effort required to find it.  One could perhaps argue that in general the work required to decode an address increase would be proportional to the log of the size of the universe, and that a Bloom filter tree could achieve access and update times...  
> **username_8** (2024-11-26): ...that were also proportional to the log of the size of the universe, but the latter would require that the time required to perform each step of the Bloom filter handling logic remained constant with respect to the size of the universe--an assumption that would be unlikely to hold if the expanding universe increased the cost of pointer-based accesses.  
> **username_5** (2024-11-26): @supercat, all you're doing is identifying the efficiency of the algorithm or mechanism associated with pointers as we know them. I don't doubt that there can be systems of addressing devised far less efficient than what we currently know. But there still has to be such a system, however inefficient and at whatever level, in order to implement basic operations of a general purpose computer.  
> **username_8** (2024-11-27): @Steve: The only differences between a Turing machine and a modern computer are that a modern machine has finite storage, but Turing machine can accomplish many practical tasks much faster.  Actually, I suspect that nowadays one could for a relatively modest cost build an arbitrarily expandable Turing machine and add storage to it faster than it would be able to access the further reaches, at least when trying to solve any practical problem.  
---
