Dynamic Arrays in C

Поділитися
Вставка
  • Опубліковано 21 лис 2024

КОМЕНТАРІ • 325

  • @joshuawilliams5306
    @joshuawilliams5306 10 місяців тому +134

    There is a slight oversight here relating to data alignment.
    If the data type requires a larger alignment than that of the platforms' pointer, then the data in the array will be misaligned. This could result in either a performance penalty or an exception.
    I'm not much of a C programmer, so I'm unsure of a completely portable solution but as of C11 you can use _Alignof to figure out how much patting to add to the header.
    It would also be good to allow a user to manually specify sticter alignments, incase they want to do something like declare an array of a certain type but plan to apply vector operations to it which require stricter alignment.

    • @DylanFalconer
      @DylanFalconer  10 місяців тому +43

      You're right, that's actually a pretty big oversight! Thanks for the comment - I'll have to update the written version.

    • @joshuawilliams5306
      @joshuawilliams5306 10 місяців тому +3

      Looking forward to checking out your final solution

    • @DylanFalconer
      @DylanFalconer  10 місяців тому +12

      I'm just on my lunch break, so I can't test this out, but I'm thinking for x64, we just add an extra 8 bytes of padding to make the whole header 32 bytes. That way, the array will always be aligned even for SIMD unless I'm mistaken

    • @joshuawilliams5306
      @joshuawilliams5306 10 місяців тому +5

      Well on x86-64 specifically I think a static padding approach would require 64 bytes given that types like __m512 exist.

    • @joshuawilliams5306
      @joshuawilliams5306 10 місяців тому +4

      Most intel CPUs dont currently support AVX-512 but I believe AMD has since zen 4.

  • @wChris_
    @wChris_ 3 місяці тому +2

    I would define the Header like so, to avoid any alignment issues:
    struct array
    {
    size_t capacity;
    size_t length;
    T data[];
    }
    you would then initialize it all the same, except you would return &h->data;
    Also you can use designated initializers: h = {.capacity=capacity, .length=length}; This is more efficient since this allows padding to be explicitly overwritten with 0.

    • @Aleh_Loup
      @Aleh_Loup 2 місяці тому

      Nice, it also makes it a conformant Flexible Array

  • @Andrew90046zero
    @Andrew90046zero 9 місяців тому +25

    Today I learned that “unordered remove” is a thing that I feel i should’ve known a long time ago.

  • @ahmedabuharthieh579
    @ahmedabuharthieh579 10 місяців тому +8

    Coming fresh off of reading the K&R book on C, This is a very informative and entertaining video. Great stuff

  • @g0mnster296
    @g0mnster296 10 місяців тому +84

    i beg you to make a beginner-intermediate C tutorial, you are really good at explaining things.

    • @grimvian
      @grimvian 8 місяців тому +2

      I hope not, because there are so many C tutorials and some of them are really good, but I think there are a lack of C videos on a intermediate level for C learners like me. :o)
      I really like videos, that are not using disturbing background music and no 'clever' action movie editing, but just starts like this great video although I must admit, that I use C99. :o)
      Macros - so far I know, they should be a 'nightmare' to debug? The only macros I use are __file__ and __line__
      I assume a C++ compiler must be gigantic compared to a C compiler...

    • @DatBoi_TheGudBIAS
      @DatBoi_TheGudBIAS 2 місяці тому

      ​@@grimvianby macros u mean predefined macros like __FILE__ and __LINE__, or macros that urself define?
      They can be a pain if u don't remember them lol

  • @AleksyGrabovski
    @AleksyGrabovski 9 місяців тому +1

    If you need a dynamic array just to have a growing string, then you don't need any structs and allocators and anything complex, you can just use regular old char* and write a function that will reallocate each time you add something to that string: `#define APPEND(Str, Fmt, ...) asprintf(&Str, "%s" Fmt, Str, __VA_ARGS__);`

  • @mamertvonn
    @mamertvonn 9 місяців тому +1

    Implemented one of this before with excessive use of macros to replicate templating. So, I can easily implement an 8t, 16t, 32t and 64t dynamic array

  • @whym1here
    @whym1here 10 місяців тому +5

    I use to implement the vector by 1st method in uni. I will try to implement the 2nd method as it seems nicer to me. Thanks for teaching new stuff. Hopping for more videos on complex data structures such as hash tables and balanced binary search tree in future (in C obviously 🙂).

  • @nyyakko
    @nyyakko 10 місяців тому +10

    A few months ago I played around with implementing a generic and type-safe vector in C and gotta say, it was not that much fun. Well i mean, it was fun for the learning, but the hacks you gotta do with the preprocessor ... is quite something.
    That being said, implementing a simple non-generic and type safe vector in plain C is really sweat. Just like you showed in the video.
    Honestly i like the tools C++ offers you, specially in the realm of generic programming. Templates can be quite powerful if you learn to not overuse it.

    • @nyyakko
      @nyyakko 10 місяців тому +1

      I suppose my point is that every language has its advantages, specially the system's one. I enjoy doing my things in C++, but also enjoy to play around with simple C from time to time.

    • @shinobuoshino5066
      @shinobuoshino5066 10 місяців тому +4

      @@nyyakko except nothing is ever simple C, doing trivial things requires walls of C code that more often than not is so unreadable that not even splitting a basic problem into 50 functions will make it any more clear, and all because midwits think that error that happens once in a blue moon should be checked everywhere and all the time, taking up like 75% of all code with nothing but if statements checking if function call failed.
      Whereas in C++ you only need to check at lowest level and throw exception on error and it will bubble up when it happens, except I've never seen an error ever happen in my entire life. I quite literally NEVER had write(2) or something like that fail. And guess what, if C++ exception isn't ever thrown, cost of exception is 0, now instead of checking on every level of call stack, you only check once, when basic write(2) fails, performance won't be a problem either, because chances are, if it was a write to external disk, it fucking died, no amount of performance will revive it.
      Files that are expected to exist also should have exception thrown over return value, return value should be reserved for files that may or may not exist, especially if there's a lot of them, then exceptions can incur a heavy cost and it's worth going out of your way with if else in some limited scope over exceptions.
      In C you don't even have a basic choice like this, it's if else if else if else all the way down and a waste of everyone's time.

    • @AkimboOfficial
      @AkimboOfficial 10 місяців тому +2

      @@shinobuoshino5066 those if statements are pretty useful when you do encounter an unusual error though!

    • @shinobuoshino5066
      @shinobuoshino5066 10 місяців тому +1

      @@AkimboOfficial ah yes, gotta clutter up 75% of my code with pointless if elses in case once in a blue moon error that never happens will happen.

    • @enzoqueijao
      @enzoqueijao 10 місяців тому +6

      ​@@shinobuoshino5066Ignoring edge cases is they key to writing good and safe software. After all, there's no problem if it's working on your machine

  • @darkcaste
    @darkcaste 10 місяців тому +8

    A short list:
    * exposing yourself to type errors and memory leaks, to save 0.9 seconds per compile. Lose hours to save a minute or two.
    * you can't share the array pointer because it gets reassigned, even passing the pointer into a function by value is dangerous.
    * malloc allocates on the heap, not the stack
    * data alignment, but easily fixed
    * you can use both C and C++, it's the same compiler, just pick the zero-overhead abstractions that work for you.
    * C++ templates can do what you're doing with macros, but with type safety, and operator overloading, a[x]
    template
    struct array {
    T *data;
    // ...
    inline T& operator [](size_t i) {
    return data[i];
    }
    };

    • @Aidiakapi
      @Aidiakapi 10 місяців тому +1

      I'm not a big fan of the proposed implementation either, due to the ease of misuse (no type distinction between ptr, array, and dyn array, dyn array append causes all pointer invalidation, not just element pointers), that said, your argument is really a strawman:
      * exposing yourself to type errors and memory leaks, to save 0.9 seconds per compile. Lose hours to save a minute or two.
      C++ compilation time, especially when using things like the STL, aren't just adding a few seconds here and there. I've worked on C++ projects that took over 45 minutes to build with minimal optimizations enabled. It's the same problem that LTO has, on any larger project, it's simply too slow to be usable, we saw build times over 24 hours, on high-end builders with 256GB of RAM, which it exhausted.
      For larger productions, using C++, to get some of the type safety, but without its STL, makes a lot more sense than just using C, but at the same time, C++'s compilation speed is not about saving small amounts of time.

    • @darkcaste
      @darkcaste 10 місяців тому +1

      @@Aidiakapi that isn't a strawman, I was responding to what he said. As for previous projects you've worked on, you know that's a red herring, without specifics of how it was structured, etc. Your LTO anecdote makes it sound like a monolith... what problem were you expecting to solve with that anyway?

    • @marcossidoruk8033
      @marcossidoruk8033 10 місяців тому

      Points are kinda valid but zero cost abstractions aren't real, all abstractions have costs (including the abstraction developed in this video), especially when it comes to C++ wich has such an utterly terrible standard library amplified by feature creep.
      C++ templates are a quite bad implementation of polymorphism too, and they absolutely explode your compile times not by just 0.9 seconds but tens of minutes in large codebases, so I will personally pass.
      I am not saying you should use C, again your points against C are mostly valid, I am just saying that C++ "solutions" are really terrible and it is understandable why people opt to not go into that. Ideally we would use a more modern language like zig, odin or Rust, but then for other reasons that is often not possible.

    • @darkcaste
      @darkcaste 10 місяців тому +1

      ​@@marcossidoruk8033 zero-cost abstraction is a runtime performance term, you're arguing semantics dude.
      But sure, let's talk about compilation times. The naive approach has you instantiating every template in every file you use them in, and then people wonder why their compilation is slow. You only need to instantiate a template once, and then extern it. Precompiled headers have been around forever, and of course you can implement a component using any C++ feature without exposing it in its interface. This is all neglecting the fact that if you have even the most rudimentary build tools, you're not rebuilding everything all the time anyway.
      There's this common argument against C++ that it's bad because it has a library or feature, despite it being completely optional. That leads to people writing unsafe code with macros to make up for the lack of that feature in C. If you don't want to use std::vector - you don't have to, same goes for exceptions, virtual functions, etc. Meanwhile, C23 is finally getting constexpr, a feature C++11 introduced ages ago. And you can still write C code alongside C++ anyway. I find the whole C versus C++ thing ridiculous.
      Other languages are other languages. I wrote my comment to be relevant to Dylan's video.

  • @DatBoi_TheGudBIAS
    @DatBoi_TheGudBIAS 2 місяці тому

    @ 1:00, im pretty sure it will display 420, IF it reaches the printf. It either overwrites that place of memory and prints it, or u get an access violation by accessing invalid memory.
    *maybe* it can happen that the piece of memory being written is volatile and somehow changes value in the nanosseconds that are between the instructions

  • @mythacker4365
    @mythacker4365 9 місяців тому

    Thanks. Besides all debate happening in comment section, I learned something today. That's good 😊.

  • @user-hk3ej4hk7m
    @user-hk3ej4hk7m 9 місяців тому +2

    C makes it feel like moving structs around in memory would just be a matter of (uint8_t*) casting and moving the bytes, sadly this isn't always the case for most architectures.

  • @Burgo361
    @Burgo361 8 місяців тому

    Vectors were the primary reason I started using c++ instead of c I don't like the idea of hard coded limits all over the place, I will be giving this method a try I actually did assume that the optimisation of vectors would be hard to match

  • @Ptolemusa
    @Ptolemusa 10 місяців тому +3

    The Trick is insanely useful. I like to allocate arenas upfront and grow my arrays into them. Using The Trick you can make a system for linking arenas together and getting an "infinite" arena where you can get however many dynamic arrays you want. Very lazy, very fast, very convenient.

  • @t___m
    @t___m 9 місяців тому

    In my college we had directional lists. We defined structure with a pointer to another structure of the same type and a bunch of variables. Wanna append the list? Just use the malloc function.

    • @samuelwaller4924
      @samuelwaller4924 9 місяців тому +2

      linked lists are way too slow to replace basic arrays like this

  • @algorithminc.8850
    @algorithminc.8850 9 місяців тому +1

    Great stuff. I look forward to checking out your channel. Thanks. Subscribed. Cheers

  • @johanngerell
    @johanngerell 10 місяців тому +6

    The second version doesn't handle reallocation. If the client has a pointer to the array data (that comes after the header) and reallocation is needed (assuming that functionality exists), then the client must be told to update his variable to the new array data. Therefore, version 2 is a great solution for *fixed length* arrays - for instance immutable string types. Passing out "a header" is necessary when the referenced array data can be reallocated - then the client doesn't need to know when reallocations happen.

    • @DylanFalconer
      @DylanFalconer  10 місяців тому +4

      The full implementation is available at the link in the description and does handle reallocation

    • @johanngerell
      @johanngerell 10 місяців тому +1

      @@DylanFalconer Ah, I see - array_ensure_capacity will re-assign to the array macro parameter, which is possible if the parameter is an L-value

  • @uuu12343
    @uuu12343 Місяць тому

    I infinitely prefer calling a dynamic array - "ArrayList" or maybe just "List", as it should be the case

  • @diceandbricks
    @diceandbricks 10 місяців тому +4

    Nice video. Unfortunately this trick does not take data alignment into consideration, which can lead to UB or performance overhead. Flexible array member would be one way to fix that.

  • @rickperez8975
    @rickperez8975 10 місяців тому +13

    Wow C is such a versatile language, I see why other languages are built on it

  • @oracleoftroy
    @oracleoftroy 10 місяців тому +4

    I think it is a bit dishonest to compare the compile time of a full solution as implemented by std::vector to a halfbaked example that isnt even fully functional yet. What is the compile time of your full solution compared to the std::vector version? I wouldn't be surprised if it still beats out vector, but it will no longer have the same margin of victory. Otherwise, of course the source with more code takes more time to compile. They weren't comparable.
    Another question is how does the compile time scale as you use it in more and more files? Is it linear? Constant time? Quadratic? I would expect the C code to scale fairly well and the C++ code to potentially have a more dramatic effect on link times, but that precompiled headers or modules would potentially remove most of that. Though if your C code were header only to encourage the compiler to inline more aggressively, that advantage would go away.
    Runtime performance should be a wash as your benchmark showed. Your version has potential cache miss for any size operations, but most of the time one is getting the size because they intend to iterate. There is more potential for optimization in std::vector because the types are all fully known, but I have some doubts that much could be done for this simple case. But the potential to inline and/or vectorize the code might favor C++ in the right situation.

    • @DylanFalconer
      @DylanFalconer  10 місяців тому

      I was comparing std::vector to the full solution I use in my code, not a half-baked one that isn't functional yet. You can see the lines of code in the video. Thanks for your comment!

    • @oracleoftroy
      @oracleoftroy 10 місяців тому +4

      ​@@DylanFalconerThat's good to hear, but it is very unclear from the video. At about 1:20 you show three lines of C that doesn't even attempt to do anything dynamic and didn't use any sort of linrary-like code. Then around 1:40 you are showing three lines of C++ using a vector, so a complete dynamic array implementation would be included in the full source listing. Then at about 2:10 you go straight to benchmarking.
      At the surface level, it looks like you are unfairly stacking the deck against C++ by using a very apples to oranges comparison, so you can see why I thought the benchmark reflected the code you showed that did absolutely zero dynamic array stuff and not the code you hadn't even talked about yet.

  • @kumboi
    @kumboi 9 місяців тому

    Sick video! I learned a lot

  • @haotj
    @haotj 10 місяців тому +10

    Does this handle alignment of T? What will happen if alignof(T) is bigger than alignof(Array_Header)?

    • @kiryls1207
      @kiryls1207 10 місяців тому

      i saw a similar approach done elsewhere along with the explanation on what happens under the hood (struct fields are just offsets from struct location). as far as i remember it should be ok to do this

    • @attilatorok5767
      @attilatorok5767 10 місяців тому +1

      Nice catch! This array_init should take in alignment and align to _Alignof(T) or _Alignof(Array_Header) whichever is larger. Could simply be written into the macro with _Alignof(T) > _Alignof(Array_Header) ? _Alignof(T) : _Alignof(Array_Header) and the functions allocator should take in an alignment, with the stdlib allocator there is aligned_alloc.

    • @OMGclueless
      @OMGclueless 10 місяців тому +2

      @@attilatorok5767 It's not just the allocator that needs this math. Every accessor needs it too since the amount of padding present in the header will depend on the type being stored.

  • @revealingfacts4all
    @revealingfacts4all 9 місяців тому +1

    I honestly don't understand why comparing C/C++ to things like Java or other managed languages. You have to understand that C++ and to a lesser extent C are lower level languages and they do not manage memory nor does the compiler and we like it that way. I don't want the compiler thinking for me. There is a myriad of reasons why too. The nice thing about C and C++ is you can use libraries that do manage memory for you or not; you don't have that option with managed languages std::vector is a library and not the language. SO MANY PEOPLE conflate things like std::map, std::vector, etc... as C++. C++ is simply the 'if', 'for', 'switch', 'class', 'template', etc... statements. I work both in desktop, rich GUI, environment as well as in embedded. The only thing that is common between those two worlds with regard to C/C++ is the C and C++ language - not the language support libraries. #define is also a pre-compiler language statement too. You don't need #define as you show in your video and many argue you shouldn't either. Scott Meyers, as one, who recommends to prefer the compiler over the pre-compiler. Finally, the whole dynamic memory topic - you should know you can, through operator overloading, make a C++ object appear or behave just like a managed language. The main take away for anyone reading this is C/C++ is vastly more powerful because it allows one to write code as close to the metal and do more yourself or as abstract as you want.

  • @pietraderdetective8953
    @pietraderdetective8953 10 місяців тому +1

    Amazing video Sir!
    Agree with the other comment: please make a beginner-intemediate C tutorial.
    Or if you don't have the time to make a full series, then I would be very happy with these kinds of videos ~ C lang tips and tricks
    Cheers!

  • @danielmilyutin9914
    @danielmilyutin9914 9 місяців тому

    Hm. I guess one could write this with C++ templates and have quite similar results. Difference in append is because you probably do NOT initialize your objects on creation. Which might be an issue: your objects are UB on creation. Thus, to be fair you should call some init fun when append.
    Imagine you have dyn array of dyn arrays. How would you manage that?
    I guess this is too painful in C. In C++ it is vector. Also it could be с container1. Depending on properties you want to achieve.
    BTW, you forgot about alignment. Maybe you were lucky with your example, but if metadata size (which you store on heap, as I understand) doesn't match object size there might be slowdown on access.

    • @phitc4242
      @phitc4242 9 місяців тому

      I made a (I'd say quite secure) dynamic vector that supports arbitrary array types, so a vec is possible and easily manageable.
      actually, a simpler version of vec, just for the sake of vector nesting, is a vec since dynamic strings (depending on the implementation) are also arrays :D

    • @danielmilyutin9914
      @danielmilyutin9914 9 місяців тому

      @@phitc4242 are you talking C or C++?

    • @phitc4242
      @phitc4242 9 місяців тому

      @@danielmilyutin9914 C...

  • @DisableComputer
    @DisableComputer 7 місяців тому

    I liked the thumbnail.

  • @songersoft
    @songersoft 9 місяців тому

    I like the brackets too. Subbed

  • @SanixDarker
    @SanixDarker 10 місяців тому

    just suscribed, well explained, thank you.

  • @evanotoole787
    @evanotoole787 10 місяців тому

    I just use alloca and malloc and keep track of the buffers using ID’s if need be.

  • @SibbeeeGold
    @SibbeeeGold 9 місяців тому +1

    General Kenobi

  • @42ott90
    @42ott90 Місяць тому

    0:00 General Kenobi

  • @mrcrackerist
    @mrcrackerist 10 місяців тому

    I normal do a DynamicArray_t struct to store the size of the type the count of items and other metadata with the pointer this allows the functions like get index and so on.
    also the header and pointer will be in the same memory section in this case.

  • @eddiebreeg3885
    @eddiebreeg3885 9 місяців тому +3

    I personally find the idea of dismissing C++ solely because of compilation times completely insane.
    Yes, compilation times matter, to an extent... but so do a lot of other factors, like language and standard library features, safety, ease of use...
    There a legitimate reasons why you'd want to use C over C++. There are also a lot of reasons why you'd want to use C++.
    The first and most obvious I can think of was actually mentionned: C++ has templates to handle type agnostic interfaces, C does not.
    But also: C++ allows you to handle resource ownership with classes, using RAII, move semantics etc. C does not, it is up to the user to handle
    copies, moves, destruction. This is much more error prone, and that's one of the reasons we still often go for C++, regardless of compilation times.

    • @teranyan
      @teranyan 9 місяців тому

      You find it insane because it is insane. The person who made this video clearly has no idea about business. Increased compile times will lose some amount of money to time, but you have the opportunity cost of using a much more primitive language. Which is not always worth it. I would argue that compile times could never possibly be a business reason to pick C over C++. Terrible video.

    • @eddiebreeg3885
      @eddiebreeg3885 9 місяців тому +1

      @@teranyan I wouldn't go as far as to say the video is terrible, to be fair, I find the idea interesting and useful for those who legitimately want C for a reason or another. I just wanted to point out this specific reason should absolutely not be it.

  • @AZIARGROUS
    @AZIARGROUS 10 місяців тому +2

    I don't have a use case for this as I only use high level languages at work but this is so interesting. Subscribed.

  • @nedclimax
    @nedclimax 10 місяців тому +4

    Nice.

  • @jongeduard
    @jongeduard 10 місяців тому +1

    Great video! Even though it does not cover some important things. Placing the header into the same block of memory plus using macros are really great ideas. 🙂
    I have even been experimenting with my own dynamic_list thing in C. The basics of it where already done with allocation and auto growth, but I clearly wrote more functions in my header file than what I completed implementations for. I did not use macros though, but it still just has the simple void pointer code.
    Most important for me was writing code that does exponential growth. And that's what I am missing in this video.
    Most programming languages as far as I have seen use exponential growth for their dynamic buffer types. Which means doubling the buffer size when it it is full, which is relatively efficient when you have no option to predict needed max capacity in a program. The overhead of copying does not need to be fully paid again is the idea.
    C is a simple language. Although I have always considered C++ fascinating at times when I worked with it, it's also a super complex programming language without enough safety at the same time.
    My current point of view on this is to either go for C because it's simple and therefore easier to understand and debug, or to go for complex but also super memory and thread safe at the same time, Rust. I really like Rust.
    There is a reason why the Linux project never adapted C++ and stayed at C, while they are now embracing Rust instead.

  • @jintz2
    @jintz2 10 місяців тому +2

    Thanks for sharing! Love your C videos!

  • @Nikoline_The_Great
    @Nikoline_The_Great 10 місяців тому +6

    I like this video. But I feel that zig answers pretty much all your problems. It integrates with existing c code. It has a good standard library (with growable arrays). It has custom allocator support built into the language.
    Its build time isn't quite as snappy as c. But it is still about half that of c++.

    • @KManAbout
      @KManAbout 10 місяців тому +1

      The more Marcros you add in c the higher the compile time as well

    • @Nikoline_The_Great
      @Nikoline_The_Great 10 місяців тому

      @@KManAbout is the impact of macros that large? I haven't checked myself. I would guess that parsing is the bulk of the compilation time.

    • @KManAbout
      @KManAbout 10 місяців тому +2

      @nikolajolanderrasmussen9128 it can get large. Just depends on the macros you have. There's overhead to everything. You can also look at compile times in some other languages that are really fast and they will usually be within 10% of similar c system with macros while offering something more robust

  • @androth1502
    @androth1502 10 місяців тому +1

    i made something like this years ago. C is actually a potentially great language if it had standard library support to make it great.
    i used it mostly as a safe dynamic string type. the allocator would store the capacity and size in the header right before the null terminated string data.

  • @krystofjakubek9376
    @krystofjakubek9376 10 місяців тому +17

    Hey nice video! I have a slightly different and in my opinion (of course) preferable solution that sits in between your approach #1 and #2.
    From my point of view there are 2 main problems with the implementation #2 you settled on:
    1) To initialize this array (even 0 sized) it is necessary to allocate (which is not ideal since a lot of the time we have zero sized arrays). Alternatively you can have every function (ie array_length) check if the array is NULL. This of course has some slowdown but more importantly these checks have a tendency to spread (We know that we will have to check every time so to precent that we check once then save the length and use the saved one - this is aditional complexity)
    2) It is not clear if the array is a dynamic array or just a pointer. This makes returning dynamic arrays slightly annoying because you always have to specify if its dynamic or not. (Its not uncomon to return static pointers when working with stateful systems).
    So we would like the two following things:
    1) the type needs to be static ie array of int is different from array of float on C type level
    2) the dynamic array should be its own type
    I do the following:
    For each type define a new struct with the same members as you impl #1 except the items pointer has the desired type (I have a macro DEFINE_ARRAY_TYPE(int) for this).
    Then I have each function on the array look like this:
    _array_resize(void* array, int item_size, int to_size);
    and a macro
    #define array_resize(array, to_size)\
    _array_resize(array, sizeof *array->items, to_size)
    The rest of the functions have similar structure. This means to acess the size you simply do array->size. To dereference ith element you do array->items[i] etc. You can even declare these array types localy (you can in C declar struct inside a function) and use it just there.
    I have detailed this approach on the Handmade discord but I wont send link here cause youtube would probably remove this whole comment.

    • @DylanFalconer
      @DylanFalconer  10 місяців тому +4

      Hey, that's actually pretty cool. I will implement it and play around with it. Thanks for sharing 🤠

    • @kiryls1207
      @kiryls1207 10 місяців тому

      do you have a gist with the implementation?

    • @yjlom
      @yjlom 10 місяців тому

      ​@@kiryls1207 maybe something looking a bit like this very barebones example (due to yt formatting some underscores were replaced by start/end italics):
      #define DERIVE_DYN_ARRAY(T) \
      typedef struct Dyn_Array_ ## T { \
      size_t max_idx; \
      T (*data)[]; \
      } Dyn_Array_ ## T; \
      \
      bool Dyn_Array_ ## T ## __set(size_t i, T x, Dyn_Array_ ## T *restrict xs) { \
      if(i > xs->max_idx \
      && !(xs->data = realloc(xs->data, (i + 1) * sizeof(T)) && (max_idx = i))) \
      return false; \
      else { \
      xs->data[i] = x; \
      return true; \
      } \
      } /* and so on and so forth */
      it does increase code size though, which is where you could use more generic functions and maybe thin inline wrappers

    • @kiryls1207
      @kiryls1207 10 місяців тому

      @@yjlom oh thank you, i thought about some code generator, my only problem would be that macros, especially on the generators side, are really hard to debug when something goes out of your hand. i guess an arena could solve some of the problems but it doesn't come free. so yeah, unless it's a really known problem or you should really ponder if it's a good idea to use these kind of macros

  • @mariosshadow8533
    @mariosshadow8533 9 місяців тому

    I don't know what the pattern is called, but it is not uncommon; apparently computer networks use this to avoid sending more that one packet.
    You may also work with the type as if it was just a pointer, which means it fits in a register. You can then use a macro to add the size of the header to the original pointer, and then get the element from there.
    As pointed out by the pinned comment, the size is not item_size * capacity + sizeof (Array_Header). You forgot the padding. The Array_Header size might not be a multiple of the alignment of the element type.
    If using GNU's C dialect, you can declare your structure to have a zero sized array at the end, which will add the necessary padding to the structure. In C99, you could use an empty array at the end of the structure, but it does not include padding, making it redundant.
    And if your problem is that you like operator overloading:
    1. That's a C++ feature. Trying to hack it into C is a bad idea.
    2. you could make a function array_data_ptr(array*), but the lack of generics means it would be used like ((int*) array_data_ptr(array))[index]. Not pretty.
    For the less knowledgeable, the C++ example at 1:40 is incomplete. You can't vector::at with a random index. It has to be in range of the length, not the capacity. Otherwise the elements would not be contiguous.
    Opinion stuff:
    I've been viewing a lot of material on C and I'm just left thinking: why are we still using it?
    Like did not anybody think of replacements before Zig and Rust???????
    C flatout makes your programs worse, no contest.
    void* is a horrible, HORRIBLE abstraction for general use. It's a pointer, not a value, meaning a value has to be stored somewhere that is not the array, deferring the objects lifetime management to something else. Some people store the objects on the heap, for some reason.
    No namespacing means that people do their namespacing, and when using namespaced stuff you can't remove the namespace, leading to overly verbose names. C could have just implemented them with emiting names with _, which is a valid character in everything I've ever seen.
    Wait, C actually has namespacing, but only for structs, enums and unions, with their respective keywords for some reason, leading to the rampant use of typedef.
    Macros are... just... copy... pasting... stuff???? might as well use automake. The C preprocessor is just a program unrelated to the compiler.
    This also leads to buffer overflows are too easy to trigger.
    Just pass a string without a null
    Null terminated strings were good in the 1970 for the PDP-11, and nothing else after that.
    string length is given in linear time. Nothing more to say, that's just bad.
    Assemblers nowadays can store a normal ASCII string and give you it's length at assembly time for you to store in the binary's data section.
    I don't even know why we call them strings, it's a character buffer without length and no invariance checking.
    Recently I had to help a poor soul whose problem was that the buffer he was reading to didn't store the null character. I recognised in 1 second (you would too), and my solution fixed the problem immediately.
    Oh, and strings can't include the null character, which actually has some uses.
    There's literally no way to define compile time constants (yet, C23 has constexpr). #define is not a solution, the preprocessor sucks, and forces the compiler to analyze the replaced values everywhere, leading to larger compilation times.
    The inline keyword exists, I guess. And no, it does not inline functions. Go figure.
    static inline is a valid combination too, when all it does is just static, completely ignoring inline.
    static is not the default.
    The default is extern, and you can prepend it to anything, but why would you? It's the default. It really only matters for global variables, which are discouraged, from other C files.
    Header files (which are copy-pasted recursively by the preprocessor) suck, no wonder languages that are not derived from C have never implemented them.
    Oh, and they increase compilation times by a considerable amount, as demonstrated by C++20 modules and, oh yeah, every other language ever.
    They are also read everytime they are included.
    I literally can't count the amount of times I've seen a linked list where a dynamic array would be more appropriate (because modern processors, memory efficiency, etc...), just because C does not provide data structures in it's standard library because it CAN'T due to the lack of generic types. No, _Generic is not a replacement, it literally matches on a static amount of types. True generics allow the compiler to emit code that is tailor made for that type.
    The lack of (monomorphised) generics promotes the use of void* (type-erased generics) without the safety of the type, or the safety of a garbage collector to manage the pointed-to-values, or the speed of implementations that use values rather that pointers.
    Error handling is a joke. Errors are propagated by a single sentinel return value, and then you have to go out of your way to check errno, a seamingly unrelated variable.
    I would rather use inline assembly than syscall wrapper functions, as the wrapper functions only return a single sentinel value and set errno, making me checking another, unrelated thing, where syscalls return both values and errors, meaning when I check if a returned value is valid, I also check what error it is.
    Apparently no one knows about unions, even less how to make tagged ones.
    No data hiding means that anyone can just overwrite the values on your struct or union, and then you can say goodbye to your invariance.
    No RAII patterns nor defer means that error handling, which is already verbose enough, is even more verbose. You either repeat code, which is discouraged, or store your return value and using labels, which are discouraged.
    I truly believe that if your first programming language is C, it will make you a worse programmer for longer than if you used anything else.
    BTW, At 4:28 your array_append call should pass a void*, but it got passed an int.
    Edit: Ok so I just learned the PDP-11 has 16 bit words and registers. Why did
    why di
    why
    why did they use null terminated strings 😂we're fucked

  • @ishaankapoor933
    @ishaankapoor933 9 місяців тому

    What are you using to make such videos. i.e. what software is that were you click around to minimize / maximize topics/headings.

  • @MrSomethingdark
    @MrSomethingdark 2 місяці тому

    General Programming!

  • @Sonyim414
    @Sonyim414 10 місяців тому

    I like it! The video itself is lacking in some parts, but it's not a bad implementation.
    There are other reasons besides compile times when choosing C. Sometimes it's just the project you're stuck with, sometimes you need a greater level of control, sometimes you just can't be assed to prepend the "extern C" to every library function you use.
    For me personally, I'll stick with a variation of solution 1 (with special macros for every typed array), but thats only because I'm an old fart and I come to expect certain functionality from squared brackets. C has it's way of doing things, and that's not bad. Just a quirk of the language.

  • @שחרטרבלסי-ח1פ
    @שחרטרבלסי-ח1פ 9 місяців тому

    That is not kinda stupid, init a value in a index that is too big for the array size actually allow the programmer this ability of writing to a memory that is not his, you can use it to kinda hack your program or do a lot of cool and weird things. C supposed to be super flexible for those purposed, thats why it so hard to be a good programmer in it

  • @leowribeiro
    @leowribeiro 10 місяців тому

    Of course you can do it C, we've always known that so nothing new there. The real question is What happened to the compile time? C++ uses the STL for std::vector which is defined using template, you could try implementing your own version in C++ without template and see how compile time is affected.

  • @yeahaddigirl
    @yeahaddigirl 10 місяців тому +6

    Compile time is why i haven't tried to compile Linux from scratch

  • @aniketbisht2823
    @aniketbisht2823 9 місяців тому +1

    If just compile times are your reason to use C rather than C++ for writing large complex projects, then that's kind of lame.
    For hot reloading code, just don't use templates or use them sparingly.
    And nobody is forced to just use standard C++ library. If you care about performance then you would write your own libraries catered to your specialized use case or use external one.
    The thing is, C has very primitive support for library authors, just functions, structs and macros.
    Whereas in C++ you have all the features that you would want and then some, which enables you to write very efficient zero cost abstractions.
    Anything generic in C would require function pointers or macros.
    And you can't make "code path decisions" at compile time like C++s if constexpr and function overloading.
    Not to mention there is no constant expressions in C while in C++ now you can allocate memory at compile time and use the entirety of cmath enabling you to do arbitrary computations at compile time, all free of UB.

    • @MrAzzlip
      @MrAzzlip 9 місяців тому

      I do agree, but you might want to check out C23 (includes constexpr)

  • @Maraus92
    @Maraus92 10 місяців тому +2

    Amazing stuff! I have a question: what tool are you using to create this style of presentation?

    • @tsunami870
      @tsunami870 10 місяців тому

      Looks like you could do this easily with Google slides or PowerPoint

    • @DylanFalconer
      @DylanFalconer  10 місяців тому

      Thank you 🤠 I have updated the description with the tools used

  • @AlexAlex-jk2tn
    @AlexAlex-jk2tn 10 місяців тому +6

    Original C solution is not the same as C++ solution (so you can not compare their speed) as it doesn't use heap memory (it uses Stack memory instead which might be not so safe, but you don't need to release it). Also your second C solution (which is working the same fast as C++) is correctly represented C++ solution, BUT you also need to provide a macro for releasing array, you can not release it using free() function because you use your header which is hidden from user.
    Summary C and C++ provides the same speed, but in C you have to use a function for releasing array memory. So use C++ if possible, but sometimes you don't have option for using it, then use C (e.g. for Embedded programming it's often prohibited to use C++ to avoid implementation its runtime).

  • @ryanwarner5006
    @ryanwarner5006 9 місяців тому

    The thing is with object oriented code I don't think comparing line count is a valid way to determine what compile time should be.

  • @rohandvivedi
    @rohandvivedi 4 місяці тому

    Good design, but this design also requires handling cases when the reference itself is invalidated after a resize.

  • @blazer511
    @blazer511 8 місяців тому

    Hi Dylan, I am just relearning C, can you write out the implementation for the array_append function at 4:08? thank you

    • @DylanFalconer
      @DylanFalconer  8 місяців тому

      Hi Aaron, I have a full implementation available if you click the Substack link in the description. Cheers

  • @Illogical.
    @Illogical. 9 місяців тому

    Is there a way to reserve a bunch of memory, and have labels that point to various places in the reserved memory?
    Example: label_a might point to the first slot in the reserved memory, label_a[7] points to the eightth slot in that reserved section of memory, but maybe label_b points to the sixth slot in reserved memory.

    • @samuelwaller4924
      @samuelwaller4924 9 місяців тому

      yes? That's what pointers are. You can make an array and index it

  • @faucar93
    @faucar93 9 місяців тому

    Why not using or comparing with the CDS library ?

  • @pushqrdx
    @pushqrdx 7 місяців тому

    Is there a name for the color scheme you're using in this video? I find it very easy on my eyes and wish I can use it for code.

  • @Crux161
    @Crux161 10 місяців тому +4

    coding tribalism is so cringe 😬
    Yes I like store bought pasta, but I also like being able to make my own - fresh - from base ingredients. In the world of development you may have an opsie-woopsie, or perhaps even a f#!€y-wucky but you learn something along the way and can grow as a programmer.
    Edit: to be clear I love this video and despite other people’s criticism I think it’s an excellent piece of work. If someone wants to criticize anything they should at least try and explain why 😅

    • @DylanFalconer
      @DylanFalconer  10 місяців тому +3

      Thank you and to everyone who pointed out the alignment issue. I'm going to update the written version today with what I think is a sensible solution

  • @jcembedded
    @jcembedded 9 місяців тому

    I love strtok func so much. Why doing something in 5 characters when you can do it in 20 lines? Quit modernity! Embrace tradition. Tradition is what makes the industry go forward!

  • @ryanc674
    @ryanc674 9 місяців тому

    is there any command that i can use to show the profiles of two programs at 2:56

  • @p99chan99
    @p99chan99 10 місяців тому

    I still dont get how memory allocation works in C 😔

  • @jamesm820
    @jamesm820 10 місяців тому

    is this the same as go lang version of dynamic array which they called a slice

    • @DylanFalconer
      @DylanFalconer  10 місяців тому

      I believe a slice is more like a "view" into an array. So, the array is fixed size, but the slice notation allows you to "slice" it up. It's a pointer and a length. It also has some convenience functions which, I think, can reallocate the underlying array's memory

  • @MaxCoplan
    @MaxCoplan 10 місяців тому +2

    that "trick" is also how many malloc implementations store metadata about the allocation

    • @prozacgodretro
      @prozacgodretro 10 місяців тому +2

      It's basically a pascal string for arrays.

    • @spell105
      @spell105 10 місяців тому +2

      It's how all heap allocators work.

  • @10e999
    @10e999 10 місяців тому +1

    I wonder what are the tradeoffs between keeping your allocator in the datastructure vs passing them as arguments.

    • @odomobo
      @odomobo 10 місяців тому +1

      You'd have to pass the allocator as arguments to append, in addition to when you init and free the array. Because of this, I think it would make it fairly onerous to use if you had to always pass an allocator.

    • @DylanFalconer
      @DylanFalconer  10 місяців тому +1

      Yeah, passing the wrong allocator may or may not be an issue, depending on the allocator used. However, it's also annoying to explicitly pass every time you want to append as that's a common operation

    • @KManAbout
      @KManAbout 10 місяців тому

      @DylanFalconer pass it to the array via array.init, then every time you append the array will use the allocator pointer you passed on init.

  • @pcuser80
    @pcuser80 10 місяців тому

    i use this in gnu c
    int addrecord(char ***inputarray, int *inputarraycounter, const char *fmt, ...)
    {
    char *str = NULL;
    int len = 0;
    if(fmt)
    {
    va_list ap;
    va_start(ap, fmt);
    len = vasprintf(&str, fmt, ap);
    va_end(ap);
    if(len == -1)
    {
    return -1;
    }
    }
    (*(inputarray)) = realloc((*(inputarray)), sizeof(char *) * ++(*(inputarraycounter)));
    (*(inputarray))[(*(inputarraycounter)) - 1] = str;
    return len;
    }

  • @dough-pizza
    @dough-pizza 9 місяців тому +1

    Arrays in other languages: ☺️☘️🌞
    Arrays in C: 💀⚰️🤦‍♂️🤦‍♀️🚨 😀🔫

    • @kezif
      @kezif 9 місяців тому +1

      To be honest when you programm microcontrollers you dont reallyneed fancy overhead that c++ offers you. If you need dynamic arrays in C i.e. "normal arrays" in other language,you could write library like in 30 minutes and use it for the rest of your life.

    • @matyasmarkkovacs8336
      @matyasmarkkovacs8336 9 місяців тому

      Nah. It's really easy and logical, once you understand it.

  • @nothing-lo8lh
    @nothing-lo8lh 9 місяців тому

    Why not just use realloc()?

  • @CoolestPossibleName
    @CoolestPossibleName 10 місяців тому +6

    How to free it?

    • @odomobo
      @odomobo 10 місяців тому +5

      Cast the pointer back to Array_Header *, decrement (to point back to the start of the allocated block of memory), and free using the allocator's free() function

    • @DylanFalconer
      @DylanFalconer  10 місяців тому +6

      @odomobo is correct. Depending on your allocation style, you may not have to worry about it anyway. I'll make more content about memory allocators

    • @CoolestPossibleName
      @CoolestPossibleName 10 місяців тому +2

      @@DylanFalconer Yeah please do

  • @hasanbaysal5526
    @hasanbaysal5526 9 місяців тому

    Did you mean linked list :)

  • @moras6519
    @moras6519 10 місяців тому +18

    This is a vector

    • @DylanFalconer
      @DylanFalconer  10 місяців тому +9

      Indeed

    • @shinobuoshino5066
      @shinobuoshino5066 10 місяців тому +12

      Except a very shitty one.

    • @ad4mwayn340
      @ad4mwayn340 Місяць тому

      This is a data structure that behaves like an std::vector, which is a dynamic array, which, mathematically, is not a vector :)

  • @charlescox290
    @charlescox290 9 місяців тому

    So, basically, you brought Ada arrays into C? Well, not quite. You don't have any type enforcement on the index.

  • @chudchadanstud
    @chudchadanstud 10 місяців тому +4

    Compile times add up but coffee breaks with your colleagues add up more. Personally, I have a hatred for micro time management. 99% of the time you have save 100x as much time by not chit-chatting with your colleagues.

    • @DylanFalconer
      @DylanFalconer  10 місяців тому +4

      I agree, but I don't have colleagues, so I've gotta optimise my own workflow. It's only partially about time, though that's what I said in the video. It's more about fiction. Waiting just 3 seconds can pull me out of a flow state, and then I'll be on Discord or something.

  • @phitc4242
    @phitc4242 9 місяців тому

    try compiling a c program with tcc... compile times are as fast as 10x of that of gcc. even clang is faster than gcc... ;D

  • @juanandresnunez658
    @juanandresnunez658 9 місяців тому

    too advanced for me lmao

  • @ko_fes
    @ko_fes 9 місяців тому

    It's not like "generics", - most closest approach to them is using macros with type provisioning without storing type size dynamically inside header and still there is a problem with fields optimization: in c++ if allocator set to default in stl container then it wouldn't be dynamically represent in container object

    • @ko_fes
      @ko_fes 9 місяців тому

      And trick with storing header as array element working only with maxaligned elements' allocation (malloc does it, of course (and it's lame)), 'couse header could have different alignment comparing to its elements and also each time using this type array there will be copying heap memory block of array with header to cache and this is not cool

  • @anon_y_mousse
    @anon_y_mousse 10 місяців тому

    I was thinking this was going to just be about VLA's, but this is cool too. This is how STB works too, and once you fix the alignment problem you'll be well on your way. I just wish that the extensions that Jacob Navia added to LCC would get added to the standard. I've been wanting C to have operator overloading for decades now. Oh well, this is one of those issues that I've always had with C and why I've been working on my own language. C is the best programming language there is, but it really does need overloads. Even Python does better here, and Python sucks.

  • @rodrigoqteixeira
    @rodrigoqteixeira 9 місяців тому

    linked lists * cougth *

    • @lavatasche2806
      @lavatasche2806 9 місяців тому +1

      linked lists are really slow

    • @rodrigoqteixeira
      @rodrigoqteixeira 9 місяців тому

      @@lavatasche2806 it depends, if you use only the start and the end, like in a queue or in stack it is actually faster

    • @rodrigoqteixeira
      @rodrigoqteixeira 9 місяців тому

      @@lavatasche2806 it depends, when you only use start or end of the linked list it isnt slow, adding stuff to the start of end of a linked list sometimes can even be faster than to an array list. So linked lists are faster than vectors/array list for queues and stacks

  • @rcoder01
    @rcoder01 10 місяців тому

    This is literally just zig lol

  • @pyromen321
    @pyromen321 10 місяців тому +2

    This is horrifying. If you’re trying to hotswap code, you doing things wrong.
    By facilitating guess and check programming, you’re opening yourself to an absolute nightmare if others end up working on it.
    I absolutely agree that compile time is important, but if your workflow is improved by waiting 30 seconds instead of 60 seconds, you’re doing countless things wrong.
    Other than that, I do really like this design pattern for projects limited to C for whatever reason.

  • @jonshouse1
    @jonshouse1 9 місяців тому

    You are confused. Undefined behavior is where the result of a set of operations may not be consistent, or where the language does not define the outcome of the operator. Accessing C arrays out of bounds in not undefined, it is just stack corruption. The fact that the stack size may accommodate the access does not make it undefined, it is just plain old corruption, illegal, wrong, faulty. It is an important distinction, as true undefined behaviour does exist, normally as a result of architecture specific details clashing with code generation. Faulty code seeming to work at runtime is not undefined, it is just unfortunate, and implementation specific - it may well crash instantly on some O/S Arch combination but run seeming without issues on others. This is why stack checkers exist, -fsanitize=address on gcc is good example and (I suspect, not tried it) would detect your example fault.

    • @asandax6
      @asandax6 Місяць тому

      It is undefined as it is not consistent. It may write or it may not wrote depending on a myriad of reasons hence why the behavior isn't defined.

    • @jonshouse1
      @jonshouse1 Місяць тому

      @@asandax6 Did you even read my comment? Corruption != Undefined && Undefined != Corruption - please think harder!

  • @affegpus4195
    @affegpus4195 10 місяців тому +2

    The best way to have a dynamic array in C is not have one.
    just use array bigger than what you need.
    Sounds stupid but it is the kind of enviroment C was made for.
    you had harsh memory and processing limits, so you set sizes for boundaries for your data and you tried to make it reasonable enough so the program could be useful and still work.
    If you truly needed bottomless pit of data, then that was a memory menagement issue, thats what heap and File pointers were made for.

  • @StephenBuergler
    @StephenBuergler 10 місяців тому

    I think microsft's BSTR type does this.

  • @raghuraamm3284
    @raghuraamm3284 Місяць тому

    69420

  • @GeorgeTsiros
    @GeorgeTsiros 9 місяців тому

    congrats
    you re-implemented a worse std::vector. This is the kind of shit why c++ exists. If compilation speeds are what hold you back, think hard about _why_ that is.

  • @louisjx8009
    @louisjx8009 10 місяців тому

    I don't like the 2d option. I ve tried it a bunch of time, but yeah no. I rarely use dynamic array, so I don't think about it too much

  • @m4rt_
    @m4rt_ 10 місяців тому

    8:08 I personally would do this: void *ptr = NULL; instead of: void *ptr = 0;
    though I think it does the same, I just feel like the former is more readable, at least to me.
    also I would give some error if the allocated memory is NULL (the if (h) check).
    Other than those nit picks, I really like this solution.
    fun fact, This way of doing it is really similar of how it is done in Jai (a programming language primarily for making games that is currently in beta)
    Though it stores the equivalent of this:
    struct Resizable_Array {
    int64_t count;
    void *data;
    uint64_t allocated;
    Allocator allocator;
    };
    And a lot of it things is baked into the compiler, but the functions to interact with it is in the standard library (which is just a bunch of source files so you can see and modify it).

    • @Zeutomehr
      @Zeutomehr 10 місяців тому +2

      soon we will be able to do nullptr

    • @shinobuoshino5066
      @shinobuoshino5066 10 місяців тому

      Assigning 0 to pointer is undefined behavior because there's no guarantee that NULL is 0, C programmers can't even get these basic things right while my knowledge of C exists only because of me wrapping shitty C API's in real languages that don't give me brain damage.

    • @oracleoftroy
      @oracleoftroy 10 місяців тому

      ​@@shinobuoshino5066Assigning a pointer to 0 is well defined and will result in the pointer being assigned to null. Note that that means the pointer may not have the bit pattern for 0 after the assignment if nullptr is different.
      What you might be thinking of is memseting a structure to zero and expecting the pointers to be null. In that case, the pointers will be 0 and not null on platforms that use a different value for null.
      Fortunately this is almost all academic as most platforms can ensure that the address 0 and null are the same, even if the pointer does address address 0, via the magic of virtual memory. And I suspect that on platforms where this isn't possible, you are hard coding memory addresses anyway and not dealing with dynamic memory much at all.

  • @bterwijn
    @bterwijn 10 місяців тому +154

    So funny, the irony. What a mess and not even type safe (void*) or memory safe (better not forget to deallocate) and then he says "obviously I prefer C", lol. And that for something as simple as a dynamic array, better hope nobody wants a hash set/table, binary tree, graph, etc. Obviously you should use a modern language if you want to do serious high-level programming. A modern language abstracts away the uninteresting time-consuming details so you can focus on the what you want to program instead of having to play with low-level pointers, macros, allocators.

    • @miniappletheapple
      @miniappletheapple 10 місяців тому +102

      Some people just prefer low level language, so that you can literally see what's going on in the low level side. The disadvantage of C++ is that it abstract a lot of the low level side, but you still need to figure it out what's going on at low level to make sure you don't make some errors.

    • @relims
      @relims 10 місяців тому +44

      Funny you said that because I implemented absl flat hash map and flat hash set in C. It doesn't take much to get use to C way of managing memory. If you can't handle that (skill issue) then there is always other languages for you to choose like JavaScript
      Edit: absl flat hash map and set is more pointer arithmetic so to be clear, I also implemented the normal hash map and set (just that I had a skill issue when I wrote it so it is only slightly faster than C++ hash map)
      Edit 2: grammar
      Edit 3: forgot I didn't specify why playing with low level pointer, allocator is good (and also fun). You are not relying on a black box to spit out your data structure object. If you have a memory fragmentation problem in C++, you might as well give up since knowing how a data structure work does not mean you understand its implementation.
      Edit 4: malloc realloc free and some POSIX allocator isn't even low level

    • @DylanFalconer
      @DylanFalconer  10 місяців тому +156

      I would use a higher level language if they weren't all so slow :(

    • @miniappletheapple
      @miniappletheapple 10 місяців тому +28

      @@DylanFalconerthen, would you use Rust?

    • @_lod
      @_lod 10 місяців тому +17

      Not managing my own memory or not having pointers feels so icky. I like being able to do things with my memory like not allocating (which is sooo slow) as much as possible

  • @AJMansfield1
    @AJMansfield1 10 місяців тому +4

    1:00 Yeah you just lost about all of your credibility here with that noob take about UB being "stupid". Undefined Behavior isn't an accident, it's a core language feature responsible for allowing compiled C and C++ code to be as fast as it is.

    • @juergenm6107
      @juergenm6107 10 місяців тому +2

      It shouldn't be an UB when an "Out of bound access" happens. When I see such code like show in this video, I see new CVEs poping up.

    • @AJMansfield1
      @AJMansfield1 10 місяців тому +2

      @@juergenm6107 Writing code that violates your data structure invariants is a cause of major bugs in _every_ language. Enforcing bounds checks on every access is both unnecessarily slow and makes the language less useful for some of its core use-cases in systems programming where "do what I say" unchecked memory accesses are both useful and critically necessary.

    • @DylanFalconer
      @DylanFalconer  10 місяців тому

      I agree, it was a silly take when UB is what makes the compile speed fast in the first place. What I should have said is I'd prefer if everything was explicit by default and then you could turn checks off for faster compilation.

    • @AJMansfield1
      @AJMansfield1 10 місяців тому

      @@DylanFalconer Hey if you want checked accesses for debugging, that's absolutely available -- it's called valgrind.

    • @gianni50725
      @gianni50725 10 місяців тому +1

      No. You can certainly justify array OOB access being undefined, but other forms of UB are silly even _with_ the potential optimizations, like the strict aliasing rule or the union live member rule (can't reinterpret the bytes of one object in a union as another -- that's UB, except specifically for type punning in C but not C++).
      Both of those rules are pure anathema to low-level programming and are best left ignored by compiler writers in most cases given that ~any useful non-trivial networking or kernel programming will violate one or both rules very quickly. Particularly with the strict aliasing rule, it should have been left to the programmer to specify if pointers don't alias, and only the programmer, via restrict.
      Not to mention, unlike rules like out-of-bounds access, they are very arbitrary to begin with. There's no obvious reason why a reinterpret cast from one pointer to another shouldn't just work, and I can't even think of a real example where the live object rule for unions would be useful for anything except guaranteeing unforeseen bugs you didn't intend.

  • @yuan.pingchen3056
    @yuan.pingchen3056 9 місяців тому

    This is a pointless topic. The C language was used to design operating systems in the 1970s. It enforced structured data structures and pointer manipulation. Even if it allowed you to dynamically allocate memory to the operating system, that's not what it was. If you want to keep your investment in C language syntax, you should try to learn C++

    • @cheesepie4ever
      @cheesepie4ever 9 місяців тому +1

      Pure waffle 😂

    • @yuan.pingchen3056
      @yuan.pingchen3056 9 місяців тому

      @@cheesepie4ever Your metaphor is inappropriate, add dynamic array to C language just like add refrigerator function to your bed.

    • @DylanFalconer
      @DylanFalconer  8 місяців тому +1

      Sounds like you are jealous of C

  • @sunofabeach9424
    @sunofabeach9424 10 місяців тому

    learn rust ffs