Hearing that this is "a pointer VOID of any type information" was so enlightening.
@wick62962 жыл бұрын
When a friend asked me for a youtube channel that explains topics regarding C/C++, you were the first one that came in my mind. Keep up the good job!
@HansBezemer2 жыл бұрын
Although I tend to HATE C++, I do agree with being explicit. It shows intentionality. No, I didn't forget - I really wanted this to happen. It also allows for catching errors if you change types. On the other hand - the "void" type is badly chosen (like many keywords in C, e.g. "static" for functions). It would have been a LOT better if void would have been expressed in "address units". It would also have made sense of "void pointer arithmetic".
@sanderbos42432 жыл бұрын
It would've also been clearer to me much sooner if there was a more significant syntax difference between the "void" returned by a function and the "void *" type. If it were called "something *" or "thing *" it would've been a lot more obvious to me that "void" and "void *" are pretty different. Also, the fact that a "void *" can't be dereferenced while a "void **" can is something that took me a while to wrap my head around. I guess it is because when dereferencing a "void *" the compiler doesn't know how many bytes it should read since there is no type to tell it how many bytes to read, while when dereferencing a "void **" it knows how many bytes it should read, since pointers are all the same size.
@HansBezemer2 жыл бұрын
@@sanderbos4243 In the old days ALL functions basically returned "int". You could ignore it if you wanted, but still. Nowadays I can't "convince" GCC to do something with an explicit void return type. But this one still does the job (although the compiler complains loudly): #include test () { int a = 45; return (a); } int main (int argc, char **argv) { printf ("%d ", test()); } Even replacing this one does the job (returning zero): test () { int a = 45; }
@sanderbos42432 жыл бұрын
@@HansBezemer Wow, thanks for sharing!
@reinasama904 Жыл бұрын
@@HansBezemer This is very interesting! thanks for sharing
@RobBCactive Жыл бұрын
@@sanderbos4243firstly when void & void* were defined there was a lot of C written already, adding keywords to languages breaks programs, so needs a strong justification and the over loading reduced the impact. Before void* char* served but routines like malloc(3) would actually allocate a pointer aligned for any type. It wasn't confusing to C programmers precisely because void wasn't a type. In C you shouldn't cast void* when using it, it's actually an artefact of C++'s stricter type rules that force it and that can be a maintenance burden. Casts are NOT desirable.
@cps72572 жыл бұрын
Usually do not click the like button but this really helped me
@ajithanandhan70092 жыл бұрын
Thank god i found you. your videos are life saver,your so great. im a 24 year old Embedded software developer from india .happy coding
@DeniseNepraunig Жыл бұрын
I haven't looked into void pointers until I watched your video. Just hearing the name "void pointer" always confused me because of my immediate thought void = nothing. But from your video I learned that I should think of it as "no type information" and that makes total sense now. Thanks a lot! I enjoy your channel A LOT!
@cleightthejw22022 жыл бұрын
@Jacob Thank! you! You're audio is way better today.
@ahmadhadwan2 жыл бұрын
The best way to write C++ is to start each file with extern "C" {
@v01d_r34l1ty2 жыл бұрын
TIL you can write C applications that use C++ APIs by simply making a C wrapper using extern "C" { ... } and compiling the .c files to .o using GCC, compiling the .cpp files to .o using G++, and linking the .o's together using G++. That's actually awesome, I thought it was only a one-way compatibility C->C++.
@anon_y_mousse2 жыл бұрын
One thing I keep seeing is people saying that void* isn't necessary anymore because of any non-C language feature, but clearly they've forgotten that old systems still exist that need their code maintained and new systems can benefit from a good programmer using C as opposed to using whatever bloated feature their language of choice implements. Optimization is a lot easier for a compiler to do if each instruction you type in correlates to just a few machine instructions, or in some cases one. If you add a bunch of fluff, it gets harder for the compiler to determine how best to optimize your code, and makes the compiler slower, and its output more bloated.
@andrewnorris54152 жыл бұрын
You can use it to code a form of dynamic programming. A void pointer could point to one of several different types, which type it is being set elsewhere in another var. (Often within the item pointed to).
@stoneman2102 жыл бұрын
auto keyword exists
@chair547 Жыл бұрын
@@stoneman210 that's not what the auto keyword is for. Auto is for code that has a single specific type that you don't want to write out. Void* allows you to make a function take multiple types
@monochromeart73112 жыл бұрын
When you ran *make* at 1:46, it used *clang++* . But in 2:03 you ran *make* again (with no visible changes except for changing which file you're editing), and it used just *clang* . Did you change the makefile and cut it out, or is it a weird *make* feature?
@JacobSorber2 жыл бұрын
The makefile switches depending on whether the extension is .c or .cpp. I accidentally started editing the .cpp file, and then switched to the .c file.
@monochromeart73112 жыл бұрын
@@JacobSorber I'm wondering how does the makefile switch without you telling it explicitly?
@wanderingthewastes61592 жыл бұрын
@@monochromeart7311 he does tell it explicitly?
@monochromeart73112 жыл бұрын
@@wanderingthewastes6159 he just calls the make program through the terminal, I don't see where does he specify the extension?
@JacobSorber2 жыл бұрын
@@monochromeart7311 My makefile has two pattern rules. %: %.c $(CC) $(CFLAGS) $< -o $@ $(LFLAGS) %: %.cpp $(CPP) $(CFLAGS) $< -o $@ $(LFLAGS) One will generate a binary from a .c file. The other from a .cpp file. Elsewhere in. the makefile it specifies that I want to build "example" and "example2". So, if I modify example.c, then the first rule will be used to compile "example". If I modify "example2.cpp" then it will use the second rule to compile "example2".
@mariovrpereira2 жыл бұрын
Your content is really good! thank you so much
@khomo12 Жыл бұрын
Thank you!👍👍👍
@FadyMegally2 жыл бұрын
Would you please explain why ((uint16_t*)p)++ is not allowed ? uint16_t being just a stand-in for any type. I have a mem block that i wanted to fill with various types (uint16 followed by uin32 etc.) and instead of doing many p + sizeof(x) statements i was trying to be concise by using this *(((uint16_t*)p)++) = 0xC0FF ; *(((uint32_t*)p)++) = 0xAAC0FFEE; but that's forbidden apparently.
@NikolaNevenov862 жыл бұрын
so...technically a void pointer is a pure memory pointer? It doesn't know or care, how it should read the data at that memory address. While a type pointer will try to interpret the memory as a specific type?
@АлександрГольдварг2 жыл бұрын
Pretty much yes, but a pointer will always be interpreted as pointer in runtime until you try to dereference it. For example GCC does not allow to dereference void pointers, you have to cast it to some typed pointer.
@knofi7052 Жыл бұрын
That's really a funny one, with 'feed beef', 'coffee' and 'dead beef'...😂
@user-lv6qm3fj2z2 жыл бұрын
I was clicking all over my screen and here I am
@JacobSorber2 жыл бұрын
Welcome. I hope you enjoyed where your random clicking landed you.
@andrewporter18682 жыл бұрын
I already knew all of this; I just like your videos. When are you going to do a video on the worst parts of C that shouldn't exist: undefined behavior, and unspecified behavior?
@REALsnstruthers2 жыл бұрын
As annoying as undefined behavior can be, its pretty much the main reason as to why c ended up being such a fast compiled language. many architectures, especially back when c was first invented and still somewhat today, do not have consistent behavior. undefined behavior allows compilers to pick the best option it can for the architecture being compiled down to. Combined with how it took nearly 2 decades for the language to be standardized, meaning plenty of people were able to prod into every nook, cranny, and edge case the language could ever have, and its unsurprising how undefined behavior ended up being the way it is. I can admit that nowadays, in modern compilers, the fact they assume undefined behavior just doesnt happen by default and optimize assuming that such behavior is impossible can introduce a lot of annoying, subtle bugs.
@JacobSorber2 жыл бұрын
Good question. Some day. Not sure which one. Are there specific undefined behaviors that you want to make sure I include - whenever that video is born?
@AGBuzz1822 жыл бұрын
@@REALsnstruthers you're right, but "behavior defined by the underlying hardware" would have been more useful than "undefined behavior", since it would have stopped standards-compliant compilers from breaking code that according to all reason should work!
@andrewporter18682 жыл бұрын
@@JacobSorber None in particular, though mentioning GCC extensions and intrinsics would be worthwhile. Oh, actually, one that comes to mind is function pointer to state casts and vice versa. That's one of the more important ones.
@andrewporter18682 жыл бұрын
@@REALsnstruthers Undefined behavior doesn't need to exist. You can just define an abstract machine upon which all things are defined. That's in fact something that I'm doing with a certain project of mine. That a particular function within that machine be a blackbox (as all models reduce to blackboxes due to the subjective nature of models) isn't a problem, but undefined behavior is. If behavior isn't defined, neither the design nor the implementation are certain in which case your code is useless without reading every compiler's documentation (or forcing everyone to use GCC semantics like I do), or avoiding UB altogether (something virtually impossible which I learned very early on trying to make everything strictly conforming to ISO C). That said, GNU C extensions master race. While I'm at it, the other major flaw with C concerns its design as a language. It lacks sufficient constructs for decoupling state interface from state implementation, i.e. structs are strictly state implementation. I can't define an unordered list of struct members of a particular amount of information in bits (no strictly power of two) and then elsewhere, define that struct in a way that is optimal for my needs on a particular architecture like x86 or ARM. Likewise, it lacks sufficient constructs to decouple behavior design from behavior implementation, i.e. logic programming. I can't represent my program succinctly as a logical circuit in the form of code, or my algorithm as just an algorithm instead of an implementation of an algorithm. Pointers are great, but I don't like how C goes about their semantics. They're too much of a black box. I should be able to just treat it as a numerical value if I need it to, nor should its data be strictly of a logarithmic size. Being able to have relative pointers or simply offsets that I can use as pointers are something sorely missing. Now, it is possible to create relative pointers yourself, but as it lacks the syntax necessary to make it pretty, it could make code unreadable because of its semantics (*relptr would be syntactic sugar for *(base+offset) or base[offset]). I am effectively saying that pointer compression should be a built-in feature, and that these compressed pointers existing as base and offset should be part of the language or stdlib.h by default. What's better? One 64-bit pointer with 256 8-bit offsets, or 256 64-bit pointers? Note, of course, that I'm well aware pointers themselves already act as memory contexts and that you can have offsets into this according to the amount of memory associated with the pointer, however, the issue with heap functions is that they tend to be expensive in implementation. This is yet another reason why an abstract machine would fix this. Since memory can be perfectly modeled, an ideal memory allocator design which compilers and platforms are free to implement as they please can be designed such that the slightly-more-than-naive implementation is cheap requiring very little metadata overhead. If such were done, then I could just simply reserve memory as memory without strictly worrying if it's on the heap or the stack, or, generally, what kind of storage class it is. There would be one storage class only, and the compiler can optimally determine the implementation details with very little help from the programmer such as whether the data is on the stack or on the heap on a particular platform; however, at the same time, there aren't officially supported constructs for speaking directly with the platform nor the hardware except through extensions (such as ASM extensions from GCC, or introducing coupling by adding a platform header). Sometimes, it's unavoidable, but it can be minimized to a great extent. How many ways are there, after all, to manipulate state, and how many behaviors? Is there anything but transient or persistent state with lifetimes varying subjectively according to that state's purpose? Also, where's me asynchronous parallelism? Get rid of threads and synchronous parallelism; just let me have instruction (or function) stacks and define my own master-slave scheduling system. I could be getting 64 operations per cycle in certain cases provided the overhead of synchronous schedulers adding tens to hundreds of cycles worth of overhead to my high performance, highly optimized implementations.
@TaleTN2 жыл бұрын
printf("%p") seems useful, thanks! I always use something like "%016llX", but then I have to know the size, cast to some large enough int type, etc. The only downside with "%p" is that the output seems to differ between compilers, but for quick debugging I guess this doesn't really matter.
@taragnor2 жыл бұрын
Well honestly you'd always be using %p for debugging anyway. Printing memory addresses is never something you'd want to show the end user.
@KuroiMeansBlack6 ай бұрын
Is It Better to use template instead of void pointer in c++?
@xbz24 Жыл бұрын
May I ask how your vscode has the same font as msvs and what font is it!
@JacobSorber Жыл бұрын
You can change the vscode font in preferences. Google("What font does MSVS use by default?") --> "Cascadia Code"
@pierreabbat61572 жыл бұрын
Could you talk about intptr_t? I have an application (Bezitopo) where an edge can be split in three, and I need to point to the three pieces, so I took an edge pointer, converted it to intptr_t, and put the piece number in the bottom two bits, which are always 0 if the word size is at least 32 bits.
@antonwalters48572 жыл бұрын
Would have been nice to touch on Variant return types, used to return one or more different types from a function. Other than a generic pointer I believe variant types are the best use for void*.
@rustycherkas82292 жыл бұрын
"void *" made possible the generic implementation of qsort() that needs the facility of a comparison function that can only be provided with two generic memory addresses. It's one thing to "compare apples with apples", and quite another thing to compare oranges with oranges.
@rustycherkas82292 жыл бұрын
@@richardlyman2961 Not all arrays to be qsort()'d are arrays of ints...
@rustycherkas82292 жыл бұрын
@@richardlyman2961 What can I say? By extension, there's no need for 'const', either. We could even revert to Assembler and free ourselves of so many constraints... Have a nice day...
@69k_gold Жыл бұрын
Thinks like void pointers and function pointers are proof that C is not low-level or mid-level as most people call it
@d.sherman8563 Жыл бұрын
You could argue that typed pointers are higher level, a void pointer is nothing more than a memory address, no size information or anything. It is purely up to you, the programmer, to use it correctly, the compiler can’t help you.
@erbenton07 Жыл бұрын
Hey, what editor are you using?
@d.sherman8563 Жыл бұрын
That’s visual studio code
@Mantorp862 жыл бұрын
Great video!
@majinuub6192 жыл бұрын
Wait did you just cast a left hand operand? didn't know we can do this!
@SpamTheHorse2 жыл бұрын
I find void* interesting such as in an event callback system. You can make a function pointer such as (In c with using the "typedef" keyword) typedef void (*FunctionListener) (void*); or (In c++ with the "using" keyword) using FunctionListener=void(*)(void*); Then you can pass a void function with the void* argument that will be casted into any DataType* within that function, where that same DataType* has been passed into that function. You can also do something like void read_bytes(void* any_data, uintptr_t size); just to read bytes of any struct/class/datatype.
@sayori39399 ай бұрын
Hehe that's exactly what I've done with a linked list library i made
@janPolijan Жыл бұрын
As you're speaking of void in this video, you ought to add void when your functions don't take arguments, so `void hello(void)` instead of `void hello()`. Those two don't mean the same thing (at least in C).
@archibald-yc5le2 жыл бұрын
For me it's more difficult in C++, where void* is frowned upon. Right now I'm doing an implementation of vector, currently working on range insertion. This operation requires some low-level copying, so I thought using std::memmove would be justified here. Is this a correct assumption? When is it generally appropriate to use some c-style expressions in C++, are there rules?
@noxagonal2 жыл бұрын
void* has many problems in C++ but if you know the type it is supposed to be, do feel free to use it if you need it. Technically you're only allowed to cast it back to the type it was originally and not to the inherited parent, though if you stick with single inheritance (EDIT: AND don't use virtual parents or member functions), you shouldn't have any problems casting void* object to it's parent type. C-style cast is not recommended because it's "general" cast. It's like static_cast const_cast and reinterpret_cast combined. Using C++ style casts are more clear on intent, I'd recommend using static_cast as it does compile time check, C-style cast does not. C-style cast also does not do dynamic_cast which makes it unsafe to use with virtual classes.
@chri-k2 жыл бұрын
@@noxagonal what do you mean by ‘does compile time check - C-style cast does not’??? ( i know practically nothing about “correct” C++ )
@noxagonal2 жыл бұрын
@@chri-k Hey. So... You can cast a type to another using 4 different keywords, each keyword only allows certain types of casts. I'll try to summarize the best I can, I apologize for the wall of text... :P C-style cast has no safeguards, anything can be cast to anything without any limitations. If you change your types somewhere, the C++ style casts may help you catch a potentially hard to debug bug in your application. These are more important in C++ as it has templates and auto variables, which means that you might not see the final types you're casting. It's less of a problem for C. static_cast: Compile time evaluation. What it considers an error is casting away const, casting from incompatible types (ie. types that are not inherited from each other), casting from incompatible number of pointer indirection levels. What it's meant for is casting base class to inherited class (non-virtual classes), casting void* to actual type and casting simple stuff like int to float... Though you don't really have to do that last one. const_cast: Compile time evaluation. Only allows casting away the const, does not allow changing of the type. Useful when you need to ensure the type stays the same. reinterpret_cast: Compile time evaluation. Cast any pointer type to any pointer type without checking anything, this means that it will not adjust pointer value to point to the right base class location when dealing with inheritance, which is something that the other casts do. Cannot cast away const. Useful when you need to keep the exact memory location. Which is actually something C-style cast in C++ does not always do. dynamic_cast: This is a little more specialized, it does a runtime check to see if casting is possible or not, this is only meant to deal with situations where you only have the base pointer and you're not sure what the original type was, this allows you to check it. struct A { virtual Function(); }; struct B : A { virtual Function(); }; struct C : A { virtual Function(); }; B and C inherit from A. Lets assume: A * obj = new B(); You'll notice that the B object now lives in memory pointed by A* only. You can try and cast it to either B* or C* like this: B * b_obj = dynamic_cast(obj); // This will succeed and you now have a handle to original type. C * c_obj = dynamic_cast(obj); // This will result "c_obj" to be null, because "obj" is not type C. (do not use static_cast for this) This is useful if you want to store multiple different types of objects within a single array or types created in an external library and you don't know the actual type. You usually would keep track of the type anyhow but this serves as an extra layer of protection.
@chri-k2 жыл бұрын
@@noxagonal i have made a wall of text to counter your wall of text: your reply only made it more confusing: casts in C protect against incompatible types, and as you can see from the following example ( unless i am missing something very important ) the C-style C++ casts do too: class A {}; class B {}; template A f(T a) { return (A)a; // [2] } int main() { f(B{}); // [1] return 0; } In instantiation of 'A f(T) [with T = B]': required from here error: no matching function for call to 'A::A(B&)' [ the giant but useful list of notes that happen on a C++ compilation error ] so it seems that the only differences between it and 'static_cast' is that it sees casting from const to non-const an error, but why would that matter if a cast returns an rvalue? rvalues are not modifiable anyway; and that it sees casting from a pointer to a pointer an error when the "level of indirection" is not the same. 'const_cast' goes into the same category as 'static_cast' -- the "why does this exist i am definetly missing something very important" category, for the same reason -- you cannot modify an rvalue and, if you take the addess of a constant and drop the const off of the pointer, writing to it could cause a crash if the constant is compile-time ( and you have no way of knowing that if it is a parameter ). 'reinterpret_cast' sounds like a very useful thing for when you need to access the bits of a float or something like that. in C you need an additional variable or a union. 'dynamic_cast' and the explanation you provided with it went right over my head. i do not understand how such a situation -- where a pointer is pointing to a structure that you did not expect to be there -- can happen by any means other than a bug. that also goes into my evergrowing "why does this exist i am definetly missing something very important" section of C++ knowledge. so i am definetly missing something very important
@noxagonal2 жыл бұрын
@@chri-k I made a reply with a link to compiler explorer with some examples... seems like KZbin ate my reply. XP I'll see if I can dig out that link somewhere.
@jose61832 жыл бұрын
I actually like both C and C++, I understand their use cases. I'm not pedantic like other people who love putting down the older brother, just because he's old.
@goodguy78832 жыл бұрын
Can you please do a video on char pointer and const char pointer and their weird behaviour when passed in functions etc.
@JacobSorber2 жыл бұрын
Probably. Can you clarify which "weird" behavior you're most interested in?
@noxagonal2 жыл бұрын
I like your videos, pretty informative. Also I wanted to point out that in C++, technically the only valid cast from a void pointer is back to the original type. If an object has inheritance (EDIT: OR uses virtual base classes), you think that you might be able to skip a cast somewhere by casting the void to a base class object. This is possible for sure but only if you cast to the first inherited class. If you use multiple inheritance and cast void pointer to the second inherited class of the object, then you will not get the correct memory location to that base class. Easy fix for this is to stick with single inheritance only and you shouldn't have any problems.
@anon_y_mousse2 жыл бұрын
Or stick with C and write your "inheritance" code however you want.
@noxagonal2 жыл бұрын
@@anon_y_mousse It's logically impossible without abstracting this problem. The point is, inheritance is useful, and readily available. It's just another tool, you just need to know how to use it.
@anon_y_mousse2 жыл бұрын
@@noxagonal Actually, it's remarkably easy if you don't abstract it. Just think of each class as a struct and ignore the vtable, or don't ignore the vtable and learn how C++ does things internally. Either way, seeing the implementation makes it easier to understand the high level garbage that C++ throws at you.
@noxagonal2 жыл бұрын
@@anon_y_mousse Okay... Lets break it down a little. There's no vtable without virtual functions btw. Both of these have the exact same memory layout, size and alignment. They're identical in memory. C: struct X { ... }; struct Y { ... }; struct Z { X x; Y y; ... }; C++: struct X { ... }; struct Y { ... }; struct Z : X, Y { ... }; Notice in both cases, Y struct memory is stored after the X struct. You may imagine the Z is a culmination of all "parent" structs plus it's own members. X and Y cannot occupy the same memory within Z. Getting hold of the second "parent" is easy, in C you'll just take z.y and in C++ you'll just access the members directly. How about the other way around? Could you cast from Y to Z? C++ can, C cannot. Why? It's because C++ tracks this memory offset between Z and it's parents and applies it at compile time when casting. C also could track this offset too, but this mechanic does not have a dedicated interface for it in C. The problem I was describing in my original post is, if you cast from Y to Z via void pointer, you circumvent this automatic address adjustment and your memory will point to the wrong place. How to fix it... Well in C you can create a dedicated casting function like ( CastYToZ(y_address) ) that take the offset of the "parent as member" and subtracts it from the given address. It's fairly manual though and you can't make multi-level inheritance without chaining these. In C++, you either never cast void pointer to any parent type directly or stick with single inheritance only. Or you go one step forward and abstract by designing a different kind of system. In the end, you can do this in C to some extent, but C++ already has functionality designed for this. It's just that you may accidentally circumvent it, just like you may forget to call a function in C and your memory accesses are out of whack.
@bluesillybeard2 жыл бұрын
@@noxagonal Very well explained. As many have said, C++ is just C with more stuff (which can be good or bad, depending on who you are and what you are doing)
@marwankhaledkasem93352 жыл бұрын
thank you👍
@JacobSorber2 жыл бұрын
You're welcome.
@kristaqvin2 жыл бұрын
Nice vid
@AGBuzz1822 жыл бұрын
Imo, c++ has it backwards. void * is one of the few types that i think you shouldn't have to explicitly cast to and from. I think casting between types of different widths, and between integers and floating point numbers (and of course between pointers to different types of which neither is void) should have to be explicit though.
@fullfungo2 жыл бұрын
If void* cast was implicit, you could cast int* -> void* -> float* implicitly.
@AGBuzz1822 жыл бұрын
@@fullfungo it is implicit in c, and i don't find the situation you describe really happens by accident. Of course i think you should use void * very sparingly in general, but for the situations where it is useful, namely different kinds of generic interfaces, the implicit cast just makes your code that much nicer. This is my opinion
@fullfungo2 жыл бұрын
@@AGBuzz182 I see 0 use cases for void* in modern C++. If you want generics, we have templates. If you want unknown data, we have std::any. Void* is simply obsolete it modern C++. Edit: it is also very unsafe, which is one of the big reasons people avoid it.
@AGBuzz1822 жыл бұрын
@@fullfungo I'm sure that's all true, but plenty of c++ programmers don't use "modern c++". Even beyond that, i was really mostly talking about what i would wish for in an eventual c "replacement". Which i suppose makes it a bit misleading that i mentioned c++ in the first place; my bad.
@fullfungo2 жыл бұрын
@@AGBuzz182 I know. There are a lot of reasons people may use “old” C++: old codebase, compatibility, workplace requirements, etc. But this means that C++ committee would have to change “old” C++ standards, which they don’t do. Also, this would break “old” C++ code just as bad as updating to “modern” versions. So if you want this change, you need to update your C++ version; then why not update to C++20? And while doing this, the need for this feature disappears.
@dexter1172 жыл бұрын
If you're in C++ use templates instead of void pointers in order to structure your code by using better practical techniques
@leonm89062 жыл бұрын
I think it should be called undefined. A void* is a * to Memory of undefined type (alignment, stride and compiletime "usage" implication are unknown(like is it a char or a number or a float)). A function of type void is function that has not defined a return type. Meanwhile Void is commonly defined as "completely empty" which kind of works for void, but not for void* since as long as it points to valid memory, it never is "empty".
@HobokerDev2 жыл бұрын
I always thought of void as "no type"
@preyumkumar7404 Жыл бұрын
is the machine you are using 32bit? why does it give an 8byte address. 64bit machines should have 16bytes of address right ? 😢
@elclippo41822 жыл бұрын
If void pointers cannot be dereferenced, how does memcpy dereference its arguments then? 🤔
@stefan-danielwagner65972 жыл бұрын
Just a wild guess as I haven't read the memcpy implementation, but all the function cares about is the number of bytes, so it does an internal cast to unsigned char* and byte by byte copy can be performed (obviously, memcpy is written more efficiently than this, but this could be a working implementation).
@bluesillybeard2 жыл бұрын
Not certain, but I would do it just by casting it to a char* or byte* and copy it one byte at a time.
@noxagonal2 жыл бұрын
It casts it some 1 byte type that can be de-referenced, like char. Often the memcpy function results in some very high performance assembly however, so either it has been written in assembly originally or the compiler did it. Regardless the implementation looks a little different from what you might expect. If you're curious how to implement memcpy efficiently... You'll want to read and write minimum of 128 bits, up to 512 bits at once (see uint128_t, __m256i __m512i and related load and store operations) so you'll get the benefit of the bandwidth. But you'll have to make sure the memory is properly aligned for these vector load and store operations so you'd copy one byte at a time until you get to a position where memory is properly aligned for vector operations, then do the bulk of the copying that way, then copy the reminder byte by byte again.
@Ak4n02 жыл бұрын
Básicamente el tipo de los puntero sirve para decirle a C cuántos bytes debe tener en cuenta desde el valor del puntero: Si es un: char * : 1 byte int * : 4 bytes double * : 8 bytes etc. Es decir, sirve para la aritmética de punteros y la notación de array: Suponiendo (teórico): char *pc = 0; pc + 1 apuntará a la dirección 1. int *pi = 0 pi + 1 apuntará a la dirección 4. Lo mismo con pc[1] y pi[1]. void * es el puntero universal. Sin dimensión. Sólo es un puntero. Por eso no puede C derreferenciarlo porque no sabe qué tamaño tiene que darle al dato: si 1, 4, 8 o 1000 bytes. En cambio la aritmética de punteros si se lleva porque 0+1 = 1, trata como un entero la dirección del puntero y por tanto: void * pv = 0; pv + 1 apuntará a la dirección 1. De nuevo hay que recordar que, aunque se pueda hacer aritmética de punteros, y notación de array, sobre void * no se puede derreferenciar pues void no tiene información de tamaño al no ser un tipo de variable válido.
@user-lv6qm3fj2z2 жыл бұрын
No idea what you said, but good job, man, keep it up :D
@zyghom2 жыл бұрын
zgadzam się całkowicie z kolegą przedmówcą
@sanderbos42432 жыл бұрын
Translated to English: Basically the type of the pointer is used to tell C how many bytes it should take into account from the value of the pointer: If it is a: char * : 1 byte int * : 4 bytes double * : 8 bytes etc. That is, it works for pointer arithmetic and array notation: Assuming (theoretical): char *pc = 0; pc 1 will point to address 1. int *pi = 0 pi 1 will point to address 4. Same with pc[1] and pi[1]. void * is the universal pointer. No dimension. It's just a pointer. That's why C can't dereference it because it doesn't know what size to give the data: 1, 4, 8 or 1000 bytes. On the other hand, the arithmetic of pointers, if it is carried out because 0+1 = 1, treats the address of the pointer as an integer and therefore: void * pv = 0; pv 1 will point to address 1. Again, remember that, although pointer arithmetic and array notation can be done, void * cannot be dereferenced because void has no size information as it is not a valid variable type. .
@Hauketal2 жыл бұрын
The values you give are very specific to some implementations. The only size that is always used (by definition) is 1 for char.
@Ak4n02 жыл бұрын
@@Hauketal sí, es cierto. Aún así en algún sistema un char podría ser de 2 bytes (no me acuerdo dónde leí esto), pero es cierto que las definiciones de los tipos básicos de C estándar dejan mucho a la imaginación del implementador. De todas formas es para representar la idea y los valores son tomados de un sistema genérico.
@zyghom2 жыл бұрын
I was always thinking: how do you know if the piece of code is C or C++? now, from your video I realised: it is only a matter of ... which compiler you use (considering the code is valid for both: C and C++). Did I get it correctly?
@damnstupidoldidiot87762 жыл бұрын
If it's using C++ exclusive features, definitely C++, otherwise, probably C.
@JacobSorber2 жыл бұрын
Sort of. Most C code will compile using either compiler (or with minor tweaks-like the cast issue in the video). But, there are a lot of C++ features as mentioned in the other response that won't work with a C compiler. It's also a good idea to indicate (in the file extension) what language it's supposed to be (.cpp, .hpp, .c, .h), just to help others spend less time guessing.
@zyghom2 жыл бұрын
@@JacobSorber I am novice to both languages. I am writing some code and without real background I am actually not sure which language I am using - OK, I am not yet on the classes (which I understand are only in C++) but for any other code I write if someone asked me: "C or C++ you used?" I would have no answer.
@VTdarkangel2 жыл бұрын
@@zyghom one of the best ways I can think of is how are you allocating memory on the heap. If your using malloc, especially without a cast, and free, your likely writing in C. If you're using new and delete, you're writing in C++. Another way is you IO. Anything from the iostream.h, like cout and cin, is C++ whereas anything from stdio.h, like printf, is C. All that being said, all the C stuff will likely work in C++ so the C commands can go either way. However, the C++ specific commands will only work in C++, and, if you're writing in C++, you're likely going to be using atleast some C++ specific commands.
@zyghom2 жыл бұрын
@@VTdarkangel you wrote exactly what I thought: as long as I use the same commands that are in both languages, no way to say much what language it is. But, will compiling with clang or clang++ change anything then? It should not I would say...
@maxaafbackname55622 жыл бұрын
Nice, but you forget to tell why there is no cast required the other way around. For example in the call to free(). I thinks that makes it more clear why the cast was added to C++.
@MECHANISMUS2 жыл бұрын
Why?
@hc3d2 жыл бұрын
In my opinoin void pointers are annoying to work with, and char pointers make much more sense in all cases. Because evertime you use the subscript operator, with char[N] you can specify an offset of N bytes. but with void[N] you specify an offset of N*4 bytes, which adds extra mental gymnastics to the equation. Char pointers are already used for dealing with binary data, I'm not sure why the C standard lib does not use if for functions like malloc and free. Perhaps I am wrong, but that is how I feel about it.
@noxagonal2 жыл бұрын
It's mostly about declaring intent to the user. If you see a char pointer, you'd assume it's a string. If it's a void pointer you know it's any data.
@thisisnotok21002 жыл бұрын
My guess is void* is how python/php handle dynamic typing
@Sakuraigi11 ай бұрын
A pointer is a pointer. It is data type is irrelevant as it is going to be holding a memory address whatever its data type. The data type is only useful if you are planning to do pointer arithmetic. Hope I am right xD
@Sakuraigi11 ай бұрын
And also it is important if you plan to dereference it so the compiler can know what kind of value it should expect to avoid type errors ar run time
@Sakuraigi11 ай бұрын
It feels kinda useless to use void pointers in c++ if you are going to cast them in the end 😂
@Sakuraigi11 ай бұрын
I didn't understand the logic at 7:20 thought
@georgecop95382 жыл бұрын
Want to see if you're running on x64: sizeof(void*) * 8 == 64
@skeleton_craftGaming Жыл бұрын
I mean, I don't understand why people have such a hard understanding of what void pointers are... If void denotes a function has no type then it only logically concludes that a void pointer it would be a pointer with no underlying object...
@handlewithoutsuitcase Жыл бұрын
Play-Rewind-Play-Repeat, untill I get it.))
@AaronMatlock2 жыл бұрын
You shouldn't malloc like that into an int unless you want random hard faults that can move as you change code, try to add debug code, etc. due to memory alignment of malloc not being guaranteed on all platforms.
@xenobino84322 жыл бұрын
From malloc(3): "The malloc() and calloc() functions return a pointer to the allocated memory, which is suitably aligned for any built-in type."
@AaronMatlock2 жыл бұрын
What that means is platform specific and at times configurable.
@mrJety892 жыл бұрын
Having a void pointer is pointless.
@mrJety892 жыл бұрын
No, it's typeless.
@TranscendentBen2 жыл бұрын
I CRINGE every time I see an int assumed to be eight bytes. I started learning C before the ANSI C K&R came out, and an int was two bytes. I still write a lot of code for Arm Cortex M 32bit microcontrollers on which an int is four bytes. Arduino is still popular with twp-byte ints (the original Arduino uses an AVR 8-bit processor, but of course C has a minimum int size of 16 bits). Just yet another rant about the vagarities of the C language (and this one even applies to C++ as well). Get off my lawn!
@Firespectrum122 Жыл бұрын
"Nietsch-aaaaaaaay"
@kitanowitsch2 жыл бұрын
0xBABACECA
@lemmenmin76762 жыл бұрын
0xAB0BA
@Byynx Жыл бұрын
This video just removed all the void from void pointers.
@panjak323 Жыл бұрын
aVoid Void.
@Jkauppa2 жыл бұрын
its just a pointer bro
@skynowa26262 жыл бұрын
Больной человек
@ordinarygg2 жыл бұрын
Learn Rust instead. Thank me later.
@xequals-pc1wl11 ай бұрын
@ordinarygg That's the language where you have to put "unsafe mutable" on every line if you want to get anything done, right?