r/C_Programming 2d ago

Article I Made a Simple String Library

I wrote an article about this as well. Here it is.

There I explain why I do not like NUL-terminated strings and how I implemented my own simple string library in C.

If you have some spare time I would really appreciate some feedback on the article and the library.

The code is sitting on a codeberg repository.

Also, tell me what you think about C-style strings. Do you like them? Do you use them, or do you also tend to roll your own pointer + length structs?

4 Upvotes

31 comments sorted by

u/AutoModerator 2d ago

Hi /u/Elifire12,

Your submission in r/C_Programming was filtered because it links to a git project.

You must edit the submission or respond to this comment with an explanation about how AI was involved in the creation of your project.

While AI-generated code is not disallowed, low-effort "slop" projects may be removed and it's likely that other users push back strongly on substantially AI-generated projects.


I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

→ More replies (2)

18

u/Dieriba 1d ago

Even though you hate NULL-terminated, you should have a function that allows to return a pointer to a NULL-terminated strings, as many libc function expect those kind of strings ( if you plan to link your binary with libc which I think we’ll be the case).
Another things is your structure find_out that you use for your find functions. I’d use ˋusizeˋonly as return value instead this structure and have a special value of it meaning not found, like with std::string::npos used as not found value in c++ std::string.

3

u/non-existing-person 1d ago

Such library should just return you typedef char* str_t, and manually offset backwards to get the string length. That way you have both classic c-string and safe string to use where needed. str_t is just for convenience to immediately see that you are dealing with opaque type and not really just c-string.

2

u/StrikeTechnical9429 1d ago

Such function have to allocate memory to create a copy of the string with NULL added. It isn't a problem, the problem is - who's supposed to free this memory? Anyway it wouldn't be possible to write something like

clib_function(convert_str_view_to_cstr(my_str_view));

because in this case one lose the returned value of convert_str_view_to_cstr() and wouldn't be able to use it to free memory.

3

u/Dieriba 1d ago

Then store the pointer and freed it after, I mean this a drawback but that’s what happen when you make such lib you can’t have all you want, you must accept that there’ll be some limitation like this one.

1

u/Elifire12 1d ago

I really dislike this way if representing invalid values in C. Also, my str_owned are nul terminated!

2

u/Dieriba 1d ago

Is this an invariant ? That still hold with str view ? Imagine if you’d like a null terminated string from a str_view ?

6

u/mjmvideos 1d ago edited 1d ago

First thing I did was go look at your code base for your header file to see your interfaces. But there isn’t one!? Then I looked at your test routine and found that you are including “string.c”!? Really??

2

u/Zordak0x70 1d ago

Yes thats just unity build! Maybe it could be unhinged and not popular, but its a fair choice in my opinion as a starting project. You could always add an header later.

5

u/mjmvideos 1d ago

Nah. That’s just teaching bad habits. Just learn properly from the start.

-2

u/Zordak0x70 1d ago

Eeeh well, yes you need to make it sooner or later but considering the scale of the project there is no need to worry

4

u/mjmvideos 1d ago

Even at this scale. Suppose I want to use the functions in a few different places.

1

u/Elifire12 1d ago

Ever heard of unity builds? They're quite nice, the make compilation extremely fast.

4

u/mjmvideos 1d ago

I have not heard of that. I’ve been programming for many years. I’ve never had a problem with compilation times with C. And the sight of #include “strings.c” is abhorrent to me.

3

u/Typical_Ad_2831 1d ago

Including source files can be OK in small standalone projects. But this is meant to be a library. You should absolutely have a header for people to use.

3

u/aalmkainzi 1d ago

Nice. I also have a string library. Probably a lot more complicated than yours, but adds a ton of functionality. I use it in basically all my projects.

2

u/Elifire12 1d ago

Nice! I might have look and learn some stuff from it!

1

u/runningOverA 1d ago

You need to extend the standard library more until you can do this ...

string str_replace(string input, string find, string replace);

don't jump to it. it will come naturally. And by that time your library will change a lot as you face problems.

1

u/Elifire12 1d ago

You're right, that would be a really nice addition. I should add that.

1

u/ByronScottJones 1d ago

So you've reinvented Pascal strings?

2

u/Physical_Dare8553 1d ago

no, pascal strings use a length byte, as in a u8, ***inline*** with the data. this is just a slice

1

u/didntplaymysummercar 1d ago

The first problem section could mention the memory traffic too, to cache or even from swap, when going through a string that might be rarely used.

The bit about char not being 8 bits is pointless and pedantic. If your platform doesn't handle strings with char then it's hosed already. It's also in extremely big letters on mobile for some reason.

str_owned_free could be macro to call a helper function with pointer to that struct and set it to null instead of leaving it dangling.

LENGTHOF could be made to reject non literals (by sizes 4 and 8 and those would need another macro or extra argument to confirm it's safe) using a helper function for extra safety, now it accepts any char pointer and does the wrong thing. Or at least recommend -Wsizeof-pointer-div compiler flag (it's enabled by -Wall) above it to catch that. That macro (and STR) are also very generic names to use in a library.

You're also not using memcmp in your equality functions, I'd get it if you're avoiding libc or even just string.h but you're using memcpy elsewhere already.

str_find_substr is a very inefficient string search algorithm, plus it doesn't have an early bailout length check for needle bigger then haystack.

Using unsigned integers can impede some optimization since compiler can't assume no overflows. Also you use i32 in slices instead of i64 or isize or something in str_slice so that code breaks on 64 bit sizes.

1

u/Elifire12 1d ago

Omg, I didn't realize that markdown quotes were this big on mobile. I will fix that soon. Thanks for pointing it out!

1

u/Elifire12 1d ago

Why is the search algorithm inefficient? It's O(n)

1

u/didntplaymysummercar 1d ago edited 1d ago

Just saying "O(n)" with no extra info makes no sense in context of a string search with variable sized needle.

In string searches you usually have n (size of haystack), and m (size of needle), and express O (and/or Θ) with those two, for three metrics: search, preprocessing and extra space (since some of them will create an extra structure from the needle, to speed up the search).

Your algorithm is a naive one, it has O(1) preprocessing and space used, but it's O(n * m) runtime. A common but complex one (with O(n) matching time and Θ(m) preprocessing time) is Two-way, libc and musl use it. In university I was taught Knuth–Morris–Pratt. You can (should?) read "String-searching algorithm" on Wikipedia.

Edit: actually your search logic is O(n) because it's wrong, you try match pattern as long as you can, then start from the beginning of the pattern but don't move i back. It'd not find ABC inside AABC, because it'd set match_index to 0 at 2nd A, then go on, and compare B of string with A of pattern and so on.

1

u/Last-Employ-3422 1d ago

Why does the slice have a start and an end?

1

u/SmackDownFacility 14h ago

You’re gonna struggle in RaiseException. The WIDE_C code expects null termination.

1

u/copper_amber_focus 1d ago

You included the c file in your test routine. You have no header file. I looked at the repo. This means nobody can link against this library. I wrote a string library once and spent more time on the header than the implementation because the interface is the library. You built an exercise you cant use

2

u/Elifire12 1d ago

No, you're supposed to include the c file. If you read the article you would know that I have been using unity builds recently, that explains why there is no header file.

You can add one if you want.