A more general name for this kind of thing is a growable array. The choice to name this "vector" is regrettable given the other things that word means, and the Rust name Vec is at least an improvement on that.
std::vector is famously the only C++ standard library container which isn't crap, but, the fun thing is that it's still a bit crap anyway. Like a lot of C++ standard library features it could be better, but that'd be an ABI break, so too bad either use somebody's custom replacement which is better or put up with the good-but-not-great std::vector
Well any newer language just seamlessly rolls that functionality into regular arrays and calls them lists, Java and C# call it an ArrayList. I'm not sure if there's a standard naming convention for it.
What really disqualifies C for anything practical these days is the strings. In that it has zero native support for them, they're not a first-class citizen as they damn well should be, they're a complete illegal alien. String.h is such a trainwreck from a UX perspective that I despise every minute I ever had to work with it. C++ at least has some out of the box support for them, even if it's very barebones and clunky af.
Like aside from getting an educational perspective of how incredibly limited and poorly designed languages used to be, there's no reason we should still be building our cars out of wood with stone wheels which is what using C (and C++ to some degree for that matter) feels like. For learning how processors and computers work at the lowest level, going with something like assembly on a microcontroller makes more sense anyway.
C++ 17's std::string_view is finally the table stakes bare minimum type, but notice that's a library feature so it's not actually part of the core language. std::string_view is approximately &[u8] that is, it's a non-owning reference to some bytes, it knows where the bytes are, and it knows how many of them. This means it's a fat pointer. In 1975 this was perhaps too expensive to be reasonable, it's 2023, we have enough registers, use a fat pointer and suck it up.
I would like more, indeed a lot more, but in a resolutely bit banging language I will put up with &[u8] and that's what std::string_view gives you in practice. The out-of-box string literal in C++ is unacceptable, as is std::string (an owning, growable string) used for read-only purposes. None of this stuff should have survived into C++ 11, and yet today you will still see it used.
Not having first class "strings" (which are a cloudy concept) is exactly the right call for a systems programming language that is concerned with the harder truth about in-memory structures.
If you want to "join together" strings data and printf/snprintf isn't convenient enough, go ahead and use different tool. But don't act like computers can natively compute the concatenation of two string buffers, and offer you the result into your expecting hands. On a lower level, everything needs to be stored in memory somewhere, and if "just somewhere in a heap allocation" is good enough for your purposes, use a different language but don't expect best performance nor simplicity.
Tbf, anything but bytes is a cloudy concept and not accepting that is just making things harder for yourself for literally no reason. If you have native arrays, you can have native strings, end of discussion.
C doesn't have strings is because there wasn't a well accepted library for them at the precambrian time it was being designed. Otherwise it would've, not out of some ideological fit-for-purpose nonsense. It was supposed to be a high level language, as high as they could go with systems being as crap as they were back then.
> Tbf, anything but bytes is a cloudy concept and not accepting that is just making things harder for yourself for literally no reason.
What I mean is that strings are an abstract concept (roughly, a sequence of characters that can be indexed and possibly mutated) with many possible implementations and runtime characteristics. IMO, for C it wouldn't make much sense to prefer one over the other, other than the obvious statically allocated fixed size array where the compiler can already do all the work.
> If you have native arrays, you can have native strings, end of discussion.
C does have native arrays (of fixed size) and so does have native strings (of fixed size). It even has convenient syntax for instantiating these strings -- string literals. There is even syntax to concatenate them (at compile time) by simple juxtaposition in the source code.
> C doesn't have strings is because there wasn't a well accepted library
Nope, the reason is that "strings" (even if you assume their representation is strictly a tuple of size + pointer to contiguous character buffer) need storage of size that is unknown at compile time.
Storage needs to be managed, and the storage management can be either manually done or be done automatically by the language runtime. C opted to not have any of the latter except for stack variables and global variables (lifetime of process) -- and neither of those support dynamically sized allocations (exclude VLAs here).
If strings were simply an issue of library or syntax support, all the strings you could envision would exist by now. But even the best string libraries (for C) are a bad joke and do almost nothing to make string processing more ergonomic.
Honestly, I don't miss any of that, almost all of my string needs are well covered by printf/snprintf. Once I worked on my own text editor, and for this developped a text rope data structure that kept track of the line endings and the number of unicode code leader bytes etc, and supported efficient string operations at almost arbitrary sizes (easily gigabytes). Most languages probably don't give you this with their built-in "string" type.
What helps though, is to pick your tools according to the job. There is a lot of scripting work or web work that the language is simply not suited for. However for systems programming it's doing fine, giving you a lot of control while automating the most common headaches (register allocation, function calls, computation of data member offsets, etc).
> It was supposed to be a high level language, as high as they could go at the time anyway.
That is patently false. It was supposed to be a language that could allow them to program systems but more conveniently, and it was based on prior art. (There are certain figures on HN that I expect to immediately pop up and add that the art was already quite more advanced and "high level" at the time and C wasn't innovating on any front).