It's quite common to work with non-null-terminated text data in C. You just have to be careful with your terminology, because such data is not, formally, a "string".
If you have some text data that's not terminated by a null character, you obviously have to keep track of its length some other way. The most common way is to keep a separate variable (often called n, size, sz, length, or len) containing the length.
For most operations which work on strings, there is a corresponding way to work with non-null-terminated text data. Here is a table:
| operation |
conventional string |
counted text |
| read from file |
fgets |
fread, read |
| copy |
strcpy |
memcpy |
| compute length |
strlen |
n/a |
| write to file |
fputs |
fwrite, write |
You can create an array containing a non-null-terminated "string" using a trick:
char nonstr[5] = "hello";
Normally, when you initialize a char array with a string literal, the C compiler picks the size, and includes space for the null terminator. That is, when you write
char str[] = "hello";
the array comes out with a size of 6. But when you specify an explicit size, and when it's "too small" by exactly 1, you get a non-null-terminated string. To summarize:
char error[3] = "hello"; /* error: array too small */
char nonstr[5] = "hello"; /* special case: no null */
char string[6] = "hello"; /* normal case: right size, including null */
char extra[10] = "hello"; /* extra space (could contain larger string later) */
char default[] = "hello"; /* default case: compiler picks size 6 */