为什么可以使用签名错误的比较函数调用 qsort 并且编译没有警告

Why can `qsort` be called with a compare function with the wrong signature and compile has no warnings

我正在努力整合一个代码库(将 qsort compar 函数移动到一个新的头文件/库,这样它就可以在不被 copy/pasta 共享的情况下共享)并注意到一些事情过程中很奇怪。

这是一个示范清单:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

 /** One record has three fields.
 * Each field contains a NULL terminated string of length at most 7 characters. */
typedef char Record[3][8];

int main(void)
{
    Record database[5] = {0};

    strcpy(database[0][0], "ZING");
    strcpy(database[0][1], "BOP");
    strcpy(database[0][2], "POW");

    strcpy(database[1][0], "FIDDLY");
    strcpy(database[1][1], "ECHO");
    strcpy(database[1][2], "ZOOOM");

    strcpy(database[2][0], "AH");
    strcpy(database[2][1], "AAAAA");
    strcpy(database[2][2], "AH");

    strcpy(database[3][0], "BO");
    strcpy(database[3][1], "DELTA");
    strcpy(database[3][2], "FO");

    strcpy(database[4][0], "FRRING");
    strcpy(database[4][1], "CRASH");
    strcpy(database[4][2], "FOO");

    //(gdb) ptype record_compare_field_1
    //type = int (char (*)[8], char (*)[8])
    int record_compare_field_1();
    qsort(database, 5, sizeof(Record), record_compare_field_1);
    for (int i = 0; i < 5; i++){
        printf("%s\t%s\t%s\n", database[i][0], database[i][1], database[i][2]);
    }
}

/* Compares Records at field one. */
int record_compare_field_1(Record rowA, Record rowB)
{
    return strcmp(rowA[1], rowB[1]);
}

编译并运行:

$ gcc -Wall main.c
$ ./a.out
AH      AAAAA   AH
ZING    BOP     POW
FRRING  CRASH   FOO
BO      DELTA   FO
FIDDLY  ECHO    ZOOOM

令我惊讶的是:

问题是:

谢谢!

int record_compare_field_1(); 声明没有原型。这是 C17/C18 标准的过时功能。

在函数调用 qsort(database, 5, sizeof(Record), record_compare_field_1); 中,record_compare_field_1 参数的类型为 int (*)()qsortcompar 参数的类型为 int (*)(const void *, const void *) . C17 6.2.7 中的这条规则允许这样做:

— If only one type is a function type with a parameter type list (a function prototype), the composite type is a function prototype with the parameter type list.

实际的 record_compare_field_1 函数定义具有原型 int record_compare_field_1(Record, Record),其中 Record 类型由 typedef char Record[3][8] 定义。由于数组参数调整为指针,所以这个和原型是一样的int record_compare_field_1(char (*)[8], char (*)[8]).

qsort 将使用错误的原型调用传入的 record_compare_field_1 函数,从而导致 未定义的行为 。大多数 C 实现对所有对象指针类型使用相同的表示,因此它可以让您摆脱它。

要正确执行此操作,record_compare_field_1 函数可以这样定义:

int record_compare_field_1(const void *a, const void *b)
{
    const Record *p_rowA = a;
    const Record *p_rowB = b;
    return strcmp((*p_rowA)[1], (*p_rowB)[1]);
}