Can't access an array-type member of a structure?

As a part of learning/exercise process, I attemptd to assign a string to an array-type member of a structure using dot (.) operator of the following sketch; but, the relevant line is not compiled. Would appreciate to know the reason which is not clear to me from the error message.

void setup() 
{
  Serial.begin(9600);
  struct date
  {
    char name[6];
    int day;
    int month;
    int year;
  };
  
  struct date birthday;

  /*
  struct date birthday =
  {
    "Rakib", 23, 12, 1980
  }; */

  birthday.day = 23;        //compiled
  birthday.name = "Rakib"; //not compiled
  Serial.print(birthday.name);
}

void loop() 
{
  
}

The problem has nothing to do with the struct. You cannot change an array in the way that you tried. Use this instead

  strcpy(birthday.name, "Rakib");

@UKHeliBob
Your code of Post-2 works well.

Then, why does the following assignment instruction work when accessing an item of the name[ ] array?

birthday.name[0] = 'R';
Serial.println(birthday.name[0]);  //shows: R

Because you are then only accessing a single item in the array, whereas in your code you are trying to access the whole array at once

As I said, the fact that the array is in a struct is not relevant

In the following codes, I can access the whole array and also an item of the array.

Serial.println(birthday.name);
Serial.println(birthday.name[0]);

The concern to me is that --
while the following code works:

birthdat.day = 23;     //edit

Then the under-mentioned code should work (logically)?

birthday.name = "Rakib";

The struct is irrelevant in this context - you can't assign a C string like that.

C-style arrays have weird quirks, they don't obey the usual value semantics of the language.

To get value semantics, either use a C++ array (std::array), or wrap it in a struct:

#include <array>
#include <cstdio>

struct S1 {
    struct {
        char data[6];
    } name;
};

struct S2 {
    std::array<char, 6> name;
};

int main() {
    {
        S1 s;
        s.name = {"12345"};
        std::puts(s.name.data);
    }
    {
        S2 s;
        s.name = {"abcde"};
        std::puts(s.name.data());
    }
}

Otherwise you'll have to use memcpy or strcpy or some equivalent element-by-element copy function.

Edit: unfortunately, the example triggers a bug in previous versions of GCC: c++ - Initializing std::array<char,x> member in constructor using string literal. GCC bug? - Stack Overflow
It works fine in Clang and MSVC and the latest version of GCC, though.

As AWOL and I have said, the struct is irrelevant

You cite

birthday.date = 23;

as an example of what you can do, but they are not equivalent because date is not an array, it is a single int whereas name is an array

Given an array of chars

char name[6];

then you simply can't do

name = "Rakib";

Interestingly, the following codes work though they don't meet my demand:

struct date birthday = {.name = "Rakib"};  //designated initialization
Serial.println(birthday.name);    //shows: Rakib

Another equivalent way using structure pointer.

struct date birthday;
struct date *ptr;
ptr = &birthday;
strcpy(ptr->name, "Rakib");
Serial.println(birthday.name); //shows: Rakib

That's an initialisation, not an assignment.

1. Variable declaration:

byte x;  //memory space is not allocated

2. Variable creation:

x = 0x12;  //memory space is allocated

3. Variable initialization:
I think that it is same as Step-2.

4. Variable assigment:
I think that it is same as Step-2 as there is an assignment operator.

Would be glad to know your definitions with examples.

Could you please correct the misleading comments in the code snippets in points 1 and 2?

Thanks.

https://www.google.com/search?q=c%2B%2B+define+declare+initialize

brings you there:

https://stackoverflow.com/questions/23345554/the-differences-between-initialize-define-declare-a-variable

1 Like

just to throw another spanner in to the works! :sweat_smile:

if you use pointers, that seems to be perfectly doable (though not sure how 'safe' that is!)

#include<stdio.h>

int main() {
    //example 1
    //assigning c-strings to struct variables
    struct{
        char str[12];
        char *ptr;
    } c;
    
    /*
    c.str = "str test"; //error: assignment to expression with array type
    */
    c.ptr = "ptr test"; //this is accepted
    printf("%s\n", c.ptr); //"ptr test" printed out
    
    //example2
    //struct initialistation
    struct{
        char str[12];
        char *ptr;
    } d = {"str test", "ptr test"};
    
    printf("%s\n%s", d.str, d.ptr); //"str test" and "ptr test" printed out
}

I assume that you ignored the compiler output such as

ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
     c.ptr = "ptr test"; //this is accepted

casting seems to make the warnings go away! (but again not a C/C++ expert here! :stuck_out_tongue: )

c.ptr = (char*)"ptr test"; //this is accepted

This is what I know from literature that "memory space is not allocated" when a variable is declared and not defined. Variable creation refers to assigning a value to it and this is the time when memory space is allocated for that variable/identifier.

Well, my definition would be irrelevant, the standard is the only authority when it comes to these things.

First off, declarations can be definitions. If you really want the details, read [basic.def] in the C++ standard. I'll quote the highlights:

A declaration may introduce one or more names into a translation unit or redeclare names introduced by previous declarations. If so, the declaration specifies the interpretation and semantic properties of these names.

A declaration is said to be a definition of each entity that it defines.
[Example 1: the following are definitions

int a;                       // defines a
int f(int x) { return x+a; } // defines f and defines x

whereas these are just declarations

extern int a;  // declares a
int f(int);    // declares f

— end example]

Assignment is pretty straightforward [expr.ass]:

In simple assignment (=), the object referred to by the left operand is modified by replacing its value with the result of the right operand.

If the right operand is an expression, it is implicitly converted to the cv-unqualified type of the left operand.

Initialization is much more complicated, so I'll refer you to [dcl.init] and give some examples:

int a;
// No initializer is specified, so it is default-initialized.
// For scalar types like int, default-initialization does nothing, 
// so a is uninitialized.
a = 1;
// This is ordinary assignment, not "variable creation" or "initialization"

int b = 2;
// This is copy-initialization.
int c {3}; 
// This is direct-initialization.

What does that mean? What literature? All variable declarations have a corresponding definition somewhere, so it doesn't make a whole lot of sense to say that declaration does not cause allocation.

"Variable creation" is not a term that I am familiar with.

Using C-style casts to make warnings go away is a terrible idea. See https://en.cppreference.com/w/cpp/language/explicit_cast:

  1. When the C-style cast expression is encountered, the compiler attempts to interpret it as the following cast expressions, in this order:

a) const_cast< new-type>( expression);

b) static_cast< new-type>( expression), with extensions: pointer or reference to a derived class is additionally allowed to be cast to pointer or reference to unambiguous base class (and vice versa) even if the base class is inaccessible (that is, this cast ignores the private inheritance specifier). Same applies to casting pointer to member to pointer to member of unambiguous non-virtual base;

c) static_cast (with extensions) followed by const_cast;

d) reinterpret_cast< new-type>( expression);

e) reinterpret_cast followed by const_cast.

The first choice that satisfies the requirements of the respective cast operator is selected, even if it cannot be compiled (see example).

In other words: it can perform pretty much any nonsensical cast you can imagine, without any type checking by the compiler. The resulting reinterpret_cast is almost always invalid, see reinterpret_cast conversion - cppreference.com. A C-style cast can also cast away const, which is what you're doing here, which is a recipe for disaster, because now the compiler can no longer check whether the variables that you're writing to are actually writable (which would be undefined behavior).

In short, avoid the use of C-style casts, especially for pointers, and never use it to quickly work around a compiler error or warning.

The correct solution in this case is to use an immutable pointer (which is the right thing to do to point to immutable string literals):

struct {
    char str[12];
    const char *ptr;
};

If you absolutely must store a string literal in a non-const pointer, use an explicit const_cast<char *>("lit"), then the readers of your code can at least see that you're doing something fishy. If you then later use that pointer to write to the string literal (with absolutely no way for the compiler to check), it's your own fault when things come crashing down at runtime.

Then why does the "Introduction to C Programming" chapter of a textbook say --
A declared variable in a program is defined either by the user or by the program.

In the following program, I have declared a variable (unsigned int x;) which has been defined (value assignment) by the program (code).

unsigned int x;
x = analogRead(A0);