r/ProgrammerHumor 2d ago

Meme bugIntroducedDebugging

Post image
1.1k Upvotes

120 comments sorted by

View all comments

344

u/AdBrave2400 2d ago

Is the mistake that they're allocating 1 byte and storing in a pointer to an int which is 4 bytes?

156

u/atanasius 2d ago

It's allowed to assign to a pointer if it's not dereferenced. Malloc always returns a valid pointer.

51

u/vishal340 2d ago

but accessing it will be issue right?

92

u/SoldRIP 2d ago

that's undefined.

2

u/nullpotato 1d ago

Undefined aka works fine on a dev build but explodes in release because all the debug info the compiler injects aren't there anymore.

40

u/backfire10z 2d ago

Likely not. Malloc guarantees that the passed-in size is the minimum number of bytes it allocates, but not the maximum. You’d likely get some minimum sized chunk, somewhere between 16-32 bytes depending on the system as far as I understand it.

41

u/atanasius 2d ago

Implementations are allowed to specify behavior that is otherwise undefined.

I looked up a C24 draft and it's actually stricter:

The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and size less than or equal to the size requested.

So a pointer returned by malloc(1) is not necessarily valid for int*.

-2

u/p88h 1d ago

Aligned means int sized at minimum.

The actual allocated size is much bigger anyways, but it will be a multiple of 8 bytes on a 64 bit system, and 'usable' part of that will be at least 8 bytes, and typically you will be able to read at least 16 without a page fault.

2

u/Deep-Piece3181 2d ago

That’s an implementation detail it’s still 100% UB

6

u/mckenzie_keith 2d ago

You are not allowed to de-reference a pointer after freeing it.

8

u/GoddammitDontShootMe 2d ago

Gemini added that return *x; that wasn't in the original code.

8

u/mckenzie_keith 2d ago

And then flagged it as a bug. It was so eager to find the bug that it added it into the code.

2

u/Niwrats 2d ago

who's gonna stop me?

24

u/EntitledPotatoe 2d ago

Malloc can return a null pointer if the operation fails. This can be the case if, afaik, for example, there is no more heap available and the OS is not capable of swapping or freeing some other memory for some reason

16

u/atanasius 2d ago

A null pointer is still valid to assign to a variable.

17

u/SeriousPlankton2000 2d ago

Also it's valid to call free(NULL)

4

u/meat-eating-orchid 2d ago

has it always been valid? I could have sworn that freeing a nullpointer was only valid in C++ but not in C. I couldn't find anything on cppreference on when this was introduced or if it has always been like this

9

u/GoddammitDontShootMe 2d ago

Very certain you could always free(NULL); in C. In fact, it's good practice to set pointers to NULL after freeing them to avoid issues like double-free.

3

u/EntitledPotatoe 2d ago

Very true, I thought you meant valid as in usable

28

u/CanardAuxEpices 2d ago

Uuuh... Actually 🤓🤓☝️☝️☝️ an int isn't 4 bytes, it's platform/compiler dependent. Assuming an int is 4 bytes will be in the majority of cases true, but it's not always the case

7

u/meat-eating-orchid 2d ago

technically, an int could even be just one byte if you are on an architecture where a byte (= smallest addressable unit) has 16 bit

5

u/CanardAuxEpices 2d ago

Yeah! But a char will always be one byte

2

u/mckenzie_keith 2d ago

We need to define terms more precisely. sizeof(char) will always be 1. But on some platforms, char is not an 8-bit type. It could actually be a 32 bit type. The c programming language does not (I believe) acknowledge or define what a byte is. But if it is an 8 bit integer type, then char will not always be one byte.

It may be better to say "octet" instead of "byte" in this type of discussion.

The implementation has to define CHAR_BIT in <limits.h>. CHAR_BIT is the number of bits in variables of type "char".

3

u/el_nora 2d ago

Fun fact, C does use the term byte all over the standard. And it is well defined. Byte is defined to be the unit that sizeof(char) is measured in. When the standard refers to bytes, they are referring to atomic divisions of memory in chunks of char.

Which does not necessarily need to be the same as an architectural byte. C requires only that their definition of byte is at least 8 bits (possibly more) and an addressable unit of memory - meaning it is some multiple of architectural bytes. Though on every platform still in use today, and not a historic curio, that multiple is 1.

1

u/mckenzie_keith 2d ago

So I am wrong about that. Oops. I guess then that one needs to define what they mean by "byte" when using it in an online discussion. The person I replied to is right, then, that char will always be one byte. But some people when they say "byte" are thinking of an 8-bit entity. Anyway, thanks for the info.

2

u/CanardAuxEpices 2d ago

You're brobably right. To be honest, I keep confusing myself with bytes and bit cause I'm french and here we use octet. Plus, my C classes starts getting quite far, but I just remembered the fun fact, I'll go check properly because it actually made me curious.

2

u/grencez 1d ago

It's kinda funny that C99 introduced int8_t and made it optional, but I'm sure there's some good reason. Basically if that type exists, then CHAR_BIT is 8. Also POSIX.1-2001 defines it that way, so most people can safely assume it.

119

u/mckenzie_keith 2d ago

De-referenced a pointer after freeing it. But the joke here is that the bug was not in the original code.

14

u/AdBrave2400 2d ago

I know

0

u/WisestAirBender 2d ago

The original code wasn't returning anything though. Isn't that a problem

17

u/mckenzie_keith 2d ago

No. main() doesn't need to return anything.

-4

u/critical_patch 2d ago

AFAIK technically it’s “undefined behavior” and will compile just fine

16

u/MegaIng 2d ago

main specifically is allowed to not return anything (the same way it's the one function that is allowed to never return)

0

u/Original-Ad-8737 2d ago

I learned recently that there is a kind of implicit return...

It will automatically assume that the return value is the last used variable or something like that.

Dont know specifics but i was weirded out why the compiler wasnt complaining about missing returns in some methods...

1

u/mckenzie_keith 1d ago

Only in main() as far as I know. All others the compiler should warn you.

3

u/nonlogin 2d ago

not at all

2

u/bloody-albatross 2d ago edited 2d ago

You're still on 32 bit? Are you doing a lot of embedded stuff?

Edit: I misread and thought they where referring to the pointer size.

14

u/AdBrave2400 2d ago

Isn't int 32-bit on most platforms and long long int is 64-bit?

18

u/Mateorabi 2d ago

That’s the neat part: it can depend!

sizeof() is your friend 

4

u/bloody-albatross 2d ago edited 2d ago

I read it as the size of the pointer being 4 bytes, but now I see you were referring to the int. AFAIK the size of int is specified as "at least" 32 bit in the C standard, and all 32 and 64 bit platforms I know indeed use 32 bit. Though the size of long (not speaking of long long) is different on different 64 bit platforms, IIRC. Windows uses 32 bit and Linux uses 64. Both use 64 for long long. I prefer to use (u)int*_t when I need to be sure about the size and I use int/size_t/time_t/... when I interface with functions that use those in their signature.

7

u/MattieShoes 2d ago

AFAIK the size of int is specified as "at least" 32 bit in the C standard

I believe it's 16 bit, though you're not likely to encounter a 16 bit int today unless it's a microcontroller or something.

3

u/SeriousPlankton2000 2d ago

Yes, long is 32 bit. A long long time ago we didn't need long long yet. I can still remember.

3

u/MattieShoes 2d ago edited 2d ago

And now there's __int_128t __int128_t, at least in gcc. :-)

So probably -170,141,183,460,469,231,731,687,303,715,884,105,728 to 170,141,183,460,469,231,731,687,303,715,884,105,727

That's what... undecillions?

3

u/bloody-albatross 2d ago

_ slipped, it's __int128_t :D

3

u/MattieShoes 2d ago

... you wrote the same thing? Yes two underscores at the start, but I wrote two underscores?

3

u/bloody-albatross 2d ago

You wrote __int_128t, I wrote __int128_t. These typos happen to me too.

→ More replies (0)

3

u/bloody-albatross 2d ago

Under 64 bit Windows. Under 64 bit Linux long is 64 bit. Don't know about macOS or other OSes. :D

4

u/yjlom 2d ago

The size of char (which is interchangeable with the size of a byte) is specified in old standards as large enough to a) be addressable without bit manipulations and b) hold the full C source character set, which need not be case sensitive and may rely on trigraphs. In practice, this comes out to at least 6 bits. In newer standards I believe it's specified as at least 8 bits.

The size of int short is at least and a multiple of that of char, the size of int is at least that of int short and a multiple of that of char, and so on.

So in theory, a conforming C implementation could have 6 bit ints.

2

u/bloody-albatross 2d ago

Right.

Oh and wchar_t is also weird. Its 16 bit under Windows and 32 bit under Linux.

1

u/conundorum 2d ago

wchar_t is messy, in large part because Windows was one of the earliest Unicode adopters. It's meant to be able to support any "wide" character, which means that it's supposed to be determined by the character pages the platform allows (and implicitly, should be 32 bits if any version of Unicode is supported, since Unicode is technically a 32-bit format regardless of encoding), but Windows adopting Unicode while it was still 16-bit UCS-2 and locking wchar_t down as a result meant that it was impossible for wchar_t to actually meet its requirements on Windows. ...Which meant that its minimum size requirement ended up being removed.

So, now its requirement is just "holds wide characters, check your implementation. Please don't use it."

1

u/conundorum 2d ago

Assuming 8-bit bytes, sizeof(int) is guaranteed to be a minimum of two, and intended to be the system's word size (which should technically be 8 for 64-bit platforms, but we're so used to 32-bit int that most platforms intentionally stagnate int at 32 bits & most processors have two native word sizes to accomodate), but can be anything higher. ILP64 models have 64-bit int, and there was at least one platform where all bytes were 64-bit and sizeof(char) == sizeof(long long) == 1, though, so it can get weird sometimes.

(Also, as a note, long long is required to be at least 64 bits. long is required to be at least 32 bits, and is meant to just be the 32-bit data type, but ends up being the design limitation fulcrum for most platforms; Windows is locked into 32-bit long because it needs to support 32-bit executables, and Linux is locked into 64-bit long because it needs to support punning pointers to long.)

1

u/P00lnoodl 2d ago

Yes but isn't the adress 8 bytes in a 64 bit system regardless of what it's pointing to?

1

u/mckenzie_keith 2d ago

x is a variable of type pointer to int. You can put x on the left side of a malloc() call. You can also free x. There is no bug up to that point.

1

u/crimsonroninx 2d ago

The mistake is using gemini for coding.

1

u/Duck_Devs 2d ago edited 2d ago

I believe it’s compliant to have malloc(1) return memory that can fit an int.

sizeof(char) is always 1; malloc works based on sizeof.

char is allowed to be anything >= 8 bits wide

int is >= 16 bits.

In theory, you could have char and int both be 16 bits.

Edit: this is why you should always do malloc(n * sizeof(type))

Edit 2: Wikipedia (C data types article) literally states that a platform could theoretically have all int types be 64 bits, for those who doubt me.

-5

u/[deleted] 2d ago

[deleted]

5

u/AdBrave2400 2d ago

The pointer is already allocated on the stack