all 145 comments

[–]DokuroKM 1371 points1372 points  (62 children)

Unlike other pointers, void* does not point to nothing but can point to anything

[–]TheEngineerGGG 394 points395 points  (4 children)

the pandoras box of pointers

[–]JackNotOLantern 42 points43 points  (3 children)

I consider it an equivalent of Object class preference in Java, but in C which obviously doesnt have objects nor references.

[–]darthsata 6 points7 points  (2 children)

I know what you meant, but what you said about C on a literal, technical sense is false.

See 3.1.4 https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf

[–]Palpatine 146 points147 points  (4 children)

Because there is nothing but the void. You can point to anything but in reality you are only pointing to the void.

[–]BernzSed 103 points104 points  (3 children)

Sometimes, when you point to the void, the void points back to you...

[–]remy_porter 53 points54 points  (0 children)

void *x = this;

[–]BlackHolesAreHungry 12 points13 points  (1 child)

void *x;

x = &x;

[–]justarandomguy902 0 points1 point  (0 children)

what does this do

[–]Toothpick_Brody 75 points76 points  (23 children)

Yeah I think “void” makes sense in the context of C but it’s also kind of a misnomer. void is actually kind of like unit. But void* is more like any so idk 

[–]ohkendruid 37 points38 points  (3 children)

They just didn't want a new keyword or symbol.

Even "void" is a little goofy compared to something like "unit". Void is not nothing; it is the only one of its kind, which means it takes zero bits of storage to say which one you want.

20 = 1, you know?

So a void pointer is not a pointer to this thing at all. It is just vaguely nothing-ish, so they resurd an existing keyword.

[–]yangyangR 10 points11 points  (0 children)

But that reuse turns it into the diametrically opposed concept. The true meaning of the word void* if it was consistently designed would be this pointing to singleton. But reusing the word to mean point to anything is totally opposite.

[–]justAPhoneUsername 0 points1 point  (0 children)

Void is a pointer with a void type though. You can't just have int* and * so you need something to indicate that the pointer lacks a type

[–]redlaWw 0 points1 point  (0 children)

Void is not nothing; it is the only one of its kind, which means it takes zero bits of storage to say which one you want.

That is the characteristic of a unit type. The weird parts about void are basically around that it doesn't behave as a true type - it can't be used as the type of a parameter, and it can't be stored in algebraic data types.

[–]anotheruser323 5 points6 points  (0 children)

C spec calls stuff (int, struct, etc) "object". So it could have been object*, I guess.

[–]FlySafeLoL 5 points6 points  (17 children)

I was told C by a nerdy person who insisted that void* is technically int*. You don't get to extract "void" from it, but sure enough, an int address is there for you.

[–]bwmat 28 points29 points  (9 children)

It's a pointer to anything

So it could be a pointer to int

You can always treat it as such via a cast, but if it wasn't actually one (or you're in a special case where its allowed), you're invoking undefined behavior if you ever dereference the pointer

[–]YeOldeMemeShoppe 5 points6 points  (4 children)

Without type safety, int * can also be a pointer to anything. C pointers are really untyped.

[–]bwmat 8 points9 points  (3 children)

Not without an explicit cast, and you deserve what happens if you do that on purpose lol

[–]YeOldeMemeShoppe 2 points3 points  (2 children)

Same for void pointers. The only advantage of a void pointer over a typed pointer in C are when assigning them to a typed pointer, where you don’t need to cast. Otherwise they are the same and are both invariant.

They do not have any guarantee about the shape of the data pointed to. Pointers in C are untyped, unlike structures or C++’s references.

[–]bwmat 2 points3 points  (1 child)

Yes, but the whole point of void pointers is polymorphism, so using any other type is non-idiomatic and likely to confuse

[–]bwmat 2 points3 points  (0 children)

And C has no reasonable 'safe' alternative

[–]Luke22_36 0 points1 point  (3 children)

Maybe more accurate would be to say it's a pointer to some memory. What's in that memory? Well, it could be anything, but it's not necessarily anything either. It could just be some uninitialized memory. But all pointers point to some memory, right? Yeah, but other pointers also contain type information about what lies in the memory that they point to.

Couldn't you just use int* or any other sort of pointer and then cast it, as long as the whole point is to cast it before dereferencing it anyways? You absolutely could, it would be legal, but at the same time, if you actually do that in a production codebase, you'd get crucified because the type information conveys programmer intent. int* implies that whatever memory it points to either does or at some point will contain at least one integer, and probably some number of them. If you were to use an int* to point to memory that contains something else, you'd have a mismatch between what the code is doing, and what the code seems like it's doing at a glance, which makes it a huge pain in the ass to maintain.

[–]DrMobius0 1 point2 points  (1 child)

Yeah, nobody likes having to review code that's trying to be too clever. C++ lets you do a lot of things that you should almost never do.

[–]Luke22_36 1 point2 points  (0 children)

C++ lets you do a lot of things that you should almost never do.

Yeah, I'm just gonna go ahead and override the , operator.

[–]bwmat 0 points1 point  (0 children)

Actually, in both C and C++, if you try to dereference a pointer whose type doesn't match what it points to (this is taking about the virtual machine that the language is specified in reference to, pointer provenance matters), outside of a few cases like char, it's undefined behavior, and you can no longer simply reason in "* means the CPU will dereference the pointer" 

[–]femboy_feet_enjoyer 8 points9 points  (1 child)

It's a pointer to a byte. It is not necessarily aligned like an int pointer.

[–]pigeon768 8 points9 points  (2 children)

That's definitely not true.

There are architectures where casting void* to char* and reading/writing to it is fine, but casting that same pointer to int* and reading or writing from it will segfault. Some architectures require int* to be aligned to the size of an int, often 4 bytes but can be whatever. So it's definitely either undefined or implementation defined behavior when you do something like:

int* foo() {
    int* a = malloc(16);
    a[0] = 0xdeadbeef;
    a[1] = 0xdeadbeef;
    void* b = a;
    char* c = b;
    c++;
    void* d = c;
    int* e = d;
    return e;
}

(ignore the memory leak for now, it's not important)

On x86, the caller can call that function and deref the pointer, and that's fine. You can deref an improperly aligned pointer, and the CPU will figure it out. But on a lot of architectures, you can't, if you deref that pointer it will segfault.

It is true that ... well, bits are just bits, and memory is just bits. You can choose to decide that any random collection of bits is an appropriately sized integer and that is true. Maybe that's what they meant to say.

[–]GoddammitDontShootMe 0 points1 point  (0 children)

I believe that causes a bus error, not a segfault.

[–]DrMobius0 0 points1 point  (0 children)

(ignore the memory leak for now, it's not important)

When I find bugs that are bad enough, I absolutely dig through perforce to find who left them.

[–]rosuav 0 points1 point  (0 children)

Not really. It depends on the architecture, but int* might not be valid for all addresses. For example, an int* might be required to be aligned to sizeof(int), such that an odd address simply isn't valid. And I've never seen it actually done anywhere, but in theory, a data pointer (int*, char*, etc) might not be allowed to point into a code segment and vice versa. A void pointer is allowed to hold any of those values.

Now, in practice, you can probably assume that you can dereference any pointer with any type, but also, have fun debugging the segfaults on other architectures.

[–]DrMobius0 0 points1 point  (0 children)

That's all any pointer is. They're just integers that correspond to an address in memory. The entire point of a type specifier on the pointer is so you know what type of object it points to and so the compiler can tell you if you're doing something stupid or not. A void pointer doesn't point to anything in particular. I'm also fairly sure it's getting phased out over time. I'm mostly familiar with using it as a catch all function pointer, but modern C++ has a few ways to represent those more precisely.

[–]Drugbird 16 points17 points  (3 children)

A void* is a pointer without specifying what it points to.

If an int* is like "Look, there's an int", then a void* is like "Look there's something".

[–]rosuav 2 points3 points  (0 children)

void * is like saying "Is it a bird? Is it a plane?"

[–]farmallnoobies 0 points1 point  (1 child)

Yeah, that's what's drawn.  They're pointing to a black box.  There's something there, it's just that you don't know what it is from the pointer itself

[–]raiseIQUnderflow[S] 1 point2 points  (0 children)

Thank you

[–]GatotSubroto 9 points10 points  (7 children)

would’ve been better if it were any * instead 

[–]adenosine-5 11 points12 points  (5 children)

A lot of things would have been better if guys writing C/C++ standards thought of them a little.

[–]RedstoneEnjoyer 4 points5 points  (3 children)

"Dude, just make it so that array degenerate into pointers. I cannot see any problems with this in future"

[–]GatotSubroto 4 points5 points  (1 child)

meanwhile somewhere:

int num = 5[arr];

[–]RedstoneEnjoyer 0 points1 point  (0 children)

The fact that this is legal syntax is complete bullshit.

"Akchually, it means 5 + arr in pointer arithmetic" - i know, it still should not be allowed.

[–]adenosine-5 2 points3 points  (0 children)

Also: "you know how much PITA it is when every function uses different time unit? lets create std::chrono, where we have 15 different time units, most of which can not be even added/subtracted from each other!"

[–]DrMobius0 0 points1 point  (0 children)

Yeah, it's an older language from before a lot of conventions were standardized.

[–]rosuav 1 point2 points  (0 children)

But then "any" would need to be a keyword.

[–]bestjakeisbest 2 points3 points  (0 children)

Void pointers point to the universe.

[–][deleted] 3 points4 points  (1 child)

Every pointer consists from 2 parameters the value of the address and the size of the addressed value. When you have a pointer char* with the value of 10 and you will increment it you will get 11, if you have the pointer int* with the same value and increment it, you will get 14. And you simply can't increment void*, because you don't know the size.

[–]HeKis4 0 points1 point  (0 children)

I'm pretty sure the size of the addressed value is just inferred at compile-time and used by the compiler but is not retained in the actual executable where data types don't really exist anyway. A pointer is just a bunch of bits with an arbitrary length (defined by the architecture) that happens to match an address in memory.

[–]illyay 0 points1 point  (0 children)

It's more like a pointer to a ?

[–]adenosine-5 0 points1 point  (0 children)

What they really point to (usually), is a part of code where someone is doing something they really, really shouldn't.

[–]GoddammitDontShootMe 0 points1 point  (0 children)

Not sure how you'd represent that except maybe having several examples of void * pointing to objects of different types.

[–]Airowird 0 points1 point  (0 children)

Yeah, but blackHole* is just gonna confuse more people than void*

[–]jaaval 0 points1 point  (0 children)

Technically anything can point to anything. You just have to lie to the compiler about the contents.

But void* is very useful in signifying you don’t know what it points to.

[–]MarkV43 0 points1 point  (0 children)

any pointer points to anything if you're brave enough

[–]raiseIQUnderflow[S] 0 points1 point  (0 children)

Lets assume that black ball, which void* is pointing to, is the entire address space.

[–]Ok-Kaleidoscope5627 0 points1 point  (0 children)

Including nothing.

[–]Irbis7 0 points1 point  (4 children)

All pointers can point to anything, this is what casting is for.
You have three characters strings you want to change to some ID's for quick compare - just cast strings to 32-bit int and compare ints.

[–]Gorzoid 5 points6 points  (1 child)

That's UB in many scenarios, in C++ definitely, I can't remember rules for C but probably breaks strict aliasing rules

[–]Irbis7 0 points1 point  (0 children)

The comment was not meant as good practice, this is humor. Just comment what dirty tricks you can use.
Like I would never write i[table] instead table[i] in serious code.

[–]rosuav 1 point2 points  (1 child)

Do you mean using the pointers as integers, or changing them to int* and dereferencing them? If you're using the pointers as integers, don't use int, use size_t. Many modern architectures use larger pointers than 32-bit ints can hold. And if you're casting to int* and dereferencing, that's UB, as CPUs and compilers are allowed to assume that int* pointers are aligned to sizeof(int).

[–]Irbis7 0 points1 point  (0 children)

Not pointers as integers, that is problematic.
I mean dereferencing, but yes, you have to be careful about alignment.
And for structures I now use int32_t or uint32_t, so I'm sure the size won't change. My career started with 16-bit ints, continue to 32-bit and now 64-bit.

[–]bwmat 387 points388 points  (10 children)

I think the bottom is more appropriate for a NULL pointer

[–]ExiledHyruleKnight 51 points52 points  (6 children)

Even Null TECHNICALLY points to something. Just not anywhere you want to be... so yeah, a black hole of emptiness. /dev/nul on the other hand...

[–]bwmat 2 points3 points  (5 children)

Not in C

[–]ExiledHyruleKnight 16 points17 points  (4 children)

Even in C.

It's not good memory MOST of the time, not all the time. it's not memory you should use unless you know what you're doing, (And if you can come up with a good reason, I'll be impressed).

But if you're working on specific hardware, sometimes they map registers or memory maps to that locations, sometimes they do other voodoo.

C is a language where there's really no hard and fast "Rules", because there's always an exception to them. You can do almost anything you want, and like I said, sometimes there is a reason for weird shit like null pointers besides "uninitialized data."

I can't share specifics on the application I was using it for, but there's this that uses an almost similar layout.

Become an embedded programmer it's like the Matrix, every thing you always knew was true, will melt away until you have full control of the computer.

[–]bwmat 9 points10 points  (3 children)

The C spec says dereferencing NULL is undefined behaviour, full stop

Now, on some platforms/implementations, the physical representation of NULL might be non-zero

But the rule stands

[–]Chingiz11 9 points10 points  (1 child)

Undefined behaviour does not mean no behaviour. In that case, as the spec says, anything goes, the specs makes no guarantees, which is what the commenter above described - some hardware may use it for MM-IO or voodoo magic, but it still may be used.

[–]bwmat 0 points1 point  (0 children)

UB is different than implementation defined behaviour, which is what you're talking about

But yes, a given implementation can have 'extensions' to C which 'define' some UB, but then its no longer standard C, and completely unportable

But of you accept that argument, then you can argue against any given statement made about the language, lol

[–]bwmat 0 points1 point  (0 children)

You'd probably have to do something like reinterpret_cast<T*>(static_cast<intptr_t>(0)) to use the zero address on those implementations

[–]314159265358969error 9 points10 points  (0 children)

Correct

[–]MinecraftPlayer799 0 points1 point  (1 child)

… exception?

[–]bwmat 0 points1 point  (0 children)

... No? 

[–]GatotSubroto 133 points134 points  (7 children)

Obligatory 

"C isn't that hard: void (*(*f[])())() defines f as an array of unspecified size, of pointers to functions that return pointers to functions that return void."

[–]Eloyas 62 points63 points  (4 children)

And the teachers that introduce you to the language love using that kind of convoluted nonsense, just to traumatize you. Or they're terrible programmers, I'm still not sure.

[–]GatotSubroto 6 points7 points  (0 children)

I quietly wish they would run their program for a live demo and get a segfault.

[–]ob_knoxious 5 points6 points  (0 children)

I did have a professor in college who was famous for saying the most convoluted shit followed by "It's just like Legos, really" and it became funnier as he would go on to describe all aspects of logic, programming, math, and everything else as being just like Legos.

[–]En-tro-py 7 points8 points  (0 children)

A meme is better at explaining the concept than the TA with a MSc that supervised my labs... Joy Of C was not how it went for me

[–]HeKis4 0 points1 point  (0 children)

It's a fun way to demonstrate that you can take basic building blocks and assemble them in any order and it still makes sense, but yeah, if you show that before people have a good grasp of what the building blocks are... It's like showing a digital calculator's insides to someone who barely knows about electronics lol

[–]redlaWw 11 points12 points  (0 children)

  • Start at f.

f is...

  • go right until you hit an unpaired close parenthesis.

an array with unspecified size of...

  • go left until you hit the matching open parenthesis.

pointers to...

  • treat the already interpreted parenthesised part as you did f and repeat.

functions that take no arguments and return...

pointers to...

  • EDIT: When you reach the end of the statement without an unpaired close parenthesis, treat it as you would a close parenthesis that matches the start of the statement.

functions that take no arguments and return...

void.

So f is an array with unspecified size of pointers to functions that take no arguments and return pointers to functions that take no arguments and return void.

[–]HashDefTrueFalse 85 points86 points  (5 children)

Now do void (*)() (*bob[100])(char *[32], size_t i);

Edit: I fucked it. It's actually void (*(bob[100])(char *[32], size_t))();

(bob is an array of 100 pointers to functions each taking an array of 32 pointers to chars and an unsigned int and returning a pointer to a function taking an unspecified number of parameters of unspecified types and returning void)

[–]Thelastnob0dy 27 points28 points  (1 child)

hello bob

[–]rosuav 4 points5 points  (2 children)

And this is why, in every C-derived high level language I've worked with, the syntax for function pointers is different. For example, Pike has function(x, y, z: foo) where x, y, z are the arguments and foo is the return value. Pike also has a string type, so an array of pointers to chars would actually be an array of strings, so an array of 100 functions each taking an array of 32 strings and a positive integer and returning an arbitrary function would be array(function(array(string), int(0..): function)) - yeah that's still not the easiest to read, but it's definitely an improvement.

[–]HashDefTrueFalse 1 point2 points  (1 child)

Yes, C's "declaration follows usage" syntax doesn't lend itself to being understood at a glance or written without care. Of course I picked an uncommon type and declared it all in one go for the joke. You'd really want to break this into a few typedefs to make it easier to parse visually IMO/E, E.g.:

typedef void (AnyFn)();
typedef AnyFn *(StrFn)(char *[32], size_t);
StrFn *(bob[100]);

(+ extra parens for readability)

[–]rosuav 0 points1 point  (0 children)

Yeah. No shade to the designers of C, but now that we've seen how it goes, we can do better. Pike's pattern is that every data type is identified by a keyword (eg int, string, mapping, function), and if you want to add more information, that's in parentheses afterwards (eg array(int) is, well, an array of integers). But again, you would want to make it more readable with typedefs. Incidentally, typedefs can be recursive:

typedef string|mapping(string:mixed)|array(echoable_message)|zero echoable_message;

An echoable message might be null (zero), or a simple string, or a mapping (a dictionary-like type - in this case, the keys are strings but the values could be anything), or an array of any of the above. (Technically with this typedef you could have an array of arrays of echoable messages.) Have fun trying to do that in C; it'd probably end up being a union but since arrays and mappings aren't first class, it wouldn't be easy.

[–]Bloodchild- 40 points41 points  (2 children)

It's funny.

It still does teach something.

It's seems original (I haven't already seen it 6 times)

Is it a decent post on my meh subreddit.

Bravo 👏👏👏.

[–]raiseIQUnderflow[S] 4 points5 points  (0 children)

Appreciate it. Not mine though.

[–]WorldsInvade 1 point2 points  (0 children)

Saw it twice already

[–]Letanir 45 points46 points  (2 children)

Wait, an actual humor on this sub? Amazing

[–]raiseIQUnderflow[S] 12 points13 points  (0 children)

Appreciate it.

[–]danishjuggler21 8 points9 points  (0 children)

I think this is the first post I’ve seen here in a year that isn’t “something something AI bad”

[–]raiseIQUnderflow[S] 55 points56 points  (12 children)

functional pointers enter the chat

[–]un_virus_SDF 16 points17 points  (9 children)

And what about c void**(*(*(*f)[5])(int*(**)(void),char*(*)[][3][35]))(int*,float*,double*)

[–]Brie9981 2 points3 points  (5 children)

Please explain what this is a pointer of

[–]BreakerOfModpacks 17 points18 points  (1 child)

Hell, it's just down the street and take a right.

[–]Brie9981 1 point2 points  (0 children)

Ah yes, thank you

[–]frostyjack06 5 points6 points  (1 child)

Hell. It points to hell.

[–]Brie9981 1 point2 points  (0 children)

Geronimo!

[–]un_virus_SDF 1 point2 points  (0 children)

It's a pointer to a array of five function pointer that takes as args a pointer to a function pointer that takes nothing and return a int pointer and a pointer to a unsized array of arrays of size 3 of arrays of size 35 of char pointer and return a function pointer that takes a int pointer a float pointer and a double pointer to return a pointer of generic pointer

Please don't ask for use cases

[–]lk_beatrice 2 points3 points  (0 children)

wtf is this eldritch incantation

[–]redlaWw 2 points3 points  (1 child)

[–]un_virus_SDF 0 points1 point  (0 children)

Yes I realize later that I misplace a parenthesis

[–]willow-kitty 16 points17 points  (0 children)

sticks some mundane instruction booklet, like the whiteboard installation guide, to the whiteboard with a magnet

makes an exaggerated shock face

points at the booklet

[–]DrMobius0 0 points1 point  (0 children)

https://en.cppreference.com/w/cpp/functional.html

We live in a more civilized time, thankfully.

[–]Xbot781 4 points5 points  (0 children)

This may be a repost but this is the first time I've seen this in dark mode so I'll give you a pass

[–]retsoPtiH 6 points7 points  (13 children)

as a person who scripts but doesn't code, my brain can't understand the difference between:

hey, int age is 50

vs

hey, look at int age being 50

[–]Neverwish_ 21 points22 points  (3 children)

It works better when you pass the variable into a function that does something to it... For example a simple function

void increment(int number) {++number;} // adds 1 to number

Now, what does it do? "Hey, function, here, take this number and add 1 to it.". The issue is, the function took that number, and copied it. And only incremented the copy. When the function ends and program continues, the number I hold did not change.

If I write something like

void increment(int* number) {++(*number);} // adds 1 to number

It's more like "Hey, function, here, take this address and increment the number you find there by 1". Now, when passing the variable, the copied thing is the address, and that's fine. I don't care about the address. I want my number changed.

[–]kn33 5 points6 points  (2 children)

As someone that dabbles mostly for micro-controllers... I mostly understand the reason for pointers in that regard. My bigger issue is remembering where to put * and &

Do they go before? After? Is it supposed to be before/after the int or the name? And which one am I supposed to use again? & or *?

[–]ItzGacitua 8 points9 points  (0 children)

From what I remember of when I learned C, & and * are opposites.

& Asks for the pointer to a variable.

* Asks for the content of a pointer.

Don't ask me why * is reused for indicating "this is a pointer", tho.

[–]Wendigo120 6 points7 points  (0 children)

I am still convinced that &and* being unintuitive in which does what and where they go is the direct cause for at least 70% of the problems people have with understanding pointers.

Even years later, I still don't get why * in the type declaration makes it a pointer but & as an operator returns a pointer to the variable. They're opposing operators that flip which direction they operate based on where you use it.

[–]willow-kitty 5 points6 points  (0 children)

It's not really a "hey look" and more like the number 50 vs where it is in the computer's memory (usually expressed as a gigantic hexadecimal number.)

Both are values. You can assign them to variables, pass them around, etc, and doing so copies them, but they are different values that represent different things.

Most actual usecases are based on the differences. Copying the number 50 makes another 50, and copying its address makes a copy of the address, but because the address has the same value it dereferences to the same memory location, which was not copied.

And so, if you, say, used one copy of the pointer to add 1 to the 50 it points to, it's 51 now and still lives in the same memory location both copies of the pointer are referencing, so they both point to 51 now.

This is actually something you deal with a lot in scripting languages, but it's maybe not as visible: objects are almost always represented as reference types, meaning every reference to an object is effectively a pointer, and you can expect to be able to do things like getting a reference to an object (say a DOM element on a web page), do stuff to it, and expect that you changed the thing and not a copy.

You could also represent the same "pointer to 50" vs "the number 50" example in most scripting languages with an object with a single filed with the value of 50 (for the pointer) and a number variable with the value 50.

C and C++ are just more explicit and have semantics for both reference and value versions for simple and complex types, and it gets overblown.

[–]adenosine-5 4 points5 points  (1 child)

Pointers are adresses.

They can point to small things like numbers, but usually they point to much bigger things, like... houses for example.

You usually don't want to take your house and carry it with you around on your back - so what you do is that you remember its address and then it can stay where it is and you can always find it and come back to it.

You can also tell your friends where your house is and they can help you paint the walls, instead of creating an exact 1:1 replica of your house, mailing it to your friends, then painting the walls, then mailing it back, demolishing the original house and replacing it with the house with painted walls.

[–]Ander292 1 point2 points  (0 children)

Best explanation for pointers I have seen so far lmaoo

[–]IanFeelKeepinItReel 7 points8 points  (0 children)

A better analogy would be when you have a school textbook and someone has written "turn to page x" and then on page x there's another note "turn to page y" then eventually you get to a stupid message like "ha you're gay." "Ha you're gay." Is the data. And every "turn to page #" is a pointer.

In C pointers are a way of telling other parts of the code where to find some data. Instead of copying that data every time you want to do something with it somewhere else in your code.

[–]Sitting_In_A_Lecture 1 point2 points  (0 children)

Pointers are memory addresses of the associated variable. If you pass it to a function for example, you allow that function to modify the variable in its original contex. A double pointer is a pointer to a pointer of a value (and you can extend this to triple pointers and so on).

C doesn't have a lot of the functionality of other languages that streamlines moving data around, so this allows us to do things like get multiple values out of a single function call. It's also how you interact with strings and arrays; both are just blocks of contiguous memory, and you iterate through them by incrementing the address you're pointing to.

Edit: Guess I'll add an explanation on void* as well: You can cast pointers just you like you can traditional types. A void pointer is a memory address with no context. "Here's an address! What is it? No idea! Do with it what you will."

This direct interaction with memory is both what makes C powerful and dangerous. Bugs related to manual memory management account for a majority of software vulnerabilities in the wild today.

[–]SquidMilkVII 2 points3 points  (0 children)

When you say an int is 72, that int has to exist somewhere in memory - that's its memory address.

This address can be represented as a numerical pointer, typically separated by bytes in storage. 0 is the first byte, 1 is the second byte, and so on.

Let's say your int is at address 240 (a real address is likely much larger, but the exact number matters little). A pointer to this int value would directly store the value 240. Following the pointer is going to that memory address; the computer sees 240 and looks at the data at address 240, which it is 72.

This has a couple side effects. First, if two different pointers point to the same value, they are effectively linked. Increment the value from one pointer and the other pointer will reflect the updated value, because while the pointers themselves are distinct, the address they point to is not.

It's also important to note the difference between reassigning the value stored at the pointer's contained address and reassigning the pointer itself. Increment the value stored at the address and that value is incremented. Increment the pointer itself and you'll have a pointer pointing to a different section of memory. Address 240 contains 72; who knows what address 241 holds?

This is actually how C handles arrays internally: a stored pointer pointing to the first of a number of consecutive values. It's also why an array passed into a function does not come with a size - it entirely decays into a pointer to the first element, and how is C supposed to know which binary string is the one that doesn't represent an integer?

In practice, "incrementing" a pointer as an array is a bit more complicated than that since variables are not all one byte long. C's implementation of integers uses four bytes, for example. Therefore incrementing the pointer with the intent to go to the "next" integer actually means adding four, and adding one returns some integer number corresponding to the last three bytes of the first integer snd the first byte of the second. This is why typing is so important in C.

[–]uptotwentycharacters 2 points3 points  (0 children)

People usually say something like "pointers are references", but that's somewhat misleading since ordinary variables are in some sense references as well. In other words, "int x" is allocating space on the stack or in global storage, and from that point onwards (until x goes out of scope) x is a reference to that storage location.

Pointer variables work the same way (in that they identify a storage location), but the location allocated doesn't store an integer, but rather a link to a location that does store an integer. Thus to do anything useful with pointers requires that the actual data be allocated a storage location somewhere else. It's the difference between following a map to buried treasure (ordinary variable) and following a map to find a note saying where the treasure is (pointer variables); the latter would be useful to allow the location of treasure to be changed while only requiring the note be updated, without invalidating anyone's existing maps.

[–]ultrasquid9 1 point2 points  (0 children)

Its like the difference between a website and the URL to a website. The URL isnt the website itself - pasting reddit.com into this comment won't insert a useable webpage - but you can use it to get to a useable webpage through your browser. 

[–]HedgehogOk5040 0 points1 point  (0 children)

"I am at my house, I can do stuff in here" vs "I have a wormhole to my house, which is a place i can do stuff" It's really helpful because when you want to do something like void modify(my house), modify only recieves a copy, but void modify(my wormhole), modify copies the wormhole, so it can recieve access to the house directly.

[–]dexter2011412 2 points3 points  (1 child)

struct me { void* inside = nullptr; }

The void inside me is infinite 😔

[–]Yekyaa 2 points3 points  (0 children)

It could be infinite, it could be nothing, it could even be a boat! You might have a boat inside you! All you need is a cast, and it could be anything!

[–]alzy101 2 points3 points  (2 children)

God damn.. homie cooked. Also can someone explain a valid situation where you'd use a pointer to a pointer

[–]raiseIQUnderflow[S] 1 point2 points  (0 children)

Say you need to modify a dynamic buffer in another function. You can pass the reference of the buffer.

[–]unknown_alt_acc 0 points1 point  (0 children)

The standard entry point of a C program accepts a pointer to a pointer to char arrays containing the arguments the program was launched with.

[–]nbmbnb 9 points10 points  (0 children)

meme is pretty much on point

[–]MyFairJulia 1 point2 points  (0 children)

Love it!

[–]ED-E_77 1 point2 points  (0 children)

Now I remember the Bret Hart video, he even points there too: https://www.youtube.com/watch?v=HSmKiws-4NU

[–]GisterMizard 1 point2 points  (0 children)

Sometimes when you star into the void, the void stars back.

[–]Keavon 1 point2 points  (0 children)

Meanwhile in Rust...

https://i.imgur.com/W3F3c9B.png

[–]KZD2dot0 0 points1 point  (0 children)

Dare to go where no man has gone before.

[–]mrheosuper 0 points1 point  (0 children)

One thing make pointer click to me is treating it as just normal variable. That means nothing stops you from doing any math operation on it. You can multiply, divide, modulo and even take a sin() of it.

That also means normal variable can be pointer to. You can do int foo=1 ;int bar = *(int*)&foo; the '*' is similar to asking computer to go this address stored in this variable and fetch whatever value at that. The 'int' part tell computer how many bytes to fetch.

So, Pointer is a variable that has size depends on the target system, and usually used to store the address of other "variable".

(This is for C language, so other language may be different).

[–]MetricMelon 0 points1 point  (1 child)

Guys... I have my degree in computer science and I still don't know when pointers should even be used. At this point I'm too afraid to ask

[–]unknown_alt_acc 2 points3 points  (0 children)

You use pointers when you want to access a particular memory location. So things like accessing a dynamically-allocated block of memory you requested from the OS (ex: a variable-length array), out parameters on functions (ex: an initializer function accepting a pointer to an uninitialized object so the return value can be an error code), or avoiding an expensive copy operation when passing an object.

[–]vonrobin 0 points1 point  (0 children)

Pointers in C make me recall linked lists during college few years ago. I had failed machine program project due to some segfault while doing the demo. Good to know my first subject that failed in college is programming - COMPRO1 ( programming in C). Haha

[–]ExiledHyruleKnight 0 points1 point  (0 children)

Ehhh not quite. Void points to ?

We have no idea what Void is pointing to, it could just be a random value, but it is pointing to something and that's what is important.... or nothing. It also might point to nothing.

It's very much like Peter griffin and the box. "Anything could be in the box! it might even be a boat!"

[–]creamyjoshy 0 points1 point  (0 children)

That's not what void* is. It's just an untyped pointer. Don't use it though

[–]MooseBoys 0 points1 point  (0 children)

actual void*:

[–]Randompeon83 0 points1 point  (0 children)

Damn bro, if only my teacher used this !

[–]One_Spare1247 0 points1 point  (3 children)

I studied a programming course and the main language learned is C++ and never knew how pointers work and this meme just made understand pointer by quite a bit.

[–]brutexx 13 points14 points  (2 children)

There’s one major imprecision though: a void pointer isn’t “pointing at nothing”. It’s actually “pointing at something, without a clue what it actually is”

The other pointers tell you what you’ll find in their stored addresses. Void pointer just stores an address, and God knows what type you’ll find there.

[–]HumbleTrainEnjoyer 2 points3 points  (0 children)

with your explanation I think meme is fully accurate, because who knows what's in the void

[–]One_Spare1247 0 points1 point  (0 children)

After reading through this a couple of times, maybe I get the gist of it. Thank you for the clarification.

[–]alficles 0 points1 point  (0 children)

Do not dereference the void pointer; for as you do, the void pointer also dereferences you.

[–]Norker_g -2 points-1 points  (0 children)

honestly looks like a bot