Грязный указатель на функцию: как убрать предупреждение?

Как я спросил и ответил в этом посте. У меня есть следующий пример кода.

#include <stdio.h>

char foo()    { return 'a'; }
char bar()    { return 'b'; }
char blurga() { return 'c'; }
char bletch() { return 'd'; }

char (*gfunclist[])() = {foo, bar, blurga, bletch};

char (*(*x())[])()
{
  static char (*funclist[4])() = {foo, bar, blurga, bletch};
  return funclist;
}

int main() 
{
  printf("%c\n",gfunclist[0]());

  char (**fs)();
  fs = x();
  printf("%c\n",fs[1]()); 
}

Мои вопросы

  • Почему
    return funclist (с "предупреждением: возврат из несовместимого типа указателя")
    а также
    возвращение &funclist
    как работает?
  • Я получаю предупреждение в строке 21 (fs = x();)
    предупреждение: назначение из несовместимого типа указателя
    , Как убрать это предупреждение?

ADDED

С помощью AndreyT. Я мог бы получить следующий код, который не имеет предупреждений.

#include <stdio.h>

char foo()    { return 'a'; }
char bar()    { return 'b'; }
char blurga() { return 'c'; }
char bletch() { return 'd'; }

char (*gfunclist[])() = {foo, bar, blurga, bletch};

char (*(*x())[])()
{
  static char (*funclist[4])() = {foo, bar, blurga, bletch};
  return &funclist;
}

int main() 
{
  printf("%c\n",gfunclist[0]());

  char (*(*fs)[4])();
  fs = x();
  printf("%c\n",(*fs)[1]()); 
}

И это менее грязный код с помощью Peoro.

typedef char (*funptr)();

funptr gfunclist[] = {foo, bar, blurga, bletch};

funptr* x()
{
  static funptr funclist[4] = {foo, bar, blurga, bletch};
  return funclist;
}

int main() 
{
  printf("%c\n",gfunclist[0]());

  funptr *fs;
  fs = x();
  printf("%c\n",fs[1]()); 
}

5 ответов

Решение

Вы должны решить, используете ли вы C или C++. Эти языки значительно различаются по отношению к таким ситуациям, как ваша.

В C++ указатель на [] "массив" (т. е. массив неопределенного размера) - это совершенно другой тип, нежели "указатель на [N] массив" (т. е. массив указанного размера). Это сразу означает, что у вашего кода нет шансов компилироваться как код C++. Это не "предупреждение", это ошибка. Если вы хотите, чтобы ваш код компилировался как C++, вам нужно указать точный размер массива в типе функции, возвращаемом

char (*(*x())[4])() // <- see the explicit 4 here?
{
  static char (*funclist[4])() = {foo, bar, blurga, bletch};
  return &funclist;
}

И, конечно же, вы должны вернуться &funclist, поскольку вы объявляете свою функцию как возвращающую указатель на массив.

В main объявление получающего указателя как char (**fs)() не имеет никакого смысла. Функция возвращает указатель на массив, а не указатель на указатель. Вы должны объявить свой fs как

char (*(*fs)[4])(); // <- pointer to an array

то есть как имеющий тип указателя на массив (обратите внимание на сходство с объявлением функции). И чтобы вызвать функцию через такой указатель, нужно сделать

printf("%c\n", (*fs)[1]()); 

В языке C явный размер массива в объявлениях указатель на массив может быть опущен, поскольку в C тип "указатель на [массив]" совместим с типом "указатель на массив [N]", но другие точки по-прежнему стоять. Тем не менее, даже в C может иметь смысл более явно указывать этот размер.


Кроме того, вы можете прекратить использование типа указатель на массив и вместо этого использовать тип указатель на указатель. В этом случае ваша функция должна быть определена следующим образом

char (**x())()
{
  static char (*funclist[4])() = {foo, bar, blurga, bletch};
  return funclist; // <- no `&` here
}

И в main вы будете работать с ним следующим образом

char (**fs)();
fs = x();
printf("%c\n", fs[1]()); 

Обратите внимание, что это main так же, как то, что вы имели в своем первоначальном посте. Другими словами, ваш исходный код представляет собой причудливое сочетание двух совершенно несовместимых методов. Вы должны решить, какой из них вы хотите использовать, и придерживаться его.

Я бы посоветовал вам напечатать указатель на вашу функцию, иначе трудно увидеть, что происходит.

typedef char (*funptr)();

Тем не мение, x возвращает указатель на char (*(*x())[]), а также funclist это не та вещь.

То же самое для fs=x();: char ((())[]) != char (**)();...

Если я не ошибаюсь, есть также некоторые ошибки с приоритетом между [] и *...

Это работает нормально:

typedef char (*funptr)();

funptr gfunclist[] = {foo, bar, blurga, bletch};

funptr *x() {
  static funptr funclist[4] = {foo, bar, blurga, bletch};
  return funclist;
}

funptr *fs;
fs = x();

Выберите либо дополнительный * или [] в возвращаемом типе x, не оба.

Когда вы делаете T a[] = { .. }; затем a объявлен как имеющий тип T[N]но не как имеющий тип T[],

Так что вам нужно поставить в скобках какое-то число.

char (*(*x())[sizeof(gfunclist)/sizeof(*gfunclist)])()
{
  return &gfunclist;
}

Это ограничит то, что вы можете вернуть до определенного размера. Это то, где C++ отличается от C, что позволит вам назначить T(*)[N] для T(*)[], В C++ нет таких так называемых правил "совместимости типов". Если вы хотите избавиться от необходимости &, вам нужно вернуть тип распада массива. Тип элемента вашего массива char(*)()

char (**x())()
{
  static char (*funclist[4])() = {foo, bar, blurga, bletch};
  return funclist;
}

Используя шаблон идентичности, вы можете сделать ваше объявление более читабельным

template<typename T> struct identity { typedef T type; };
identity<char()>::type **x()
{
  static identity<char()>::type *funclist[] = {foo, bar, blurga, bletch};
  return funclist;
}

По первому вопросу

cdecl> explain char (*(*x())[])()
declare x as function returning pointer to array of pointer to function returning char
cdecl> explain static char (*funclist[4])()
declare funclist as static array 4 of pointer to function returning char

Таким образом, ожидаемый возвращаемый тип x - это указатель на массив указателей на функции, возвращающие char, но вы возвращаете только массив указателей на функции, возвращающие char. Добавляя &, вы фактически возвращаете указатель на массив указателей на функции, возвращающие char.

Во-вторых, снова ожидаемый тип возврата x - это указатель на массив указателей на функции, возвращающие char, но мы видим,

cdecl> explain char (**fs)();
declare fs as pointer to pointer to function returning char

То, что вы хотите вместо этого, char (*(*fs)[])();

cdecl> explain char (*(*fs)[])();
declare fs as pointer to array of pointer to function returning char
Другие вопросы по тегам