1

I have a struct defined similar to the following:

struct csv_headers {
    char field1[256];
    char field2[256];
};

And then later in my code I have:

struct csv_headers fields;
strcpy(fields.field1, str_ary[0]);
strcpy(fields.field2, str_ary[1]);

Where str_ary[n] is some string.

Does this cause undefined behaviour the same way the following code would?

char str[] = "some text";
strcpy(str, "text");

Since strcpy should not be used for string literals?

If so, what is the accepted way to copy a string into a string declared inside a struct? My code example above compiles with no warnings using -Wextra (assuming I typed it correctly - I don't have the code in front of me right now).

I am aware of the potential dangers of using strcpy and don't wish to discuss that here, that is for illustrative purposes only. I just want to know the accepted way of copying a string into a struct containing strings.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
5a5a8
  • 81
  • 3
  • 1
    What's wrong with `char str[] = "some text"; strcpy(str, "text");`? `str` is an array of 10 `char`, so you can `strcpy` 5 `char` into it. – mch Jan 13 '21 at 09:52
  • You can't pass a single character to `strcpy` so yes it is undefined behavior because `strcpy(fields.field1, str_ary[0]);` doesn't make any sense and cannot compile cleanly. Is your question why you can't pass a `char` instead of a `char*` to `strcpy`? – Lundin Jan 13 '21 at 09:52
  • In your example, `str` is *not* a string literal, it’s a regular `char` array, and it’s a totally valid target for `strcpy`. – Konrad Rudolph Jan 13 '21 at 09:53
  • 2
    @Dai Not instead. You should use both. – klutt Jan 13 '21 at 09:54
  • if `str_ary` is `char str_ary[n]` then `strcpy(fields.field1, str_ary[0]);` is wrong, don't know what you are trying but `strcpy(fields.field1, &str_ary[0]);` will work instead of your usage – IrAM Jan 13 '21 at 09:55
  • `"text" is a literal; however you can copy it _to_ a non-literal. – Paul Ogilvie Jan 13 '21 at 09:56
  • @Lundin each element of str_ary is a char*, so like char **str_ary; or something of the sort. "Where str_ary[n] is some string" (NOT some char) – 5a5a8 Jan 13 '21 at 09:57
  • @KonradRudolph I see, I guess I misunderstood string literals here – 5a5a8 Jan 13 '21 at 10:00
  • @Dai I was under the impression -Wextra included the warnings from -Wall, am I mistaken? – 5a5a8 Jan 13 '21 at 10:02
  • @5a5a8 Yes you are. They enable different warnings. And some warnings require both. – klutt Jan 13 '21 at 10:04
  • @klutt Huh - TIL! Thank you :) https://stackoverflow.com/questions/11714827/how-to-turn-on-literally-all-of-gccs-warnings – Dai Jan 13 '21 at 10:15

3 Answers3

2

Does this cause undefined behaviour the same way the following code would?

char str[] = "some text";
strcpy(str, "text");

Since strcpy should not be used for string literals?

str isn’t a string literal, it’s a char[10] (initialised by copying data from a string literal1), and it’s a valid target for strcpy. This code does not cause undefined behaviour; it’s well-defined and valid.

Likewise, fields.field1 and fields.field2 in your example are regular char[]s. That they’re members of a struct isn’t important in this context.

However, be sure that the source is valid: str_ary[0] looks fishy, unless str_ary is an array of zero-terminated strings, i.e. something like char[][].


1 The initialisation of a string array from a string literal happens as if by strcpy, i.e. it’s almost exactly the same as writing

char str[sizeof("some text")];
strcpy(str, "some text");

In fact, compilers could (and at least in some cases do) generate the same code for this initialisation as for the direct assignment. By contrast, char *str = "some text"; doesn’t perform copying, and here str is a pointer to a string literal, and therefore read-only, and not a valid target for strcpy.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
0

Does this cause undefined behaviour the same way the following code would?

Well, your snippet does not cause problems.

char str1[] = "foo"; // str1 is a regular array initialized to "foo"
char *str2 = "bar"   // str2 is a pointer, pointing to a string literal
strcpy(str1, "FOO"); // Ok
strcpy(str2, "BAR"); // Undefined behavior
klutt
  • 30,332
  • 17
  • 55
  • 95
0

As long as the string fits in the target space, even

char str[] = "some text";
strcpy(str, "text");

is well-defined.

char field1[256]; declares an array with room for 256 characters. This allows you to store a null-terminated string of up to 255 characters (the array size, minus one for the null terminator).

char str[256] = "some text"; additionally initializes the array to contain the string "some text". You can still copy a string of up to 255 characters into the array, since that's how much room there is.

char str[] = "some text"; is exactly identical to char str[10] = "some text";. With the empty brackets, str is still an array of characters. What the empty brackets mean is that the size of the array (what would normally go inside the brackets) is determined automatically. There is no other difference compared with brackets with a size in it. For an array of characters, the size is the length of the string plus one for the null terminator.

In all cases involving an array, the string used to initialize the array (if there is one) is stored in the array itself. It isn't accessible in any other way, and if you change the array, the string that was used to initialize it is not present in memory anymore (at least in the sense that there's no way to find it — there's no guarantee that it won't be present in a memory dump, but you have no way to access it from your program).

The critical difference is not between char str[10] and char str[], but between char str[…] and char *p. With a * instead of brackets, char *p declares a pointer, not an array.

char *p = "some text";

does two things. One thing is that it arranges for a string literal "some text" to be stored somewhere that's accessible for the whole duration of the program. Another thing is that it declares a variable p that points to this string literal. You cannot modify the memory where "some text" is stored (in practice, on many platforms, it's in read-only memory). On the other hand, you can make something else point to that memory, and you can change p to point to something else.

char *p = "some text";
char *q;
puts(p); // prints "some text"
q = p;
p = "hello";
puts(q); // prints "some text"
puts(p); '// prints "hello"
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254