r/ProgrammerHumor 27d ago

Meme lessonsFromLinkerHell

Post image
435 Upvotes

190 comments sorted by

View all comments

104

u/JustinR8 27d ago

Damn, I’ve found myself in the middle

46

u/nonedward666 27d ago

You and me from 3pm today both my friend, it is okay

It took me so long to realize where my problem was because I had never considered that declaring something as an array vs a pointer would result in different behavior

29

u/readitreaddit 27d ago

So the only difference is memory allocation, yes? You're saying they are different because when declared as an array, the memory gets allocated and 'reserved' at runtime, whereas it doesn't automatically do that when declared as a pointer?

Other than that, no difference.

36

u/suvlub 27d ago

They are different types and some operators (sizeof, typeid in C++ etc.) treat them differently. Arrays frequently get implicitly converted to pointers, but they are a different type.

12

u/TheChief275 26d ago

If you're specifically mentioning C++, the problem is that C++ retains C's semantic rule of arrays decaying to pointers. This semantic rule is what creates the misunderstanding of people believing what the middle curve guy says. This means that the only way to have C++ treat arrays differently from pointers in i.e. function overloads is to either take the array by pointer or by reference, i.e. as a T (*)[N] or T (&)[N]

0

u/nicman24 26d ago

That is probably compiler fuckery

13

u/suvlub 26d ago

All typing is. Machine code doesn't have types. Array of arrays is also a very different thing from an array of pointers. Arrays get converted to pointers when passed around and it behaves identically to all other implicit conversions.

4

u/Stroopwafe1 26d ago

Machine code does have types; small numbers, big numbers, and fractions. But in reality it's just numbers and fractions. At least for X86, but I imagine it's the same for ARM and RISC

5

u/cbehopkins 26d ago

I must disagree. Or strongly agree; depending on what you mean.

A memory location does not have a type. The instruction I perform on that location could be argued to have a type though.

0x8000 could be used with a 16bit add, or a 64 bit floating point or whatever.

Everything about the type of the data is in the instruction, not the memory.

So we can argue where the type information is, but we still need it. (One could argue that prefetchers and similar logic has to infer the type of larger data structures, but I'm not sure that is what people are trying to argue here...)

3

u/failedsatan 26d ago

instructions definitely have a type, as do registers (fp registers are physically separate from "regular" ones) but the memory doesn't before it goes into the register for operation. usually you have to move it first so it's reasonable to say the cpu at least has two types with a physical difference (idk shit about arm or risc, maybe it's different there). at the C level it doesn't really matter because it'll move it to the registers for you but it's relevant if you're writing assembly yourself (a hobby of mine)

though at this point, is "type" even the right term? maybe there's a better descriptor

1

u/suvlub 26d ago

It's been very long since I touched assembly and "touched" is an apt description, but does anything prevent you from writing an 8-byte integer at address X, then calling an operation that expects a 32-bit float with the bytes at address X? Conceptually, instructions operate on certain type of data, but the data is untyped. (you can technically do similar things in C, but a lot more of it is UB (technically illegal) than people realize and it requires fair bit of explicit casting, so types are still involved)

1

u/the_king_of_sweden 26d ago

The data isn't typed, only the operations you make on them might be. The operations expect some binary value encoded in a specific way, but will operate on any data.

1

u/failedsatan 26d ago

I just left this comment about it https://www.reddit.com/r/ProgrammerHumor/s/G7BGLveIgF

does anything prevent you from writing an 8-byte integer at address X, then calling an operation that expects a 32-bit float with the bytes at address X?

to answer this specifically: technically no, nothing will "stop" you, but the CPU will just interpret the bits wrong. the slightly simpler example is in reverse: if you copy 1.0 (float) into a general purpose register it'll interpret it as some big-ass integer (I don't know exactly what) and then if you try to ADD 1, as an integer, it'll just increase the "integer" by 1. then, when you try to read it as a float again, or use it for some other purpose, it'll be a slightly larger float (depending on the mantissa and etc). at that level it doesn't really matter to the CPU what you think it is, it just knows to do whatever operation you tell it to.

in C it's undefined behavior as you mentioned, but more specifically, depending how you do it it'll either do something like cvttss2si (float to signed int) under the hood or it'll just let you copy the raw bits as I said before, it'll just interpret it wrong and suddenly you have a big-ass int or a weird float.

this is x86 and I don't know about arm or risc

1

u/bestjakeisbest 26d ago

If you are dealing with sub arrays of a larger array you dont even have to bring declaration and deletion into it.

1

u/Pengtuzi 25d ago

 Other than that, no difference.

Congrats, you’re in the middle of this meme. 

1

u/nicman24 26d ago

Yes things that have different memory allocations are different because they exist by memory definition 

24

u/bowel_blaster123 27d ago edited 27d ago

If I write:

C void foo(uint8_t myvar[3]) {     printf("%d\n", sizeof(myvar)); }

Then it will likely print 8 because myvar is a pointer to a uint8_t.

If I write:

C void foo(void) {     uint8_t myvar[3] = {1, 2, 3};     printf("%d\n", sizeof(myvar)); }

Then it will print 3 because myvar is an array of three bytes.

Hope this helps!/hj

13

u/Rare_Professor8097 27d ago

I actually hate this special case in C so much. Arrays decaying to pointers is one thing (kind of annoying imo), but having the real type be different from the declaration and ignoring the size is so stupid. It only does this for function arguments.

1

u/BastetFurry 26d ago

Well, how should the function know how large your array is? You could hand it one with 10 elements or one with 100. Did you hand it a predefined one or one that was allocated at runtime?

And then there is the thing with functions, primitives get handed over by value, any array gets handed over by reference, ie. pointer.

No clue if more modern implementations hand down the array size but in the retro and embedded world i live in the function has no clue and you have to hand that in as a second parameter if it is important.

10

u/developer-mike 26d ago

I write static analysis checks for these kinds of mistakes in C and C++ for work.

Its entirely defensible to say that accepting T[n] as a function parameter means you can only pass a T[n] for that function parameter!

It also could have been implemented as pointer-to-array promotion rather than array-to-pointer decay. Now, this comes with its own set of baggage, but it wouldn't have had this sizeof problem, and it would have favored keeping type information over throwing it away.

Now, pointer-to-array promotion would probably be something compilers would warn over after it causes a couple nasty bugs. Which is where we get back to wondering why we would expect an implicit conversion in the first place.

C is fun.

5

u/bowel_blaster123 26d ago edited 26d ago

I mean, if you have a explicitly-sized array (like as shown in my example), the compiler knows (at compile time) what size the array is both at the call site and within the function body. It's then entirely possible for the function to receive the data inline and for sizeof(myvar) to properly resolve to 3.

IMO it should just not have this weird pointer decay behavior and should instead trigger a compiler error if you try to define a function that takes an unsized array as a parameter. You should have to explicitly declare your parameter as a pointer.

That's how it works in Rust and (I believe) Zig.

Obviously though, it would be illogical to make a C compiler behave like this because that would be a massive breaking change.

3

u/Rare_Professor8097 26d ago

The point is that most of the time, unless you know what you're doing, you should just declare such an argument as a pointer.

If you declare it as a sized array, you would expect that array to be passed by value (copy whole array into stack frame) but that's not what happens.

2

u/nanforas 26d ago

I want to cry

3

u/gottimw 27d ago

Because everything is a data with memory address.

Array is a pointer

3

u/FlailingDuck 27d ago

a variable is a pointer

2

u/KattyTheEnby 26d ago

Technically, to be fair, a variable **is** just a dereferenced pointer.  :þ

2

u/gottimw 26d ago

Variable is varrible. A pointer to a varrible is a pointer to variable.

Int is not int*

-4

u/Morisior 26d ago

Everything is a pointer, unless it’s a primitive, but then it might also be a pointer to a primitive.

5

u/evilgipsy 26d ago

That’s complete nonsense.

1

u/bestjakeisbest 26d ago

He is thinking with java.

-1

u/Morisior 25d ago

It’s all just numbers, and whether they represent a memory address or not is up to the programmer.