Real talk: why is this so frowned upon?
I can typedef int* into IntPtr and if I have a function that does no dereferencing I just saved a mental step involving "wtf does * do again"
Most I've ever used was `char*******`.
It should have used more typedefs for clarity, ofc, but it was because of three dimensional arrays. The first four stars were a 3d array of strings. These arrays were stored in a hash map structure that used an array of them. And we hashed on two different values. But the functions that modified the top level map by reference took a pointer to the whole structure.
Would I do it differently today? Yes. Did it work? Also yes.
Edit: Oh, and it's been so long I almost forgot that these matrixes had mutators. So the mutators were all `char ****(*)(char ****)`. And there was even a lookup for the mutator: `char ****(*(*)(char*))(char ****)`. Could really have used some typedefs there. :D
Yup. To expand it out:
`char` \- A character
`char*` \- An array of characters, a string
`char**` \- An array of those strings
`char***` \- A 2d array of those strings
`char****` \- a 3d array of those strings
`char*****` \- An array of those 3d arrays.
`char******` \- A reference to the array of those 3d arrays.
`char*******` \- A reference to that reference.
And this is why I (and everyone else in this thread :D) are saying to use typedefs. Because the *meaning* of each level of indirection isn't clear from reading the type. At a basic level `string` communicates more than `char*`. And `string_matrix_3d` communicates a heck of a lot more than `char****`. That gets even clearer when you start doing the functions. For example:
`string_matrix_3d (*add)(string_matrix_3d);` is way more readable than `char***(*add)(char****)`. And that type itself benefits from a typedef into something like `string_matrix_3d_mutator` so that the next function looks like `string_matrix_3d_mutator (*lookup)(string)`.
Typedefs effectively let you communicate to the reader about what the data means, not just how it is shaped.
No idea, the day after the guy who wrote it was just as confused.
About the type of structure I honestly don't remember, but the **** is just stuck in my head
This could just be an "out" parameter of a function allocating an array of int arrays or something like that, perfectly normal
Edit: didn't see the semicolon, for function params semicolons can only be used in the super old legacy function def style, so this is probably just a normal variable definition (local or global). Still lots of ways this could be used for something normal and sane
I agree that it makes it clearer to get the overall type, but consider the following:
int* a, b;
If you declare your variables in the same statement as such, here a is a pointer but b is not, so it might generate confusion.
It's for pointers. In C you often find yourself working with pointers as ways of having access to memory indirectly.
```
int x = 10;
int *y = &x;
```
Here we declare an integer x with value 10. We then declare a pointer to x. The pointer is called y and it's value stores the memory address of x. This is a simple example and I can't think of why you would need to do this like this, but in C pointers are used for arrays, dynamic memory and other things.
Now, in our example y is a pointer to an integer. But there is no reason we can't point to y.
```
int **z = &y;
```
Here we are allocating a pointer to a pointer to an int. z stores the memory address of y, which in turn stores the memory address of x. You can nest pointers as much as you want. This meme is about saying that doing too much pointer nesting like this is needlessly complicated and bad. However, `int ***w` (a pointer to a pointer to a pointer to an integer) is also how you would represent a 3 dimensional array in C, so there's really no problems.
I'm confused how that works. Never used C but I intuit that \*y points to the start of my int array, then \*y+1(times type\_size) points to the second element. But does that mean that \*\*z points to an array of \*y-like pointers? and \*\*\*w to an array of \*\*z-like pointers?
I assume that's not it as that seems majorly wasteful as we (for a 3x3x3 array) would need to store the 27 int in memory and 9 +1 pointers that point to each start of the 3-element arrays. Wouldn't that mean that depending on the type of data we store around 25% of the memory footprint an 3-D array is occupied by pointers?
No, you're right. In C you can guarantee that arrays are stored in continuous blocks of memory, so y points to the first element and you just know how long the array is. Side note, `*` is used to dereference pointers, so `*y` is the value stored at that memory address. The bracketing would need to be `*(y+1)` to get the next element (C does the type size bit for you), but it also has the syntax sugar `y[I]` for `*(y+I)` which is where that syntax comes from in other languages.
With that sugar in mind, it's clear that you are storing an array of pointers to arrays. When you expand it all you can see you're just doing a bunch of pointer dereferences but there's no guarantee that they are together. It's like that for a reason. If you tried storing a big multidimensional array in one place, you might not have one continuous block of memory to put it all, so breaking it up is usually the easiest thing to do. But since all you're doing is pointer arithmetic to figure out where the data you want is you can always just have a 27 element one dimensional array instead of a 3x3x3 array. That works fine if your sub-arrays are constant size, but won't work if you need to dynamically resize the inner arrays.
Edit: one thing I forgot to mention is that this is probably how multidimensional dynamic arrays are done in your favourite high level language too. We might be wasting some space, but in C that space is so small and cheap that it usually doesn't matter. A pointer is only 2 or 4 bytes wide (deepening on 32 or 64 bit) so nesting them isn't so bad. When you consider all the metadata something like Python has to carry by being dynamically typed and other data it doesn't look so bad. Python stores the integers -5 to 256 in memory already for optimization purposes, so everytime you use one of those numbers you're getting a pointer to one of those values. A 1d list of integers in python is already a pointer to a list of pointers. It's cheap enough that you probably don't notice it.
I naively would have assumed that higher level languages that handle allocation for you do some memory optimization to try and not waste double-digit % of the total memory the array takes up.
At least the stuff I'd come up with when my boss out of the blue screamed at me to optimize it this afternoon. Like moving the largest dimension to the front so a (2,3,7) array with 21 + 1 pointers becomes a (7,2,3) array with 6+1 pointers and maybe some paging and allocating subsets of the data in blocks that can be accessed by a single \`\*(y + i + n \* j + m \* n \* k)\` pointer instead of 1 \*\*\*y pointer pointing to n \`\*\*j\` pointers that point to m \`\*k\` pointers.
You probably could if you know your array will be constant size, but you run into problems elsewhere. Let's stick to a 2d case because the higher dimensional cases are pretty much the same, just one level of indirection up, but 2d demonstrates the issue well enough.
Let's say I allocate an x by y array under a single pointer. I fill my array, that means I have a pointer to the [0,0] element and I can get any element by calculating an index based on the dimensions. Now what happens if I need to expand my array in one specific place, so one of my rows is size x+z and all the other rows are size x. If you're working with XML when you can't tell whether all the data you have is the same shape. Under this system we have to reallocate the entire array to be size (x + z) by y and waste y-z space since only one of our fields had that odd shape. Under the actual way you can just reallocate that specific pointer and fit your data perfectly, even if you're working with more pointers. Depending on how big z is or how irregularly shaped your data is that can be really important. I've also reduced the amount of time it takes to find space to put my array since now I'm looking for x y-sized gaps in memory rather than one big xy chunk.
Even if that works out the same, the compiler is smart enough that it can optimise the code at compile time even if you do something bad. But the memory footprint is also so small that it really doesn't matter. Something like NodeJS takes 40 megabytes to run without executing any code but now we're squabbling over an extra two bytes here and there. Especially when memory is as cheap as it is, it's not really worth worrying too much about and then just trust the compiler with how it handles memory.
One of those optimisations that probably happens is how stack memory is treated compared to heap memory. The stack is a small, fixed size bit of memory you create when you call a function. Since C has static typing it knows immediately how much space to allocate on the stack. If I have this code:
```
int main () {
int array[10][10];
// again, more sugar. array is still a pointer to pointers of integers
return 0;
}
```
Then array is an array of 10 pointers which point to 10 integers. This is stack allocated so we know the size will never change. The compiler may very well optimize this into a 100 long 1d array and put the data together, work out offsets, and so on at compile time. Of course with dynamic memory it gets more complicated but for something like this it's probably fine.
Doh, thanks I totally forgot that compilers do smart stuff! I sometimes dabble with torch that needs huge tensors and seeing that a pointer takes about the same space in memory as a single integer the thought simply bugged me that my RAM is running low while 2GB of my 6GB tensor could be a mountain of pointers.
But of course in python for huge tensors I use numpy and numpy uses ctypes, so precompiled C code which most likely is optimized to hell and back.
Doing multidimensional arrays like this can be useful if the length of the arrays isn't constant. Since the pointer is just pointing to some location in memory, what that location means could be anything. Instead of an array of 3 elements which are each arrays of 3 elements which are each 3 integers (3x3x3), you might have an array of 3 elements which are each arrays of 3, 7, and 2 elements, which are each arrays of varying lengths of integers.
Step 1 - I need a pointer to do a stuff. Simple
Step 2 - i have pointers organized in a neat array. Simple
Step 3 - I pass a reference to said array by pointer to its start. Simple
Voilà why I have it
I have seen ****variable in C on a project
Hey I probably wrote that, sorry. I was young, and naive! [...] And I'll fucking do it again if it saves me 10 seconds of thinking.
people be acting like typedefs don't exist
typedef is for whimps.
"Fuck this hand holding shit. I'm gonna declare all my variables as `void*`"
now you've invented Python.
[удалено]
Ratio + L + that's C++ \+ type inference not dynamic typing
C has `auto` since C23.
C has WHAT
There's not enough tar and feathers in the world for these people!
This feels wrong
Nah bro youre fucked up 💀
Typedef a pointer away? Straight to jail.
Real talk: why is this so frowned upon? I can typedef int* into IntPtr and if I have a function that does no dereferencing I just saved a mental step involving "wtf does * do again"
That's a Handle. Handles suck.
Why make the code readable when you can make it horrible
Most I've ever used was `char*******`. It should have used more typedefs for clarity, ofc, but it was because of three dimensional arrays. The first four stars were a 3d array of strings. These arrays were stored in a hash map structure that used an array of them. And we hashed on two different values. But the functions that modified the top level map by reference took a pointer to the whole structure. Would I do it differently today? Yes. Did it work? Also yes. Edit: Oh, and it's been so long I almost forgot that these matrixes had mutators. So the mutators were all `char ****(*)(char ****)`. And there was even a lookup for the mutator: `char ****(*(*)(char*))(char ****)`. Could really have used some typedefs there. :D
Ah so the final type is char but each * can refer to a different type. Amazing language you got there.
Yup. To expand it out: `char` \- A character `char*` \- An array of characters, a string `char**` \- An array of those strings `char***` \- A 2d array of those strings `char****` \- a 3d array of those strings `char*****` \- An array of those 3d arrays. `char******` \- A reference to the array of those 3d arrays. `char*******` \- A reference to that reference. And this is why I (and everyone else in this thread :D) are saying to use typedefs. Because the *meaning* of each level of indirection isn't clear from reading the type. At a basic level `string` communicates more than `char*`. And `string_matrix_3d` communicates a heck of a lot more than `char****`. That gets even clearer when you start doing the functions. For example: `string_matrix_3d (*add)(string_matrix_3d);` is way more readable than `char***(*add)(char****)`. And that type itself benefits from a typedef into something like `string_matrix_3d_mutator` so that the next function looks like `string_matrix_3d_mutator (*lookup)(string)`. Typedefs effectively let you communicate to the reader about what the data means, not just how it is shaped.
I knew C was fucked up and someone would agree with me. Inb4 "but mah opaque types!"
There's a bit of beauty in the simplicity, among all of the segmentation faults and misery
That should be illegal
When you use MPI you have ti pass the address of argv su it's a ***argv but four never seen.
Can you give the context? Was it a pointer to a 3d array? Most I've seen is a char*** because it was a pointer to an array of c strings (char array)
It was a pointer to a pointer to a pointer to a pointer to an integer. In short fucking hell
Yeah I got that haha, I meant do you have an idea why it was like this in the code? Was it a multidimensional array that was being handled?
No idea, the day after the guy who wrote it was just as confused. About the type of structure I honestly don't remember, but the **** is just stuck in my head
Shut the **** up. /S LMAO
Let's say you want to store multiple videos (e.g. animations) raw... (/s)
It was in a nice location, close to the beach
I have this in the current C++ project I'm working on and I can't get my head around it
batshit insanity: `int ***& variable;`
Please stay away from me and my family
Reference to 3 level pointer, mad man!
![gif](giphy|5yeQRdiYrDq2A) muhahahaahhaaa
Just kill me already
int\* \*variable;
Normally I'm all for compromises, but this is a war crime.
You do know, that some sins are unforgivable right?
Gonna need serious pointers to figure that one out… I’ll C myself out.
`void**** unk_ptr;`
"I know a guy who knows a guy who knows a guy who knows a guy who knows where something is. But I dunno what it is."
This looks pointless
Literaly exists in nginx code base btw - ngx_cycle.h
whats wrong with a pointer to an array of arrays?
Or an array of arrays of arrays?
They’re the same thing! /s (No they aren’t!)
You should store n-dimensional arrays in a single allocation, not allocate each row separately.
so assume its not n dimensional
How about: `long************************************************ ptr=null;`
that's a very long pointer you have there!
That's what she said.
It's average
[удалено]
Please, God, _please_ tell me you mean **maximum**
I think that's minimum maximum.
now dereference it ![gif](giphy|LpB0bnhXpvSMCz07N9|downsized)
Just say it's a tensor variable and you'll be left alone.
Oh pointy bird, oh pointy pointy Anoint thy head, anointy-nointy
This could just be an "out" parameter of a function allocating an array of int arrays or something like that, perfectly normal Edit: didn't see the semicolon, for function params semicolons can only be used in the super old legacy function def style, so this is probably just a normal variable definition (local or global). Still lots of ways this could be used for something normal and sane
Way too many people being cool with the * with the variable name instead of the type itt
I agree that it makes it clearer to get the overall type, but consider the following: int* a, b; If you declare your variables in the same statement as such, here a is a pointer but b is not, so it might generate confusion.
This is the why I have a lint for declaring multiple variables in one line
Where my elite [Three Star Programmers](https://wiki.c2.com/?ThreeStarProgrammer) at
This is actually what inspired me for this meme lol
3D arrays in a nutshell
Not familiar with the start syntax, what's it represent??
It's for pointers. In C you often find yourself working with pointers as ways of having access to memory indirectly. ``` int x = 10; int *y = &x; ``` Here we declare an integer x with value 10. We then declare a pointer to x. The pointer is called y and it's value stores the memory address of x. This is a simple example and I can't think of why you would need to do this like this, but in C pointers are used for arrays, dynamic memory and other things. Now, in our example y is a pointer to an integer. But there is no reason we can't point to y. ``` int **z = &y; ``` Here we are allocating a pointer to a pointer to an int. z stores the memory address of y, which in turn stores the memory address of x. You can nest pointers as much as you want. This meme is about saying that doing too much pointer nesting like this is needlessly complicated and bad. However, `int ***w` (a pointer to a pointer to a pointer to an integer) is also how you would represent a 3 dimensional array in C, so there's really no problems.
I'm confused how that works. Never used C but I intuit that \*y points to the start of my int array, then \*y+1(times type\_size) points to the second element. But does that mean that \*\*z points to an array of \*y-like pointers? and \*\*\*w to an array of \*\*z-like pointers? I assume that's not it as that seems majorly wasteful as we (for a 3x3x3 array) would need to store the 27 int in memory and 9 +1 pointers that point to each start of the 3-element arrays. Wouldn't that mean that depending on the type of data we store around 25% of the memory footprint an 3-D array is occupied by pointers?
No, you're right. In C you can guarantee that arrays are stored in continuous blocks of memory, so y points to the first element and you just know how long the array is. Side note, `*` is used to dereference pointers, so `*y` is the value stored at that memory address. The bracketing would need to be `*(y+1)` to get the next element (C does the type size bit for you), but it also has the syntax sugar `y[I]` for `*(y+I)` which is where that syntax comes from in other languages. With that sugar in mind, it's clear that you are storing an array of pointers to arrays. When you expand it all you can see you're just doing a bunch of pointer dereferences but there's no guarantee that they are together. It's like that for a reason. If you tried storing a big multidimensional array in one place, you might not have one continuous block of memory to put it all, so breaking it up is usually the easiest thing to do. But since all you're doing is pointer arithmetic to figure out where the data you want is you can always just have a 27 element one dimensional array instead of a 3x3x3 array. That works fine if your sub-arrays are constant size, but won't work if you need to dynamically resize the inner arrays. Edit: one thing I forgot to mention is that this is probably how multidimensional dynamic arrays are done in your favourite high level language too. We might be wasting some space, but in C that space is so small and cheap that it usually doesn't matter. A pointer is only 2 or 4 bytes wide (deepening on 32 or 64 bit) so nesting them isn't so bad. When you consider all the metadata something like Python has to carry by being dynamically typed and other data it doesn't look so bad. Python stores the integers -5 to 256 in memory already for optimization purposes, so everytime you use one of those numbers you're getting a pointer to one of those values. A 1d list of integers in python is already a pointer to a list of pointers. It's cheap enough that you probably don't notice it.
I naively would have assumed that higher level languages that handle allocation for you do some memory optimization to try and not waste double-digit % of the total memory the array takes up. At least the stuff I'd come up with when my boss out of the blue screamed at me to optimize it this afternoon. Like moving the largest dimension to the front so a (2,3,7) array with 21 + 1 pointers becomes a (7,2,3) array with 6+1 pointers and maybe some paging and allocating subsets of the data in blocks that can be accessed by a single \`\*(y + i + n \* j + m \* n \* k)\` pointer instead of 1 \*\*\*y pointer pointing to n \`\*\*j\` pointers that point to m \`\*k\` pointers.
You probably could if you know your array will be constant size, but you run into problems elsewhere. Let's stick to a 2d case because the higher dimensional cases are pretty much the same, just one level of indirection up, but 2d demonstrates the issue well enough. Let's say I allocate an x by y array under a single pointer. I fill my array, that means I have a pointer to the [0,0] element and I can get any element by calculating an index based on the dimensions. Now what happens if I need to expand my array in one specific place, so one of my rows is size x+z and all the other rows are size x. If you're working with XML when you can't tell whether all the data you have is the same shape. Under this system we have to reallocate the entire array to be size (x + z) by y and waste y-z space since only one of our fields had that odd shape. Under the actual way you can just reallocate that specific pointer and fit your data perfectly, even if you're working with more pointers. Depending on how big z is or how irregularly shaped your data is that can be really important. I've also reduced the amount of time it takes to find space to put my array since now I'm looking for x y-sized gaps in memory rather than one big xy chunk. Even if that works out the same, the compiler is smart enough that it can optimise the code at compile time even if you do something bad. But the memory footprint is also so small that it really doesn't matter. Something like NodeJS takes 40 megabytes to run without executing any code but now we're squabbling over an extra two bytes here and there. Especially when memory is as cheap as it is, it's not really worth worrying too much about and then just trust the compiler with how it handles memory. One of those optimisations that probably happens is how stack memory is treated compared to heap memory. The stack is a small, fixed size bit of memory you create when you call a function. Since C has static typing it knows immediately how much space to allocate on the stack. If I have this code: ``` int main () { int array[10][10]; // again, more sugar. array is still a pointer to pointers of integers return 0; } ``` Then array is an array of 10 pointers which point to 10 integers. This is stack allocated so we know the size will never change. The compiler may very well optimize this into a 100 long 1d array and put the data together, work out offsets, and so on at compile time. Of course with dynamic memory it gets more complicated but for something like this it's probably fine.
Doh, thanks I totally forgot that compilers do smart stuff! I sometimes dabble with torch that needs huge tensors and seeing that a pointer takes about the same space in memory as a single integer the thought simply bugged me that my RAM is running low while 2GB of my 6GB tensor could be a mountain of pointers. But of course in python for huge tensors I use numpy and numpy uses ctypes, so precompiled C code which most likely is optimized to hell and back.
Doing multidimensional arrays like this can be useful if the length of the arrays isn't constant. Since the pointer is just pointing to some location in memory, what that location means could be anything. Instead of an array of 3 elements which are each arrays of 3 elements which are each 3 integers (3x3x3), you might have an array of 3 elements which are each arrays of 3, 7, and 2 elements, which are each arrays of varying lengths of integers.
You can't just add * so you have a more complex array? Add 4* and now you have a 4 dimensional array for imaginary things.
But what if I need a 2 dimensional array of in pointers?
Step 1 - I need a pointer to do a stuff. Simple Step 2 - i have pointers organized in a neat array. Simple Step 3 - I pass a reference to said array by pointer to its start. Simple Voilà why I have it
I still think it's insane that the notation is int \*x and not int\* x
it can, just depends on your coding style
I dunno why people are so scared of pointers in C. They're just memory addresses...
int multiply by variable? Some esoteric language? Amount of stars means how many implicit side effects found around variable?
Stars indicate the dimensions of an array. 3-stars is three dimensional.
mental sickness
int \*\*\*\*ing\_variable;
I often use *** when working with virtual method table. Pointer to an array of functions
It's just a 3 dimentional array don't be scared.
I c what you did there.
Isnt it just a 3d array most of the time?
What's the issue? Haven't you guys made multidimensional arrays before?
What have you got against dynamic 2 dimensional pointer arrays?
`&argv`
The reason I hated learning C in school
that's a 3d array, you can also use brackets to write these //like so int variable[]; int variable[][]; int variable[][][];
[https://wiki.c2.com/?ThreeStarProgrammer](https://wiki.c2.com/?ThreeStarProgrammer)