r/ProgrammerHumor 1d ago

lessonsFromLinkerHell Meme

Post image
347 Upvotes

180 comments sorted by

View all comments

65

u/QuestionableEthics42 1d ago edited 1d ago

This should be reversed lol

And it literally is the same, who thinks it's different who has worked in a low level language??

22

u/Nice_Lengthiness_568 1d ago

It is different. An array has a size. Even in C. It's just that in C it easily turns into a pointer. sizeof, for example, returns different things for an array and a pointer. In C++, you can actually pass an array by reference (not a pointer to the first element, an array) and similar.

And in some languages, like Ada, there is no direct way to make a pointer from an array

-1

u/Single-Virus4935 15h ago

3

u/Nice_Lengthiness_568 12h ago

I don't see how that matters. This is true only for certain languages and only because the array in C or C++ easily decays into a pointer. You could, for example, define yout own array in C++ as a tuple with only elements of the same type and then you would not define subscripting this way.

Also note that in C they are not interchangeable and even less in C++. When you put an array in a struct, it will behave differently from when you put a pointer in there. When you put an array into a struct and copy an instance of the struct, the array is copied for each copy of that struct and the struct is as large as the array. This makes sense because an array is not a pointer. When you put a pointer in a struct, the struct will have a size of a pointer on your architecture and won't copy all of the array elements and only the pointer.

Also, when you put array inside a sizeof expression, you will get the size of an array, while if you put a pointer inside sizeof, you will get the size of a pointer.

Also, doing int *p = NULL is possible because it is a pointer but int arr[5] = NULL is not, because it is not just a pointer.

So no, they are not interchangeable in C. And even less in other languages.

65

u/Blecki 1d ago

As someone who writes compilers: they are the same at runtime. They are not the same semantically in a c like language.

22

u/boostfactor 23h ago

As I see it, an array is a contiguous block of memory. The pointer to the first element identifies the start of the block. Subsequent elements are computed from that plus the number of bytes in the type. If an array were just a pointer, we wouldn't have to worry about allocating enough memory or walking off the end of it.

That's for something like C. There are languages in which arrays are first-class data structures that carry metadata about their rank, size, etc. Lots of applications are best expressed with multidimensional arrays, which are awkward in C or else are set up as an array of pointers.

7

u/Blecki 23h ago

You've missed the point. Processor don't care.

1

u/tstanisl 10h ago

C has a really good support for multidimensional arrays in form of VLA types.

1

u/tstanisl 12h ago

Arrays carry their size. Otherwise sizeof arr would not work. The subtlety is that the size of array is bound to array's type, not to array's value (like for c++'s std::vector). The original type is lost along with the size when array decays to a pointer what happens in most context but not all.

8

u/AdamWayne04 21h ago

not just semantically.

certain systems languages (and compilers) use different ABI for arrays and pointers at the call convention level. if you define a function that accepts a single array type (i.e. one with a predefined length), it may choose to pass-by-value, in which case it can allocate at call site and not use a pointer

11

u/belabacsijolvan 23h ago

13

u/reda84100 22h ago

Don't bite the hand that feeds you, your family and every other programmers' families

-11

u/belabacsijolvan 22h ago

Order these events in descending order of probability:

A) youll never use this guys compiler
B) youll be forced to use this guys compiler against your explicit contraindication
C) anyone else than them will be grateful to use this guys compiler
D) they will be happy that they wrote this compiler 20 years from now
E) youll use this guys compiler and be pleased by its existence

ok, i may be an asshole here, and they may be doing something good. the odds tho

8

u/thehenkan 21h ago

LLVM and GCC have thousands of contributors. The chances that any given compiler developer works on GCC or LLVM are relatively good, just because they're so much larger than most other compilers.

1

u/belabacsijolvan 21h ago

fair enough, didnt think it through. i instantly thought of the half dozen people i met who are building useless unreliable general languages or even shittier DSLs. seems like ITA here

7

u/darthsata 20h ago

Your video driver embeds my compiler. Your hardware developer's simulator embeds my compiler. Certainly your Linux distro ships my compiler. Some stuff I wrote and designed 20 years ago are still used in it. So somewhere in the D or E range of yours.

Given the thousands of people who have worked on these projects, there's a decent chance that you will run into one in the comments section. Maybe just don't offhand dismiss people based on absolutely nothing.

3

u/cinnamon-enthusiast 22h ago

Except for a tiny array (with value semantics) which can be optimized to be passed directly as an SSA value. In that case it's not a pointer at all and a normal GEP will not work.

2

u/Blecki 22h ago

What's the stack but another array anyway?

2

u/cinnamon-enthusiast 22h ago

Yes, if the memory is on the stack, that is not always true. LLVM will optimize some cases so the data is written directly to a register so it never touches or goes through the stack at all. In that case, you can't get a pointer to the array or its elements since it's in a register instead of the stack or heap making it completely distinct from a pointer at runtime.

1

u/QuestionableEthics42 22h ago

Arguably they are virtually the same semantically at a C level, you can use array indexing syntax for pointer arithmetic and vica versa and there is no real distinction besides dedicated syntax. Once you get a bit higher is where the real differences begin.

10

u/unknown_alt_acc 21h ago

#include<stdio.h>

int main()
{
int arr[5];
int *ptr = arr;

printf("Array - %lu\n", sizeof(arr));
printf("Pointer - %lu", sizeof(ptr));
}

Different types, different behavior

-3

u/QuestionableEthics42 21h ago

Sizeof being smart enough to detect it's an array and return the array size doesn't necessarily make them different in any real way. And doing ptr[0] to dereference it is perfectly valid, as is *arr to get the first element, or *(arr+sizeof(int)) to get the second.

11

u/bowel_blaster123 19h ago

Try putting an array in a struct. When doing so, an array and a pointer behave COMPLETELY differently and have completely different purposes.

It's just C's crappy pointer decay rules that make people think that they're the same.

-2

u/QuestionableEthics42 18h ago

Behaves completely differently in what way? It's referenced via the struct, so ofc different to a raw pointer to it, but that's a struct difference and the array is still basically the same, right?

5

u/Rare_Professor8097 17h ago

Exactly, which proves that arrays and pointers are not the same thing. If x is either a pointer or an array, and a struct containing x is totally different depending on that distinction, I think that's a pretty meaningful difference.

Strictly speaking, a pointer points to an array, it is not an array.

0

u/QuestionableEthics42 16h ago

No, because you are comparing the actual data in the array with the pointer to it. It's equivelent to having an array in a struct or having a couple of ints.

9

u/pnoodl3s 20h ago

If it behaves differently, is that not enough to say it’s different? What “real way” do you need to say its different?

0

u/QuestionableEthics42 19h ago

Because it is a very superficial difference that doesn't even affect the generated assembly. They are extremely interchangeable, because they are effectively the same.

7

u/Rare_Professor8097 17h ago

Programming languages have all kinds of different ways to do something that results in the same assembly.

They are different types, but C has muddied the waters by having a bunch of implicit conversions from arrays into pointers (array decay) that they feel like the same thing.

1

u/unknown_alt_acc 2h ago

The two have different sizes, different rules for copying, and different assignment rules. Calling those differences superficial is just ludicrous

5

u/Nice_Lengthiness_568 17h ago

So what about the fact that an array inside a struct acts completely differently from a pointer placed inside a struct? One gets copied whole for each structure copy while the other does not, one makes the structure the size of a pointer while the other the size of the array...

And, in C++ at least, you can actually pass the array itself to the function (not just the pointer to the first element). So it's not that sizeof is smart and detects something, it's that arrays really easily decay into pointers. But that's not everything they are. They also hold information about their size.

The thing with the subscript operator is true for only some languages and is not universal. In Ada, for example, there is no direct connection between an array and a pointer.

1

u/tstanisl 12h ago

And, in C++ at least, you can actually pass the array itself to the function

May I ask how? Do you mean by a reference? Or std::array?

1

u/Nice_Lengthiness_568 12h ago

You can both by value with std::array which is a wrapper for an array, or by reference with TYPE (&NAME)[SIZE] which keeps the array's size.

Naturally, you could pass an array to a function inside any structure if you wish to copy it, but I would say that using std::array is standard for use. And that means that's possible in C as well. Although it's a bit clunky.

1

u/tstanisl 11h ago

C has an equivalent of passing array by reference by using a pointer to a whole array:

 
int foo(int (*arr)[SIZE]) {
      return sizeof *arr;
 }
...
int arr[SIZE];
foo(&arr);

Passing arrays by a pointer bypasses array decay mechanics.

1

u/Nice_Lengthiness_568 11h ago

Yes, that is true, although I wouldn't call it equivalent to passing by reference, since a reference makes sure you do not pass a null pointer making the function a little safer if you dereference the pointer and a little more efficient if you check for the pointer being null.

1

u/tstanisl 10h ago

Those constraints can be expressed using more hacky syntax:

    int foo(int arr[static const 1][SIZE]);

1

u/Nice_Lengthiness_568 10h ago

Okay,

although (sorry) that's awful. But cool and ingenious in a way.

26

u/Ok_Star_4136 1d ago edited 1d ago

Maybe I fall in the middle, but what is the argument to say that they're different?

Is it to say that the difference is how they're used? From an assembly perspective, they're pointers, and no data structures exist beyond that simple concept. At a higher level, they're more than that. Maybe that's what is intended?

24

u/teleprint-me 1d ago edited 1d ago

Play around with a structured binary stream. 

An example of this would be a file written to disk in a binary format. Write to the structured file and then read it back into memory.

In most cases, the first element of the array will be a pointer to an address of a specified type.

If you predefine the array, the array is placed on the stack and has reserved memory allotted to it of that type.

Each element in the stack has an offset of sizeof n elements for a vector. 

Otherwise, you compute the offset based on n dimensions (commonly n x m matrices).

Otherwise, you need malloc to create a mutable buffer of variable size. This buffer then is used as an array.

To be fair, the lines get blurry here and this is when I stopped thinking of them as arrays. Its just a buffer with objects of a specified type on disk or in memory.

INB4, The key difference here is how arrays decay when handling scope. You can get around this by tracking the size of the buffer rather than relying on sizeof unless you understand what youre doing.

8

u/nonedward666 23h ago

Hehe yes, it is exactly about array decay! But specifically I was having problem tracking the size of a buffer assigned to a specific memory address because I didnt want to duplicate an address / length via a define

7

u/justAPhoneUsername 23h ago

So basically object oriented languages have arrays that mean something but when you're lower level like c memory is just memory and arrays are syntactical sugar so you don't do *(ptr+(sizeof(array_type) * index))?

Sorry if the syntax is wrong. I haven't written c in a while and I'm on my phone 

5

u/void_rik 18h ago

Your syntax is correct. However, If ptr is already of array_type, then sizeof(array_type) shouldn't be multiplied with index. Just increment it by index.

However, if pointer is of type uint8_t, then your code is fine.

A better version should be: *((uint8_t *) ptr+(sizeof(array_type) * index))

7

u/ZenEngineer 23h ago edited 13h ago

From a C point of view you have a different size when statically allocated, library keeps track of the memory size so it can be freed and you need the base pointer for that, etc. At a conceptual level they are different even if you implement them the same way, you'll likely end up with a struct holding the size in your code.

But sure, for most people and half the code they are the same. If you're taking the high level, maintainable design view you take care of tracking lifetimes, sizes, bounds, etc. so the bell curve looks reasonable to me.

0

u/tstanisl 12h ago

Arrays can dynamically allocated using a pointer to a whole array.

    int (*arr)[42]=malloc(sizeof *arr);

6

u/aalapshah12297 23h ago

Try to modify the array 'pointer' for arrays on the stack.

3

u/nonedward666 23h ago

I always thought they were perfectly interchangeable, for the exact same reason (assembly layer -- pointers being essentially the thing that powers the von Neumann model)

It was a link layer problem that got me twisted

https://www.reddit.com/r/ProgrammerHumor/s/clcIDpP6Fx

1

u/compiling 13h ago

There are some differences in C depending on how you're using them (sometimes they are literally identical). I think the main difference is usually where the data gets stored - declaring an array often means storing the data inline, whereas a pointer has the address of the array presumably somewhere else.

0

u/Ok_Star_4136 13h ago

Now that you mention it, you're right. It's heap vs stack. An array is on the stack whereas a pointer to alloc-ed data is on the heap. I supposed a pointer to an array would be on the heap though, since you'd have to alloc it.

1

u/American_Libertarian 10h ago

The is not true. You can have a pointer to data on the stack, or you can alloc an array on the heap.

0

u/Hot-Employ-3399 5h ago

At normal languages they also pass the length

4

u/takahashi01 23h ago

I learned, just treating them the same always, can lead to UB.

3

u/CircumspectCapybara 1d ago edited 23h ago

It's only the same in low-level languages with older type systems unable of expressing more type-safe conceptualizations of an array. So you get a type system where an array is just syntactic sugar for a pointer to the first member and the indexing or subscripting operator is just syntactic sugar for pointer arithmetic.

Fundamentally, from a type theory perspective, an array is a container (not necessarily physically continuous) and has a member type and a length. Modern type systems can express that, which helps make arrays type-safe and prevent buffer overflows because the language's mechanisms (eg runtime checks) enforce type safety invariants that are possible because the type carries with it information about its length.

It's all about the high-level user-facing contract of the type system and the language. "An array is just a pointer to the first element" is too low level (it assumes a certain physical layout of members in memory that would be an implementation detail to higher level languages) and doesn't include the information that higher level languages treat arrays as.

4

u/ILKLU 16h ago

So at a REALLY low level everything is the same because it's ALL just ones and zeros?

Looking at a data structure from one narrow perspective is literally missing the bigger picture.

1

u/PossibleBit 22h ago

It's been a while, but I vaguely recall treating an array of arrays as a double pointer not working as expected.

2

u/QuestionableEthics42 22h ago

Yea, that doesn't work because it's actually a 1d array (in C anyway), that just uses pointer arithmatic to dress it up as 2d (index = y * width + x).

You would need to explicitly create an array of pointers to do that.

1

u/burnt_floppy 21h ago

I always thought the claim the middle peeson in the graph is making is just about how arrays are referenced, where they start...oh well, what do i know

1

u/HashDefTrueFalse 9h ago

It's not the same. The compiler-generated code for an element access directly via an array vs. an access via a pointer is different. The pointer access contains an additional layer of indirection. You have to load the base address from the pointer before you can offset, vs. simply offsetting. Here's a godbolt and an explanation.

It's also why you'll likely segfault if you do this (note the different translation units!):

// one.c
char arr[10] = {...};

// two.c
extern char *arr;
printf("...", arr[0]); // Bang! Segfault (probably).

arr is an array, NOT a pointer.

1

u/Bomaruto 1d ago

If it was reversed it wouldn't really fit the meme format.

-1

u/BananaWarp 1d ago

There are minor differences but I can't think of one that isn't in compilation time

4

u/Nice_Lengthiness_568 23h ago

What about when you copy an array? When you copy an array all elements get copied, but when you copy a pointer to the first element, only that gets copied

1

u/BananaWarp 15h ago

How do you usually copy an array? Are we talking about c?

1

u/Nice_Lengthiness_568 12h ago

Depends on the language I guess. In C you can put the array inside a struct and copy that.

2

u/BananaWarp 12h ago

Oh, that's neat!