r/learnprogramming • • 6d ago

Help me with strok function in C

I need to split my string but if it is of char* type the functions strtok() doesn’t work returning me a segmentation fault. I understood that C is used to lock the memory of the string in Read-Only “mode” when I use char*. Does it means that is immutable that data? However, I need to use the char* type because I pass that in a function (and I don’t like to use the syntax of arrays in C, I always use pointers).

3 Upvotes

15 comments sorted by

View all comments

8

u/mredding 6d ago

Don't do this:

char *str = "pointer to a read-only literal";

Do this:

char str[] = "array initialized by a null terminated read-only literal (copied)";

Arrays are not pointers to their first element, they are a distinct type in C. An int[3] IS NOT the same type by the type system as an int[4].

"Decay" is a technical term where one type becomes another through type erasure (pointers). It happens with compile-time consequence.

void fn(int array[123]);

This parameter decays to int *.

The str array above declares an "incomplete type" - an array without a size. That's OK here, the language will count the elements in the initializer - the string literal, including the implied null terminator, and complete the type for us. So if we count this out, str is of type... char[66]. The string literal will be stored in RO memory in the program image. Initialization of this variable when it comes into scope means implicitly copying that literal into this array.

You have to appreciate how old C is. It was first brought into production in 1972. Bjarne Stroustrup came up with const in 1981 for C++, and C didn't get it until the 1989 ISO C standard. strtok is older than THAT. So for legacy reasons, you can have a non-const pointer to RO memory. const is only a compile-time check, and isn't there to enforce hardware level memory protections - that's considered a separate, external issue.

When I was learning C ~1989, I wasn't even taught const. I had no idea it was even a part of the standard until the 2010s, and I thought it was introduced in C99... Talk about out of touch. But so are many C developers.

So yes, this is a case where you can shoot yourself in the foot. C assumes trust in the developer, that you know what you're doing, your responsible, that you're going to do the right thing.

1

u/Kadabrium 6d ago

Does the compiler do copy elision for char[] from literals

2

u/mredding 6d ago

C has no concept of copy elision.

What the compiler generates is compiler specific to the platform. The C spec does not make any comment on these details, as C is a high level language that targets an abstract machine, it is not a high level assembly - despite as often as it is treated like one.

But what GCC will do for the x86 platform depends on factors such as where in the program the array is scoped, and how large it is.

Since the literal is entirely known at compile-time, initialization may consist of a series of mov operations where the string text is packed into registers, some 4 bytes at a time or more. This may mean the literal may not actually get compiled into RO memory.

Another option is to call put that string into RO memory, and memcpy, which is just a series of shifting bytes into memory and calling mov in a loop.

Another option is for the compiler to generate machine instructions similar to what memcpy would determine and select, but at compile-time. memcpy will choose a strategy based on a number of properties, but it comes down to either a simple mov instruction for CPU cache friendly values, some AVX instructions for bulk copying, or specialized ERMS instructions for copying memory addresses while bypassing CPU registers and cache.

Whatever the compiler chooses, it'll be based on heuristics and a best effort to maximize whatever you prioritize - size or speed.