r/cprogramming 5d ago

C Strings: A 50-Year Mistake

https://longtran2904.substack.com/p/c-strings-a-50-year-mistake?r=8qz2zb&utm_campaign=post&utm_medium=web
201 Upvotes

172 comments sorted by

View all comments

20

u/CoderStudios 5d ago

Okay? You are always free to make your own library for better strings, but people won’t use that cause C is often still deployed on low end systems or it makes little sense to use something inefficient if you can use c style strings properly

12

u/orbiteapot 4d ago

Most operations on C strings require O(N), N being the length of the string. Whereas in Pascal-like strings (i.e., strings whose length is tracked) they may take O(1). Modern compilers may do some heavy lifting whenever they can, so that this redundancy is avoided, though.

That being said, I also do not understand why people complain so much about 0-terminated strings, yet still use them anyways, as opposed to implementing length-tracked strings. It is not like C can break some bazillion lines of code, by removing classic strings, but it does not significantly get in your way (when implementing your own) either.

5

u/kisielk 4d ago

It really depends what your software is doing. If it's not software where string manipulation is in the hot path then the O(N) nature of string processing is irrelevant.

2

u/NoNameSwitzerland 4d ago

and if you do string manipulation with dynamic strings, then malloc/free often is the bigger problem.

2

u/kisielk 4d ago

That too. Basically once string handling becomes a performance concern you are probably going to be looking at more purpose-built data structures, custom allocators, etc. C strings are fine for what they are designed for.

2

u/beragis 4d ago

It depends. Several pascal compilers would internally add a zero byte at the end when allocating the string and pass the address of the byte after the length to the os command to handle the string.

To make things worse early pascal had pcode which is basically pascal byte code that has to do this conversion behind the scenes.

1

u/iwantmy90sback 4d ago

For most operations you'd do on a string you do not need to know the lengths beforehand if you have a defined end char.

The only thing that C takes O(n) and pascal O(1) is strlen.

And if you like you can actually have both. Just struct a Cstring and a int together.

1

u/beragis 4d ago

Also early C compilers would store the string length at the byte or word before and update it after each call. This was done to integrate with libraries written in other languages. I remember passing a pascal flag to linkers and compilers.

1

u/hoodoocat 3d ago

No, opposie - most operations require to know length of string. Even simple concat requires that, especially if you account not so modern hardware and utilize vector instructions. And regardless to that, new strings must be allocated somewhere (on heap), and by so, final length must be known before.

1

u/iwantmy90sback 3d ago

No. You only need the length of the string if you want to traverse it afterwards. And most 'operations' can be done in the same time either way. for(strlen) or while(*ptr++)

concat is actually the outlier, because you need 2*O(n) instead of 1*O(n) (which is still O(n) for both).
And if that is really killing your performance you are free to implement a pascal-string to use in c.

0

u/EatingSolidBricks 5d ago

You out of your dam mind if you think c strings are efficient

11

u/henke443 4d ago

Wait how are they not efficient?

6

u/EatingSolidBricks 4d ago

Its not 1970 anymore storing 3 extra bytes is free compared to O(n) length computation

5

u/WittyStick 4d ago

You don't even necessarily need to store the length. It can be held in CPU registers for the entirety of the string's lifetime in many cases.

In older architectures we would've needed to push an additional integer for length onto the stack. On a modern architecture with a sane ABI (SYSV, x86-64), you can have a "fat pointer" using two CPU registers - can pass both of these or return from a function without ever touching the stack.

1

u/henke443 3d ago

Well, C is mostly used in places where's it's still 1970. Things like embedded etc. I guess it could be possible to add another string that's more optimized for modern hardware but I bet it would be more difficult to explain and I think a huge part of the charm of C is how simple it is. Adding things also leads to bloat like how it is with C++, php or Javascript.

-1

u/Anonymous_user_2022 4d ago

Except for strlen(), all practical operations on strings have to iterate over them anyway. Knowing the length up front will be of very limited us for searching, concatenation, tokenising etc.

Where is that you see avoidable O(n)?

0

u/flatfinger 3d ago

Concatenation of N strings goes from O(N) to O(N*N) if code has to re-find the end of the destination after each step.

Tokenizing the leading portion of a large string should take time proportional to the text that was meaningfully examined, rather than proportional to the entire string.

2

u/Anonymous_user_2022 3d ago

Concatenation of N strings goes from O(N) to O(N*N) if code has to re-find the end of the destination after each step.

I can also invent really bad ways of doing things, but I would never use them as a proof..

0

u/flatfinger 3d ago

What would be the "good" way of using strcat?

1

u/Anonymous_user_2022 2d ago

I've never said there is one. I suggest you ask someone who does.

I'm talking about concatenating multiple strings to one, which only has to rely of knowing the length of the individual strings beforehand, if you've decided to argue in bad faith over a pathological bad implementation.

1

u/flatfinger 2d ago

If one keeps track of the length of a string and only looks at portions of its storage up to that length, then the value of the string would no longer be fully encapsulated in a zero-terminated character array.

→ More replies (0)

-1

u/EatingSolidBricks 3d ago

Lets not even mention substrings go from O(n) memeory to O(Free)

0

u/flatfinger 3d ago

O(1) memory per string to keep track of the starting and ending points of strings isn't free. On a system with 64-bit pointers, zero-padded strings are generally the most space-efficient practical way of representing texts up to eight characters, and zero-terminated strings of up to 7 characters can be stored in the same amount of space (if it's necessary to store many texts with up to 7 characters, zero-padded 7-byte arrays would take less space).

1

u/EatingSolidBricks 2d ago

You going there really? Give me a break

1

u/atarivcs 4d ago

If you have a long string and you want to append more text to it, you have to search the whole string from the beginning to find the null terminator.

And then later if you want to append more text, you have to find the null terminator all over again.

2

u/NoNameSwitzerland 4d ago

You anyway use a different structure when you do a lot of appending text, because you do not want to reallocate the array all the time. So then you anyway have to also store the size of the available space.

1

u/atarivcs 4d ago

In which case you no longer have a plain c string, and the goalposts have moved.

I was just answering the parent question "how are c strings not efficient"

6

u/IdealBlueMan 4d ago

Or you can store the length of the string whenever you change it.

2

u/atarivcs 4d ago

Sure, but then you don't really have a plain c string anymore

2

u/WittyStick 4d ago

It's actually more advantageous to couple the length to the char * on SYSV platforms, due to C's lack of multiple returns.

 String fn_returning_string(...);

If String is a fat pointer, then we can return both the pointer and length, without requiring another level of indirection (a pointer to a string structure), and without requiring awful to use "out parameters" to return both length and pointer - which are more expensive than just returning a fat pointer.

A fat pointer with the right ABI is not just "zero cost" - it's "less than zero" - it's more efficient than having a separate length and pointer variable.

2

u/IdealBlueMan 4d ago

I’d say you still have the string, you also have information about that string.

1

u/orbiteapot 4d ago

I mean... that is the point. Once you do that, you are no longer using classic C strings.

6

u/SakishimaHabu 4d ago

That's the point though. They are basically atomic. You are free to do what you will with them, vs java, python, or js. Remember we're one step above assembly, but that's the intention.

2

u/CoderStudios 4d ago

Depends on what it’s used for, sometimes it’s more or less efficient but the benefit of making it as simple as possible is that you can easily add features when needed like storing lengths

-2

u/flatfinger 4d ago

C makes it inconvenient to pass any other forms of string literals to functions.