忽略/不需要函数指针参数

时间:2015-07-22 08:32:19

标签: c function-pointers

我目前正在为微处理器编写C代码,而且我遇到了一些我无法解释的问题。我已经使用函数指针实现了一个命令行界面。为此,我创建了一个结构,其中包含命令的名称,指向要运行的函数的指针以及帮助描述。

typedef void(*command)(char *);
typedef struct commandStruct {
    char const *name;
    command execute;
    char const *help;
} commandStruct;

const commandStruct commands[] =
{
    {"led", CmdLed, "Turns on or off the LED1"},
    {"AT+START_SIM", start_simulation, "Starts the simulation"},
    {"AT+STOP_SIM", stop_simulation, "Stops the simulation"},
    {"",0,""} //End of table indicator.
};

void exec_command(char *buffer)
{
    uint16 i = 0;
    char *cmd = buffer;
    char *args;
    while (buffer[i])
    {
        if(buffer[i] == '=')
        {
            buffer[i] = 0;
            args = buffer + i + 1;
            break;
        }
        i++;
    }

    uint16 cmdCount = 0;
    while(strcmp(commands[cmdCount].name,""))
    {
        if(!strcmp(commands[cmdCount].name,cmd))
        {
            commands[cmdCount].execute(args);
            break;
        }
        cmdCount++;
    }
}

void start_simulation(void) {run = 1;}
void stop_simulation(void) {run = 0;}
void CmdLed(char *args)
{
    P1DIR |= BIT0;
    if(!strcmp(args,"on")) P1OUT = 1;
    if(!strcmp(args,"off")) P1OUT = 0;
}

我已经包含了函数exec_command的上方,这是函数指针的使用位置。在底部,我还提供了start_simulationstop_simulation函数以及CmdLed。我提前写了CmdLed,然后回来写了start_simulationstop_simulation。我忘记了我已经将我的函数指针定义为以(char *)作为参数。然而,我惊讶地发现所有东西仍然编译并且运行得非常好。为什么是这样?似乎任何论点都被“倾销”了。并没有使用。

2 个答案:

答案 0 :(得分:2)

这不应该用现代编译器编译。

这里会发生什么:

start_simulation将使用char*参数调用,但由于start_simulation没有参数,因此该参数将被忽略(或在您编写时“转储”)。

请记住,在C函数中,参数被推入堆栈,调用者在调用后清理堆栈。因此,如果你调用一个没有参数的函数假装它有参数,那么

  • 调用者将参数推送到堆栈
  • 无参数函数由调用者
  • 调用
  • 该函数忽略了堆栈上的参数
  • 函数返回给调用者
  • 来电者清理堆栈

还要看this SO question

答案 1 :(得分:1)

我假设您在使用它们之前声明了函数,或者您编译器隐含地声明它们为(C默认值):

int start_simulation();
...

意味着start_simulation应该在其他地方定义为接受任何参数并返回int的函数。

但是这应该至少给你一些警告,因为定义与声明不同,或者函数声明与命令结构的第二个元素的命令声明不匹配(在const commandStruct commands[] = ...中)

因为它编译并运行正常,传递参数的cdecl方式如下:

  • 参数按相反顺序推送到堆叠
  • 调用被调用者(调用者的返回地址被推送到堆栈)
  • callee可以从堆栈指针开始获取其参数+返回地址的大小,但从不将它们从堆栈中删除
  • callee返回
  • 调用者删除它推送到堆栈的参数(它知道它们)

这意味着您可以随时添加未使用的参数,前提是编译器足够容忍编译错误的调用,或者使用空参数列表声明函数。