Operands are not assignment compatible
Asked Answered
B

2

6

I have the following code:

struct something {
  char *(*choices)[2];
};
char* arr[2] = {"foo", "bar"};

int main(void) {
  struct something obj;
  obj.choices = &arr;
  return 0;
}

When I compile this using a normal C compiler (gcc), I get no errors. However, I am compiling for the Z80, and it raises a ERROR (152) Operands are not assignment compatible, which is described as:

An attempt was made to assign a value whose type cannot be promoted to the type of the destination.

I fail to understand how the types of &arr and char *(*choices)[2] could differ. What can I do to fix this ?

(I'm using the Zilog z80 compiler, which is part of ZDS 5.2.0)

Baldpate answered 11/5, 2019 at 12:44 Comment(6)
"normal C compiler (gdb)" you mean gcc here instead of the debugger gdb, right?Shellback
It seems you hit a bug. You might want to raise an error with the vendor.Shellback
Does the compiler claim to support ANSI C ?Emotionalism
That repo isn't the compiler. The compiler came from Zilog. You'll have to ask them what version of the C language they support.Epicurean
Maybe try using memcpy? If only sizeof(char*(*)[2]) == sizeof(void*) you could void *val = &arr; memcpy(&obj.choices, &val, sizeof(void*)).Satan
Thanks for reporting the errors alk and and Raymond, I've updated the question. To all those who suggested it was a bug, I'll try to report it to Zilog and I'll edit/add an answer if there's a fix or it turns out it was something else.Baldpate
B
4

Zilog support has claimed that it is in fact not a compiler bug, and that the original code does not compile as it is not strictly ANSI C. It is accepted by GCC because the compiler is "lenient" and has added a few more syntax rules that go beyond ANSI C specification. Here is the full response:

The GCC compiler, while very good, is not necessarily a perfect implementation of the C Standard. Over the years I have seen a few cases where widely used compilers, like MSVC++ and, less frequently, GCC accept syntax that is not strictly ANSI C when that syntax seems to be a harmless quasi-extension of standard C and there’s no danger of its being interpreted in some alternate, legitimate meaning. This may be another instance of that.

There’s a fine point of C syntax involved and here is my understanding of that point, along with why perhaps GCC allows the customer’s original syntax. Once a function pointer, e.g. a properly defined variable fnPtr, has acquired a definition, it is allowed to invoke it without the preceding * indirection operator through an expression like

result = fnPtr(x); // This is legal syntax…

result = (*fnPtr) (x); // … even though this is “more correct”

The reason why the 1st syntax shown above is allowed is that the parentheses enclosing parameter x are regarded as a C operator whose type is “pointer to function”. So the presence of those parentheses makes the indirection operator unnecessary when the function pointer is actually used for making a function call. However, in a case like this customer code where you are just using the function pointer in an assignment statement, this doesn’t come into play and so those operands are not, in fact, strictly assignment compatible. However, a user who is not an expert language wrangler can hardly be blamed for expecting that if the function pointer can be used without the * in one place, it should also be acceptable in other contexts. This might be why the GCC developers have apparently decided to accept the user’s syntax.

Here is an alternate version that compiles:

struct something {
  char *(*choices[2]);
};

char* arr[2] = {"foo", "bar"};

int main(void) {
  struct something obj;
  *obj.choices = &arr;
  return 0;
}
Baldpate answered 22/5, 2019 at 17:56 Comment(0)
E
3

It's probably some kind of weird compiler bug. This code compiles fine:

struct something {
  char *** choices;
};

char * arr[2] = {"foo", "bar"};

int main(void) {
  struct something obj;
  obj.choices = &arr;
  return 0;
}

And I'm afraid that it's the only workaround that is the most compatible with original idea.

Eskisehir answered 11/5, 2019 at 18:35 Comment(3)
obj.choices has the type of char ***, while &arr has the type of char*(*)[2]. Assignment is from incompatible pointer type. You can change char ***choices to char **choices and obj.choices = &arr into` obj.choices = arr` and compile without any warnings.Satan
@Satan thanks this works, however would you mind explaining how this works ? my (very limited) knowledge of pointers telle me that choices is a pointer to a string, but then how come we can store an array there ? is an array of strings a pointer to a string ?Baldpate
The rule is: an array of type is implicitly converted to a pointer to type (except some corner cases, ex. sizeof) see C11 6.3.2.1p3. So an array of char* is converted into a pointer to char*, so to a char**. A "string" in C language, is just z zero byte terminated char array. There is nothing special in char*. You don't store an array, &arr is a pointer to an array of 2 pointers to char, ie. char*(*)[2]. arr has the type of an array of 2 pointers to char, but is implicitly converted to a pointer to a pointer to char.Satan

© 2022 - 2024 — McMap. All rights reserved.