Поведение const-квалифицированной функции в C

#c #function #constants #typedef

#c #функция #константы #typedef

Вопрос:

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

Я создал небольшой файл примера (test.c):

 typedef void* vop(void*);

vop  fn;
const vop cfn;

int main(void){
    vop *p_fn = fn;
    const vop *cp_fn = fn;  // <- gives compiler warning
    vop *p_cfn = cfn;
    const vop *cp_cfn = cfn;
}
 

и запустил

gcc -Wall -Wno-unused-variable -c test.c

что приводит к следующему предупреждению:

предупреждение: инициализация создает указатель на квалифицированную функцию ‘__attribute__ ((const))’ из неквалифицированных [-Wdiscarded-qualifiers]

Поэтому «нормально» присваивать «указатель на const vop» переменной типа «указатель на vop», которая, если бы это не был указатель на функцию, давала бы что-то вроде:

предупреждение: инициализация отбрасывает квалификатор ‘const’ из целевого типа указателя [-Wdiscarded-qualifiers]

Но теперь он предупреждает об обратном случае. Итак, возникает вопрос: в чем разница между указателями на функции, квалифицированными const, и теми, которые не являются const-квалифицированными?


Примечание: cppreference содержит следующий абзац:

Если тип функции объявлен с определителем const type (с помощью typedef ), поведение не определено.

Является ли предупреждение, которое я видел, результатом этого «неопределенного поведения», или этот пункт не применяется в данном случае (и если нет, в каком случае он может быть применен)?

Ответ №1:

Тип функции не может иметь для него никакого определителя типа, в том числе const . При этом поведение не определено.

Из раздела 6.7.3p9 стандарта C:

Если спецификация типа массива включает в себя какие-либо квалификаторы типа, тип элемента определяется таким образом, а не типом массива. Если спецификация типа функции включает в себя какие-либо квалификаторы типа, поведение не определено.

Это объявляет const тип функции:

 const vop cfn;
 

И это объявляет указатель на const тип функции:

 const vop *cp_fn;
 

Оба из которых нарушают 6.7.3p9.

Ответ №2:

В чем разница между указателями на функции, квалифицированными const, и теми, которые не являются const-квалифицированными?

const имеет обычное значение — одно может быть изменено, const другое — нет. Пример:

 void something();
void something_else();
int main() {
    void (*normal_pointer)() = something;
    normal_pointer = something_else; // all fine

    void (*const const_qualified_pointer)() = something;
    const_qualified_pointer = something_else; // error

    // for fun, let's aad typedef examples
    // similar with a typedef, if you want to
    typedef void functype();
    functype *pnt = something;
    pnt = something_else; // all fine

    functype *const cpnt = something;
    cpnt = something_else; // error

    // note that if typedef is already a pointer... then it's already a pointer
    typedef void (*functypepnt)();
    functypepnt pnt2 = something;
    pnt2 = something_else; // all fine

    const functypepnt cpnt2 = something;
    cpnt2 = something_else; // error
}
 

Является ли предупреждение, которое я видел, результатом этого «неопределенного поведения», или этот пункт не применяется в данном случае (и если нет, в каком случае он может быть применен)?

Да. vop является типом функции. const vop является неопределенным поведением. gcc выдает предупреждение и игнорирует квалификатор.

Возможно, вместо этого вы захотите const указать сам указатель, а не указывающий на тип:

 vop *const cp_cfn = fn;
 

Комментарии:

1. Спасибо за ответ, но на самом деле мне просто было любопытно узнать о случае, когда const он помещается перед звездочкой. Заголовок вопроса может немного вводить в заблуждение. Должен ли я ее изменить? Этот ответ будет хорошей ссылкой для людей, которые могут наткнуться на этот вопрос и действительно хотят получить «постоянный указатель».