stdarg и что здесь вообще происходит?

Рейтинг: -1Ответов: 1Опубликовано: 21.03.2023

Написал простейший код с использованием varidatic functions

#include <stdio.h>
#include <stdarg.h>

typedef void (*callback_ft)();

void OnPlayerJoin(int, char*);
void OnPlayerDisconnect(int, char*, int);


void call(callback_ft callback, ...) {
    va_list args;
    va_start(args, callback);
    callback(args);
    va_end(args);
}
int main(int argc, char* argv[]) {
    call(OnPlayerJoin, "Mike");
    call(OnPlayerJoin, "Bob", "Hello"); // неправильный вызов
    call(OnPlayerDisconnect, "John", 176);
    call(OnPlayerDisconnect, "John"); // неправильный вызов
}

void OnPlayerJoin(int n, char* name) { printf("Hi %s!\n", name); }
void OnPlayerDisconnect(int n, char* name, int time) { printf("%s disconnected! Played for %imin\n", name, time); }

Код работает, запускается и выводит:

введите сюда описание изображения

Но у меня возникло 3 вопроса:

  1. Почему при указании сигнатуры функции с пустыми параметрами, в вызываемые аргументы передаются правильно, даже с учётом того, что первый опорный параметр n явно не передаётся?
  2. Почему при неправильном вызове код продолжает более-менее адекватно работать не вываливаясь сразу в segfault?
  3. Насколько такой код переносим и насколько его опасно применять в реальных проектах?

Я действительно не понимаю как это работает и почему это вообще работает...

[Visual Studio 2022 | Стандартные настройки проекта, кроме флагов /TC и /std:c17]

Ответы

▲ 1Принят
  1. Тип typedef void (*callback_ft)(); означает указатель на функцию с аргументом типа void или int. Но никак не va_list
    Неправильное использование функции приводит к неопределённому поведению. UB

  2. ... UB

  3. Никак не переносим.

Переносимым будет если вы передадите функциям указатель на список аргументов. И не надеяться на удачу.

#include <stdio.h>
#include <stdarg.h>

typedef void (*callback_ft)(va_list*);

// int n , char * name
void OnPlayerJoin(va_list*);
// int n , char * name , int time
void OnPlayerDisconnect(va_list*);

void call(callback_ft callback, ...) {
    va_list args;
    va_start(args, callback);
    callback(&args);
    va_end(args);
}

int main(int argc, char* argv[]) {
    call(OnPlayerJoin, (int)0,(char*)"Mike");
    call(OnPlayerJoin, (int)1,(char*)"Bob");
    call(OnPlayerDisconnect,(int)0,(char*)"John", (int)176);
    call(OnPlayerDisconnect, (int)1,(char*)"John",(int)5);
}

void OnPlayerJoin(va_list*const l) {
 int n = va_arg(*l, int);
 char* name = va_arg(*l, char*);     
 printf("Hi %s!\n", name); 
}

void OnPlayerDisconnect(va_list*const l) {
 int n = va_arg(*l, int);
 char* name = va_arg(*l, char*);  
 int time = va_arg(*l, int);  
 printf("%s disconnected! Played for %imin\n",
  name, time); 
}