-3

I have a strange behavior that I can't understand with data in a structure. See my code:

typedef struct _Foo {
    char refDossier[12];
    char refDossier62 [9];
} Foo;

string refStr = "157809110000";
string ref62Str = "02mFqwTo";

strncpy(fooStruct.refDossier, refStr.c_str(), sizeof (fooStruct.refDossier));
strncpy(fooStruct.refDossier62, ref62Str.c_str(), sizeof (fooStruct.refDossier62));

cout << "REF DOSSIER 62: " << fooStruct.refDossier62 << endl;
cout << "REF DOSSIER: " << fooStruct.refDossier << endl;

Here the result:

REF DOSSIER 62: 02mFqwTo
REF DOSSIER: 15780911000002mFqwTo

I don't understand why the value of fooStruct.refDossier62 appears whyle you print the value of fooStruct.refDossier. I know that the data of a structure are contiguous in memory but in my point of view this is not the reason of this strange behavior.

When I run the following code:

for (int i = 0; i< sizeof (fooStruct.refDossier); i++) {
   cout << fooStruct.refDossier[i];
 }
 cout << endl;
 for (int j = 0; j< sizeof (fooStruct.refDossier62); j++) {
   cout << fooStruct.refDossier62[j];
 }

I have a correct output:

157809110000
02mFqwTo

There is therefore no memory overwriting, which I feared.

Any ideas ?

ChrisMM
  • 8,448
  • 13
  • 29
  • 48
Jocelyn
  • 27
  • 1
  • Also be aware of undefined behavior which means anything can happen including but not limited to the program giving your expected output. But never rely on the output of a program that has UB, the program may just crash. – Jason Mar 06 '23 at 13:52
  • 10
    You're forgetting that C-style strings are really called ***null-terminated** strings*. Your arrays needs space for the null-terminator character `'\0'` as well. Since your arrays are not null-terminated, they can't be used by any functions that expects strings, and that include the stream output operators. – Some programmer dude Mar 06 '23 at 13:53
  • 6
    `"157809110000"` is too long for 12-`char` array because a room for terminating null-character is required. – MikeCAT Mar 06 '23 at 13:53
  • 2
    Any ideas? `std::string` ! – 463035818_is_not_an_ai Mar 06 '23 at 13:54
  • 2
    On a totally different note, please read [What are the rules about using an underscore in a C++ identifier?](https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier) The C++ specification doesn't allow you to define a symbol like `_Foo`. – Some programmer dude Mar 06 '23 at 13:55
  • 5
    Also be aware that [`strncpy()`](https://linux.die.net/man/3/strncpy) do NOT put terminating null-character if the input is too long. – MikeCAT Mar 06 '23 at 13:55
  • Minor point: you don't have to do the `typedef struct ... { ... } name;` dance in C++. `struct name { ... };` works just fine. The rules are different from C. – Pete Becker Mar 06 '23 at 14:00
  • @MikeCAT it was the problem ! Many thanks. – Jocelyn Mar 06 '23 at 14:01
  • 2
    Although the question is well asked and well formed, I suspect it's getting downvotes because it's a basic, common problem and therefore the low question score reflects a lack of research as opposed to a poorly asked question. – Wyck Mar 06 '23 at 14:03
  • Unless I have a measurable and concrete performance need, I always prefer `snprintf()` over `strncpy()` as to protect myself against a forgotten '\0'. – YSC Mar 06 '23 at 14:03

1 Answers1

1

This is an example of why you need to be careful with strncpy.

strncpy(fooStruct.refDossier, refStr.c_str(), sizeof (fooStruct.refDossier));

This will copy up to sizeof (fooStruct.refDossier) bytes to the destination buffer. While this prevents the buffer from being overrun, if there are at least sizeof (fooStruct.refDossier) bytes to be copied, which there is in this case, a null terminated byte will not be added.

So fooStruct.refDossier doesn't contain a null terminated string, and attempting to use a string function on it will result in reading off the end of the array, triggering undefined behavior.

The field should be made one element larger:

typedef struct _Foo {
    char refDossier[13];
    char refDossier62 [9];
} Foo;

And strncpy should copy one less than the buffer size, and a null terminated should be explicitly added.

strncpy(fooStruct.refDossier, refStr.c_str(), sizeof (fooStruct.refDossier)-1);
fooStruct.refDossier[sizeof (fooStruct.refDossier)-1] = 0
strncpy(fooStruct.refDossier62, ref62Str.c_str(), sizeof (fooStruct.refDossier62)-1);
fooStruct.refDossier62[sizeof (fooStruct.refDossier62)-1] = 0;
dbush
  • 205,898
  • 23
  • 218
  • 273