Reputation: 117
Essentially I am tokenizing a string and strncpy
ing the string found to a structure member, i.e. stringid. It of course suffers from the problem of lack of termination, I have added an extra array space for it, I've no clue how to add it properly.
I had done it like so:
my_struct[iteration].stringID[ID_SIZE-1] = '\0' //updated
I am unsure if that really works, it looks horrible IMO.
Str(n)cpying a null character, or 0, results in a warning generated by GCC and MinGW:
warning: null argument where non-null required (arg 2)
Am I blind on how to do this in a clean manner? I was thinking of memsetting the member array to all zeros, and then copying the string in to nicely fit with null termination. Do you have any suggestions or practises?
Upvotes: 1
Views: 2157
Reputation: 90
I ended-up writing a strncpyz(char* pszTo, char* pszTo, size_t lSize) function that forces the NULL termination. This works pretty-well if you have a library to put it in. Using it also requires minimal code changes.
I'm not keen on the macro approach because somebody will pass a pointer to the wrong macro.
Upvotes: 0
Reputation: 400029
Two things:
strncpy()
has very unexpected semantics, it will always 0-fill the buffer if not totally filled by the string, and it will not terminate the string if it completely fills the buffer. Both of these are weird enough that I recommend against using it.stringID[ID_SIZE]
seems to be doing; that is out of bounds.The best solution is to write a custom version of strncpy()
that is less weird, or (if you know the length of the input) just use strcpy()
.
UPDATE: If the length of your input tokens is static, but they're not 0-terminated in the source buffer due to your tokenization process, then just use memcpy()
and manual termination:
const char * token = ...; /* Extract from tokenization somehow. Not 0-terminated. */
const size_t token_length = ... /* Perhaps from tokenization step. */
memcpy(my_struct[iteration].stringID, token, token_length);
my_struct[iteration].stringID[token_length] = '\0';
I don't see a need to "wrap" the above in a macro.
Upvotes: 3
Reputation: 13806
As others have noted, strncpy
has odd semantics. The idiomatic way to do a bounded string copy is to strncat
onto an empty string:
my_struct[iteration].stringID[0] = '\0';
strncat(my_struct[iteration].stringID, src, ID_SIZE-1);
This always appends a terminating NUL, (and fills at most ID_SIZE characters including the NUL).
Upvotes: 0
Reputation: 987
Actually, null terminating the way you suggested isn't horrible at all and I personally very much like it.
The best way, in my opinion, would be to define it as a macro in similar fashion:
// for char* blah;
#define TERMINATE_DYNAMIC_STRING(str, len) str[len] = '\0';
// for char mytext[] = "hello";
#define TERMINATE_STRING(str) str[sizeof(str)/sizeof(str[0]) - 1] = '\0';
Then you can use it all around your code as much as you want.
On Windows Microsoft gives you the following functions which null terminate when copying string: StringCchCopy
Upvotes: 1