1

I am trying to create a phonebook struct and to assign values to the elements of the array:

#include <stdio.h>
#include <string.h>

typedef struct
{
    char name[30];
    char number[30];
}
phonebook_record;

int main(void)
{
    phonebook_record people[2];
    people[0].name = "my_name";
    people[0].number = "+555-000-0000";
}

The error pops up at the assignment stage:

phonebook.c: In function 'main':
phonebook.c:15:20: error: assignment to expression with array type
   15 |     people[0].name = "my_name";
      |                    ^
phonebook.c:16:22: error: assignment to expression with array type
   16 |     people[0].number = "+555-000-0000";
      |                      ^

What have I missed? Is it the way I define the structure to contain two arrays of chars?

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
Janet
  • 101
  • 1
  • 1
  • 9
  • 3
    One thing you have missed is that C and C++ are different languages. While you're learning C, don't tag questions with C++ or claim to be working with C/C++. – Jonathan Leffler Jun 14 '23 at 12:43
  • strcpy() in the name. You are trying to assign a pointer to a char array. – Martin James Jun 14 '23 at 12:44
  • You can't assign strings in C. You have to use functions like `strcpy()` to copy values around. Or initialize the elements of the array of structures. – Jonathan Leffler Jun 14 '23 at 12:45
  • You can not assign an array with `=`. Either you need to copy it element by element, use a function for that (like `strcpy()`, `strncp()`, `memcpy()`, ...), put the array into a struct. Or you don't copy it and use a pointer. – 12431234123412341234123 Jun 14 '23 at 12:46
  • `strcpy` is the correct solution for beginners. See the linked duplicate. Though just for the record/completeness, it _would_ also be possible to do `people[0] = (phonebook_record) { .name = "my_name", .number = "+555-000-0000" };`. This is known as a _compound literal_, not really a beginner topic - but we can copy structs by value even when they contain arrays. – Lundin Jun 14 '23 at 13:04

2 Answers2

4

While you may initialize an array from a string literal, you cannot perform assignment of a string literal to an array. For that, you must use something like strcpy.

Initialization:

#include <stdio.h>
#include <string.h>

typedef struct
{
    char name[30];
    char number[30];
}
phonebook_record;

int main(void)
{
    phonebook_record people[2] = {
        { .name = "my_name", .number = "+555-000-0000" }
    };
}

Copying the values (individual characters) of a string literal to an array:

#include <stdio.h>
#include <string.h>

typedef struct
{
    char name[30];
    char number[30];
}
phonebook_record;

int main(void)
{
    phonebook_record people[2];
    strcpy(people[0].name, "my_name");
    strcpy(people[0].number, "+555-000-0000");
}
Oka
  • 23,367
  • 6
  • 42
  • 53
  • I suggest using `strncpy()` instead, to avoid future buffer overflows when changing the declaration of `name` or `number` or the data. – 12431234123412341234123 Jun 14 '23 at 12:51
  • 1
    @124312341: `strncpy` is a very dangerous function, because if the string is too long to fit into the buffer, `strncpy` will not write a terminating null character.. It would be safer to use `snprintf` with the `%s` specifier instead. – Andreas Wenzel Jun 14 '23 at 12:54
  • @12431234123412341234123 `strncpy` is a [nightmare function](https://man.openbsd.org/strncpy). `snprintf` is the safer version of `strcpy`. Or use `strlcpy` if available. – Oka Jun 14 '23 at 12:54
  • @12431234123412341234123 Check out this post I once wrote about that topic [Is strcpy dangerous and what should be used instead?](https://software.codidact.com/posts/281518) – Lundin Jun 14 '23 at 12:55
  • And actually... probably... always using soon-to-be standard `memccpy` over `strncpy`. – Lundin Jun 14 '23 at 12:59
  • @AndreasWenzel @Oka Yes, `snprintf()` would probably be even better. But i still argue that `strncpy()` is better than a plain `strcpy()` when you don't check the string length vs buffer size. When the buffer is too small, you get UB with `strcpy()` imidiatly. With `strncpy()` you only get UB when the input string is too long, you don't set any `char` to `'\0'`, you don't use a buffer that is 1 `char` longer with the value `0` AND you read the the string for long enough. – 12431234123412341234123 Jun 14 '23 at 13:00
  • @Lundin I don't think `strcpy()` is dangerous or bad or anything, just that you have to check that your input will fit. – 12431234123412341234123 Jun 14 '23 at 13:04
  • @12431234123412341234123 The problem isn't that `strcpy` is dangerous. The problem is that `strncpy` is dangerous. It has a well-known design flaw - it does not add null termination, because the function was _never_ intended to be a safe version of `strcpy` to begin with, or even to be used on null terminated strings. It _zero pads fixed-length strings_. Do you work with fixed-length strings or even know what they are? If not, then don't use `strncpy`. Again, check out the link I posted above. – Lundin Jun 14 '23 at 13:07
  • @Lundin `strncpy()` isn't dangerous when used correctly. It's origin doesn't matter. `char buffer[n+1]; strncpy(buffer,source,n); buffer[n]='\0';` is safer than `char buffer[n+1]; strcpy(buffer,source); buffer[n]='\0';` (yes `snprintf()` is better still, or `strlen()` + `memcpy()`). – 12431234123412341234123 Jun 14 '23 at 13:17
  • @1243123: The function `strncpy` is also inefficient, because it pads the entire destination buffer with zero bytes, when a single zero byte would probably be sufficient. This is especially inefficient when dealing with small strings and large memory buffers. – Andreas Wenzel Jun 14 '23 at 13:21
  • 1
    @12431234123412341234123 The correct use of `strncpy` is to zero pad fixed-length strings on ancient Unix/BSD computers. And it is dangerous because in ordinary programming you'd declare an array as `buffer[n]` and then expect it to copy like every other function in the rest of the C standard library, `strncpy(buffer, source, n)`. Speaking of having the declaration in one place, the call in another place and not checking the size before use... – Lundin Jun 14 '23 at 13:26
3

In C, you cannot assign a value directly to an entire array, except during initialization (i.e. when declaring the object). Here is an example:

int main(void)
{
    phonebook_record people[2] =
    {
        { "my_name",   "+555-000-0000" },
        { "my_name_2", "+555-000-0001" } 
    };
}

If you want to change the content of an null-terminated character array after the object has been declared, you can either assign values to the individual elements of the array (i.e. the individual characters), or you can for example use use the function strcpy to change the content of several characters at once. Here is an example:

int main(void)
{
    phonebook_record people[2];

    strcpy( people[0].name, "my_name" );
    strcpy( people[0].number, "+555-000-0000" );
}

However, using the function strcpycan be dangerous, because if there is not enough space in the destination memory buffer, then you will have a buffer overflow. Therefore, it would generally be safer to use the function snprintf instead, like this:

int main(void)
{
    phonebook_record people[2];

    snprintf(
        people[0].name, sizeof people[0].name,
        "%s", "my_name"
    );
    snprintf(
        people[0].number, sizeof people[0].number,
        "%s", "+555-000-0000"
    );
}

Instead of overflowing the destination buffer, the function snprintf will truncate the string, if it is too long. If you want to detect whether such a truncation happened, you can check the return value of snprintf.

However, although it is not possible to assign a value to an entire array outside a declaration, it is possible to assign a value to an entire struct. Since the two char arrays are members of a struct, assigning a value to the struct will indirectly assign a value to the entire content of both arrays. One way to assign a value to a struct is to assign it the value of a compound literal. Here is an example:

int main(void)
{
    phonebook_record people[2];

    people[0] = (phonebook_record) { "my_name", "+555-000-0000" };
}
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • Make sure to check the string length and buffer size before you use `strcpy()`, otherwise you risk UB. – 12431234123412341234123 Jun 14 '23 at 12:49
  • @12431234123412341234123 Both target and destination sizes are known in this case, so that's a non-issue. (`memcpy` would be ever so slightly faster however.) – Lundin Jun 14 '23 at 13:01
  • @1243123: Yes, you are correct that using `strcpy` can be dangerous. I have therefore added a safer alternative solution to my answer. – Andreas Wenzel Jun 14 '23 at 13:04
  • 1
    @Lundin Only as long as you don't change the code. The declaration with the buffer size and the string are in different locations, it isn't unlikely that someone who changes one of both forgets to adjust the other one. In other words, such code is harder to maintain. – 12431234123412341234123 Jun 14 '23 at 13:10
  • @12431234123412341234123 Someone who isn't familiar with just how cumbersome and problematic C strings are to work with should probably not be maintaining C code in the first place :) – Lundin Jun 14 '23 at 13:11