C语言中的可移植嵌套函数
是否可以使用嵌套函数/块编写可移植C代码 我知道gcc只支持嵌套函数作为非标准扩展,而clang只支持块,但是有没有一种方法可以编写代码,使用标准C和宏在两者上编译 如果不可能,最好的解决方法是什么?作为一个例子,如何实现一个采用参数的以下排序的可移植版本?GCC中的一个简单示例:C语言中的可移植嵌套函数,c,gcc,clang,nested-function,C,Gcc,Clang,Nested Function,是否可以使用嵌套函数/块编写可移植C代码 我知道gcc只支持嵌套函数作为非标准扩展,而clang只支持块,但是有没有一种方法可以编写代码,使用标准C和宏在两者上编译 如果不可能,最好的解决方法是什么?作为一个例子,如何实现一个采用参数的以下排序的可移植版本?GCC中的一个简单示例: int main(int argc, char*[] argv) { char reverse = 0; int cmp_func(const void *a, const void *b) {
int main(int argc, char*[] argv)
{
char reverse = 0;
int cmp_func(const void *a, const void *b)
{
const int* aa = (const int)a;
const int* bb = (const int)b;
return (reverse) ? aa - bb : bb - aa;
}
int list[8] = {1,2,3,4,5,20,100,200};
qsort(list, 8, sizeof(int), &cmp_func);
}
一个类似的例子可以用块来组合。理想情况下,解决方案应该是线程安全的(因此避免使用全局变量)
编辑:为清楚起见,假设“标准”表示C99。以上是一个微不足道的例子。我所追求的是一种需要一些参数的C99排序方法。在这里,它只是使用一个字符作为布尔值,但我在寻找一个解决方案,将采取多个整数等。看起来这可能是不可能没有全局变量
编辑2:我意识到,传递一个空指针和一个函数指针可以让你做任何可以用嵌套函数完成的事情。感谢@Quuxplusone建议qsort\u r
和qsort\u s
。我试着在qsort\u r
和qsort\u s
上组装一个便携包装器。它使用一个比较器函数和一个空指针来存储状态,从而消除了复杂排序算法对嵌套函数的依赖——因此您可以同时使用GCC和Clang进行编译
typedef struct
{
void *arg;
int (*compar)(const void *a1, const void *a2, void *aarg);
} SortStruct;
int cmp_switch(void *s, const void *aa, const void *bb)
{
SortStruct *ss = (SortStruct*)s;
return (ss->compar)(aa, bb, ss->arg);
}
void sort_r(void *base, size_t nel, size_t width,
int (*compar)(const void *a1, const void *a2, void *aarg), void *arg)
{
#if (defined _GNU_SOURCE || defined __GNU__ || defined __linux__)
qsort_r(base, nel, width, compar, arg);
#elif (defined __APPLE__ || defined __MACH__ || defined __DARWIN__ || \
defined __FREEBSD__ || defined __BSD__ || \
defined OpenBSD3_1 || defined OpenBSD3_9)
SortStruct tmp = {arg, compar};
qsort_r(base, nel, width, &tmp, &cmp_switch);
#elif (defined _WIN32 || defined _WIN64 || defined __WINDOWS__)
SortStruct tmp = {arg, compar};
qsort_s(*base, nel, width, &cmp_switch, &tmp);
#else
#error Cannot detect operating system
#endif
}
注意:我还没有在很多平台上测试过这个,所以如果你看到一个bug/这个在你的机器上不起作用,请告诉我
作为使用示例,我实现了与所选答案相同的排序:
int sort_r_cmp(const void *aa, const void *bb, void *arg)
{
const int *a = aa, *b = bb, *p = arg;
int cmp = *a - *b;
int inv_start = p[0], inv_end = p[1];
char norm = (*a < inv_start || *a > inv_end || *b < inv_start || *b > inv_end);
return norm ? cmp : -cmp;
}
int arr[18] = {1, 5, 28, 4, 3, 2, 10, 20, 18, 25, 21, 29, 34, 35, 14, 100, 27, 19};
int p[] = {20, 30};
sort_r(arr, 18, sizeof(int), sort_r_cmp, p);
int sort\u r\u cmp(常数void*aa、常数void*bb、void*arg)
{
常数int*a=aa,*b=bb,*p=arg;
int cmp=*a-*b;
int inv_start=p[0],inv_end=p[1];
字符规范=(*ainv|u结束|*binv|u结束);
返回标准?cmp:-cmp;
}
int arr[18]={1,5,28,4,3,2,10,20,18,25,21,29,34,35,14100,27,19};
int p[]={20,30};
sort_r(arr,18,sizeof(int),sort_r_cmp,p);
因为C标准不允许嵌套函数,所以没有可移植的方法在C中编写嵌套函数。宏在这里帮不了你什么忙,因为它们是由预处理器计算的,编译器仍然会看到嵌套函数并标记错误的代码。嵌套函数不在C标准中,但它是一个
gcc
扩展(当标记-fnested functions
被激活时)。此外,您还可以使用静态函数(cmp_func
)和额外的参数(reverse
)来完成同样的工作。根据@Kirilenko的建议,我提出了一个解决方案,使用全局变量和互斥体将参数传递给排序比较器函数。这种方法是线程安全的,可以用嵌套函数实现所有功能,并且应该可以在编译器之间移植
本例对整数列表进行排序,但反转给定区域的排序
//为排序参数定义锁
pthread_mutex_t lock;
//排序函数中使用的参数-反转区域(包括)
int反转开始,反转结束;
//使用全局变量(反转开始、反转结束)作为参数的比较器
int cmp_func(常数无效*a,常数无效*b)
{
常数int aa=*(常数int*)a;
常数int bb=*(常数int*)b;
如果(aa<反转|U开始| aa>反转|U结束||
bbinvert_end)
{
返回aa-bb;
}
其他的
{
返回bb-aa;
}
}
无效排序(int*arr、int arr\u len、int inv\u start、int inv\u end)
{
//互斥锁
pthread_mutex_lock(&lock);
//设置参数
反转启动=反转启动;
反转末端=反转末端;
//排序
qsort(arr、arr_len、sizeof(*arr)和cmp_func);
//无互斥
pthread_mutex_unlock(&lock);
}
示例结果:
输入:152842321018252129343514100102719
反转开始=20,反转结束=30
产出:12345101819192827272521203435100
为什么要麻烦嵌套函数和全局函数呢?100%可移植(甚至到K&R)解决方案只需使用两个不同的函数,一个按常规顺序排序,另一个按相反顺序排序,然后将其称为
qsort(list, 8, sizeof(int), reverse ? cmp_func_reverse : cmp_func);
注意:无需仅为了好玩(并回答原始问题)而使用&
获取函数的地址,是的,完全可以使用宏系统在符合标准的C99中编写嵌套函数来“解开”嵌套版本的代码。下面是一个可能的实现:
有了它,你可以写下这样令人憎恶的东西:
typedef int(* fptr)(int);
func(fptr, someFunc, (void) {
return fn(int, (int a), {
fptr f = fn(int, (int b), { return b * 6; });
return a * f(a + 1);
});
})
让我们非常清楚一些事情:这是用C编写这类代码最糟糕的方法。如果你发现自己真的需要使用宏库来编写这样的代码,那就辞去程序员的工作,成为一名农民吧。在生产中使用这个,你的同事可能会在你睡觉时谋杀你
另外,有趣的是,尽管它在技术上符合标准,但唯一具有预处理器的编译器可以处理这么多宏的重量,不管怎样是GCC和Clang。您必须更好地定义“可移植”。C无处不在;我认为嵌入式平台X的编译器不会对任何可能发布的黑客行为感到满意。标准C不允许嵌套函数。在标准C中,我想不出实现这一点的方法,但为什么还要这样做呢?这其实会有些混乱!
cmp_func
的常规函数应该与您展示的一样好。我将qsort_r的可移植包装放在:我不知道在不使用全局变量的情况下如何将额外参数(reverse
)传递给比较函数(cmp_func
),但您不能使用全局变量