C99: приведение обратных вызовов с различным количеством аргументов - PullRequest
6 голосов
/ 30 апреля 2020

в следующем примере я делаю CAST функции без аргументов в указателе на функцию, которая должна получить аргумент. Предполагая, что это дает желаемый результат, возможно ли, что эта процедура вызывает некоторую неисправность? онлайн тест: https://onlinegdb.com/SJ6QzzOKI

typedef void (*Callback)(const char*);
Callback cb;

void inserisce_cb(void* c) {
    cb=c;
}

void esegue_cb(){
    cb("pippo");
}

void scriveTitolo(const char* titolo) {
    Uart_Println(titolo);
}

void scriveTitolo2() {
    Uart_Println("pluto");
}

void main(){
    inserisce_cb(scriveTitolo);
    esegue_cb();
    inserisce_cb(scriveTitolo2);
    esegue_cb();
}

Ответы [ 2 ]

4 голосов
/ 30 апреля 2020

Дополнительные мысли к ответу Eri c, который также относится и к C99:

Если вы вызываете функцию со списком аргументов, не совместимым со списком параметров функции, это соответствует C99 §6.5.2.2 (6) неопределенное поведение.

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

Вы можете объявить свой scriveTitolo2 с игнорируемым параметром:

void scriveTitolo2(const char*) {
    /* ... */
}

И все довольны : вы и компилятор.

4 голосов
/ 30 апреля 2020

Преобразование указателя на функцию в другой указатель на функцию определяется стандартом c, но использование результирующего указателя для вызова функции с несовместимым типом недопустимо для C 6.3.2.3 8:

Указатель на функцию одного типа может быть преобразован в указатель на функцию другого типа и обратно; результат должен сравниваться равным исходному указателю. Если преобразованный указатель используется для вызова функции, тип которой не совместим с указанным типом, поведение не определено.

Объявление void scriveTitolo2() { … } определяет функцию, у которой нет списка типов параметров. (он использует старый стиль C списка идентификаторов, при этом этот список пуст), и это не требует аргументов. Указатель Callback указывает на функцию, которая имеет список типов параметров и принимает аргумент const char *. Они несовместимы в соответствии с C 2018 6.7.6.3 15:

Для совместимости двух типов функций… Если один тип имеет список типов параметров, а другой тип определяется определением функции, которое содержит (возможно, пустой) список идентификаторов, оба должны совпадать по количеству параметров,…

Поскольку они не совпадают по количеству параметров, они несовместимы.

выше говорит только о проблеме преобразования из void (*)() в void (*){const char *) и использования результата для вызова функции. Существует отдельная проблема в том, что указатель функции передается в inserisce_cb, который принимает аргумент типа void *, который является указателем на тип объекта. Стандарт C не определяет поведение преобразования указателя на тип функции в указатель на тип объекта. Чтобы исправить это, необходимо объявить inserisce_cb для получения указателя на тип функции, такой как void inserisce_cb(Callback c).

Если можно изменить scriveTitolo2, то проблему совместимости можно решить, изменив ее на возьмите параметр const char *, который не используется, изменив его определение на void scriveTitolo2(const char *).

(Обратите внимание, что лучше объявить scriveTitolo2 с современным стилем C, как void scriveTitolo2(void) { … }, чем без void. Это не связано с вопросом, так как это не сделает типы функций совместимыми, но этот формат объявления предоставляет компилятору больше информации при многих обстоятельствах.)

...