Kenshin R.
Kenshin R.

Reputation: 117

C: How to copy over null terminator to structure member, in cleaner way?

Essentially I am tokenizing a string and strncpying 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

Answers (4)

davep
davep

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

unwind
unwind

Reputation: 400029

Two things:

  1. Beware that 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.
  2. Never index an array with it's size, like 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

fizzer
fizzer

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

Grim
Grim

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

Related Questions