У меня такой код:

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

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

Когда я компилирую это, используя обычный компилятор C (gcc), я не получаю ошибок. Тем не менее, я компилирую для Z80, и он вызывает ERROR (152) Operands are not assignment compatible, который описывается как:

Была предпринята попытка присвоить значение, тип которого нельзя преобразовать в тип назначения.

Я не понимаю, как могут различаться типы &arr и char *(*choices)[2]. Что я могу сделать, чтобы это исправить?

(Я использую компилятор Zilog z80, который является частью ZDS 5.2.0)

c z80
5
TrakJohnson 11 Май 2019 в 15:44

2 ответа

Лучший ответ

Поддержка Zilog утверждает, что это на самом деле не ошибка компилятора, и что исходный код не компилируется, поскольку он не является строго ANSI C. Он принят GCC, потому что компилятор "снисходительный" и добавил еще несколько правил синтаксиса которые выходят за рамки спецификации ANSI C. Вот полный ответ:

Компилятор GCC, хотя и очень хороший, не обязательно является идеальной реализацией стандарта C. За прошедшие годы я видел несколько случаев, когда широко используемые компиляторы, такие как MSVC ++ и, реже, GCC принимают синтаксис, который не является строго ANSI C, когда этот синтаксис кажется безвредным квази-расширением стандарта C и его опасности нет. интерпретируется в каком-то альтернативном, законном значении. Это может быть еще одним примером этого.

Здесь есть тонкость синтаксиса C, и вот мое понимание этого вопроса, а также почему GCC допускает оригинальный синтаксис клиента. Однажды указатель на функцию, например правильно определенная переменная fnPtr, получила определение, ей разрешено вызывать ее без предшествующего * оператора косвенного обращения через выражение типа

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

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

Причина, по которой допускается 1-й синтаксис, показанный выше, заключается в том, что скобки, содержащие параметр x, рассматриваются как оператор C, тип которого «указатель на функцию». Таким образом, наличие этих скобок делает оператор косвенного обращения ненужным, когда указатель функции фактически используется для вызова функции. Однако в случае, подобном этому пользовательскому коду, когда вы просто используете указатель функции в операторе присваивания, это не вступает в игру, и поэтому эти операнды, на самом деле, не являются строго совместимыми по присваиванию. Тем не менее, пользователя, который не является экспертом по языку экспертов, вряд ли можно обвинить в том, что он ожидал, что если указатель функции можно использовать без * в одном месте, он также должен быть приемлемым в других контекстах. Возможно, поэтому разработчики GCC решили принять синтаксис пользователя.

Вот альтернативная версия, которая компилируется:

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

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

int main(void) {
  struct something obj;
  *obj.choices = &arr;
  return 0;
}
3
TrakJohnson 22 Май 2019 в 17:56

Вероятно, это какая-то странная ошибка компилятора. Этот код прекрасно компилируется:

struct something {
  char *** choices;
};

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

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

И я боюсь, что это единственный обходной путь, который наиболее совместим с оригинальной идеей.

3
Krzysztof Szewczyk 11 Май 2019 в 18:35