I'm starting a new project in plain C (c99) that is going to work primarily with text. Because of external project constraints, this code has to be extremely simple and compact, consisting of a single source-code file without external dependencies or libraries except for libc and similar ubiquitous system libraries.
With that understanding, what are some best-practices, gotchas, tricks, or other techniques that can help make the string handling of the project more robust and secure?
Strings in C language are an array of characters ended with null characters ('\0'). The null character at the end of a string indicates its end and the strings are always enclosed by double quotes. In C language characters are enclosed by single quotes.
The nine most commonly used functions in the string library are: strcat - concatenate two strings. strchr - string scanning operation. strcmp - compare two strings.
In C programming, a string is a sequence of characters terminated with a null character \0 . For example: char c[] = "c string"; When the compiler encounters a sequence of characters enclosed in the double quotation marks, it appends a null character \0 at the end by default.
Without any additional information about what your code is doing, I would recommend designing all your interfaces like this:
size_t foobar(char *dest, size_t buf_size, /* operands here */)
with semantics like snprintf
:
dest
points to a buffer of size at least buf_size
.buf_size
is zero, null/invalid pointers are acceptable for dest
and nothing will be written.buf_size
is non-zero, dest
is always null-terminated.foobar
returns the length of the full non-truncated output; the output has been truncated if buf_size
is less than or equal to the return value.This way, when the caller can easily know the destination buffer size that's required, a sufficiently large buffer can be obtained in advance. If the caller cannot easily know, it can call the function once with either a zero argument for buf_size
, or with a buffer that's "probably big enough" and only retry if you ran out of space.
You can also make a wrapped version of such calls analogous to the GNU asprintf
function, but if you want your code to be as flexible as possible I would avoid doing any allocation in the actual string functions. Handling the possibility of failure is always easier at the caller level, and many callers can ensure that failure is never a possibility by using a local buffer or a buffer that was obtained much earlier in the program so that the success or failure of a larger operation is atomic (which greatly simplifies error handling).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With