News for hackers. Stuff that matters.
Re:C Strings and my slow descent to madness by gavinhoward on Thursday April 06, 2023 @09:34AM (#35468115)
Okay, I agree that by default, C strings are bad.

But it doesn't have to stay that way. Someone else in the comments mentioned antirez's sds library for dynamic strings. This works, but you could also easily roll your own. All you need is an init function, and perhaps an assert or other check at the end of it that the string has a nul terminator.

At that point, type checking will let you blindly pass those strings (or their char arrays) to any of those C functions without worry.

Re:C Strings and my slow descent to madness by magicalhippo on Thursday April 06, 2023 @09:42AM (#35468194)
> by default, C strings are bad.

C strings aren't bad. They can't be, because they don't exist. C doesn't have strings. And that is the issue.

As you say, things get a lot better when you actually introduce strings as a concrete concept rather than a set of lose conventions.

Re:C Strings and my slow descent to madness by junon on Thursday April 06, 2023 @09:47AM (#35468257)
There is no "loose convention". A C string is a null terminated string of non-null bytes. That's the definition. Working with them in memory-unconstrained environments is unnecessarily hard.
Re:C Strings and my slow descent to madness by magicalhippo on Thursday April 06, 2023 @10:07AM (#35468482)
There is indeed just convention. The language defines string constants similar to what you say[1] (an array of characters, terminated by a null character), but in the language itself there's no way to declare that a function takes a string rather than a pointer to a character. Alternatively if you work with a fixed-sized character array, there's nothing separating it from "just" an array of characters that are not null terminated.

So that strcmp expects a string rather than a pointer to a character is just convention. In languages which actually has strings as a concrete concept, like say Pascal and derivatives, you can actually differentiate between those two cases.

[1]: https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html#...

Re:C Strings and my slow descent to madness by int_19h on Thursday April 06, 2023 @03:47PM (#35473423)
That means that the strings aren't properly reflected in the type system. But the existence of string literals with a very definite in-memory layout means that it's not just a convention even so.
Re:C Strings and my slow descent to madness by magicalhippo on Thursday April 06, 2023 @03:50PM (#35473467)
But you can't use those string literals in any way without relying on convention.
Re:C Strings and my slow descent to madness by int_19h on Thursday April 06, 2023 @03:55PM (#35473520)
That was my point. Although you can, actually - since literals themselves are array-typed, you can sizeof them to get the character count without relying on null termination. It's even possible to get a non-null-terminated literal if the target array type is not large enough to fit null, e.g.:

   char s[3] = "foo"; // not null-terminated!
Re:C Strings and my slow descent to madness by giantrobot on Thursday April 06, 2023 @05:36PM (#35474851)
> character count

Byte count.

Re:C Strings and my slow descent to madness by int_19h on Thursday April 06, 2023 @06:26PM (#35475369)
If you want to be pedantic, "an object declared as type char is large enough to store any member of the basic execution character set". It doesn't actually have to be a byte.

In practice, in C context, character == char == byte. Other concepts have to use different names to avoid confusion with the language spec.

Re:C Strings and my slow descent to madness by junon on Saturday April 08, 2023 @07:32AM (#35492325)
In C a 'char' is a 'byte', if we're going to be extra pedantic.
Re:C Strings and my slow descent to madness by flohofwoe on Thursday April 06, 2023 @11:16AM (#35469482)
> C doesn't have strings.

C has string literals though, and those bake a specific string representation into the language (of course libraries can use their own string representation, but those then need at least some conversion function from string literals).

Re:C Strings and my slow descent to madness by b5n on Thursday April 06, 2023 @07:08PM (#35475762)
String literals in C are simply a pointer to an array of bytes (generally speaking), and that's how they should be treated. Considering them as strings in the HLL sense is the entire issue here.
Re:C Strings and my slow descent to madness by tialaramex on Thursday April 06, 2023 @02:32PM (#35472439)
I don't think it's useful to pretend C doesn't have strings when it has string literals.

WUFFS doesn't have strings. That's what a language which doesn't have strings looks like, you can't write "Hello, world" in WUFFS because it involves Strings, which WUFFS doesn't have, and I/O, which WUFFS also doesn't have.

A pretence that C doesn't have strings because it lacks a concrete string type in the language itself also seems like you'd be claiming C++ doesn't have strings, Zig doesn't have strings, and Rust came pretty close to not having strings (for a while it was mooted to make Rust's str just a slice [u8] but today Rust does bless str as a distinct type even though e.g. &str and &[u8] aren't very different)

Re:C Strings and my slow descent to madness by magicalhippo on Thursday April 06, 2023 @05:49PM (#35475010)
C++ before C++11 didn't. I've fixed errors in projects which was due to string literals not being std::string. After C++11 things are more murky due to user literals[1]. I'd lean towards saying the language still doesn't, but yeah, murky.

Zig and Rust I don't know enough about.

And I'm not pretending. C has string literals which are of a non-distinct type. You can't distinguish between a string literal and an array of characters. This is the crucial bit.

The result is that the standard library, and lots of other code, relies on convention alone to pass strings around. This has been and continues to be the source for countless serious bugs. The kind of bugs which are a total non-issue in languages which has strings.

[1]: https://en.cppreference.com/w/cpp/language/user_literal

Re:C Strings and my slow descent to madness by junon on Thursday April 06, 2023 @09:43AM (#35468208)
There's also RapidString, though the original author seems to have disappeared off of the internet. Would be curious to see benchmarks against sds.
Re:C Strings and my slow descent to madness by bluetomcat on Thursday April 06, 2023 @09:46AM (#35468241)
The best way to write C is to treat strings as memory locations with characters and nothing more. Every such memory location has an allocated size, either statically at compile-time (with literals and arrays), or dynamically with malloc and friends. Treat string operations as mere memory operations and don't imagine them to be something else. The str* functions from the standard library are just convenience helpers which are not adding "string" functionality whatsoever.
Re:C Strings and my slow descent to madness by nerdponx on Thursday April 06, 2023 @10:54AM (#35469140)
Maybe you could summarize this by saying that C strings are "strings of bytes", not "strings of characters".
Re:C Strings and my slow descent to madness by lolcatuser on Thursday April 06, 2023 @07:54PM (#35476201)
It's better to say that, in C, characters are bytes. That's why coming at this from the modern perspective of "characters are the things on my screen" is always going to confuse you - C doesn't have bytes, only characters. C programmers (should) understand this the same way Lisp programmers understand that "CAR" and "CDR" refer to "first" and "rest" or Forth programmers understand that "the stack" is the data stack and has no relation to the call stack.
Re:C Strings and my slow descent to madness by nerdponx on Friday April 07, 2023 @11:46AM (#35482896)
So a "char" is a byte, but a "char" is perhaps not a "character", except as a funny coincidence of jargon.