鱼C论坛

 找回密码
 立即注册
查看: 1542|回复: 13

[已解决]好难求解答

[复制链接]
发表于 2018-11-9 18:48:06 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
test.c文件
#include<stdio.h>
#include < stdarg.h>

typedef void(*P)(int i);

void fun(int n, P show(int))
{
        int i;
        for (i = 0; i < n; i++)
        {
                show(i);
        }

}

main.c文件
#include<stdio.h>
void print(int n)
{
        printf("%d\n", n);
}

void print2(int n, int m, char a)
{
        printf("print2 %d\n", n);
        printf("print2 %d\n", m);
        printf("print2 %c", a);
}

int main() {
        fun(10, print);
        return 0;
}

环境我用的是vs2013


请问对于任意参数类型和数目的函数,应该怎么改进才可以使用fun这个函数
最佳答案
2018-11-12 11:21:34
本帖最后由 风扫地 于 2018-11-12 12:07 编辑

分析例子.zip (56.95 KB, 下载次数: 5)
看看这个附件的pdf文件在你那里能不能打开。

实验结论:
在vs2017 的cl编译器的处理情况(在其他编译器其他处理平台中未必有这些结论
1.堆栈不会不平衡,因为堆栈平衡机制是谁调用谁平衡,而不是被调用者来平衡堆栈;还有一些约定是由被调用者来平衡堆栈的(比如说stdcall方式),但如果是被调用者来平衡堆栈的话,本实验中无法体现。
2.堆栈虽然不会不平衡,但是多传参或者少传参都会造成数据错位,压多了还好只是可能错位,压少了,被调用者取数据就会取到别的数据区里面当成你传入的参数,这个参数只是读取越界还好,要是你还要写这个参数(或者少传的是一个指针,还用通过这个指针去访问别的空间),那就可能嗝屁了,访问到不是自己可以访问的数据区,后果无法预测;
3.返回值类型对不上的话数据会混乱,整数类型通过eax来返回,浮点型数据通过专用寄存器来返回,我的实验例子里面如果返回值要求是int,就直接从eax中取数据了(取到了printf函数的返回值),但实际上存储浮点型数据的专用寄存器中的结果并没有用到;
4.pdf中的专用栈空间应该修改为专用寄存器。
5.刚刚又在 test_function 加了__stdcall的关键字修饰,结果出了异常,堆栈不平衡了。



所以函数指针的使用要按规矩来,不要调戏编译器。
你要一个万能返回值和万能输入参数的函数指针类型太简单了,一般来说,其实一个 int32 型的数据都可以存下来(看汇编代码,就是存的函数首地址然后去call这个地址就调用成功了,地址值就是一个数,存一个数只要长度够,啥类型不行),用的时候取到这个数再强转成某个类型的函数指针就可以了,但是调用方式必须与函数声明的格式务必一致,务必一致。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-11-9 20:05:00 | 显示全部楼层
本帖最后由 风扫地 于 2018-11-9 20:06 编辑

void fun(int n, P show(int))
改成
void fun(int n, P show)


------------------------------------
typedef void(*P)(int i);
这里已经定义了一个函数指针类型P,用的时候直接

P p;
p = 函数名;
p(int i);

这样就可以调用了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-11-11 09:58:53 | 显示全部楼层
风扫地 发表于 2018-11-9 20:05
void fun(int n, P show(int))
改成
void fun(int n, P show)

//Main.c
#include<stdio.h>
#include < stdarg.h>

typedef void(*P)();

void fun(int n, P print)
{
        int i;
        for (i = 0; i < n; i++)
        {       
                char a = 'a';
                int iii = 0;
                print(i,i,a);
                a++;
        }
}

//test.c
#include<stdio.h>


void print(int n)
{
        printf("%d\n", n);
}

int print2(int n, int m, char a)
{

        printf("print2 %d\t%c\n", n+m,a);
        return n + m;
        //printf("print2 %d\n", m);
        //printf("print2 %c\n", a);
}
/*void print2(int n, int m, char a, int c)
{
        printf("aaaaaa");
}*/
int main() {
        //funParn("int-int-int","");
        fun(10, print2);
        return 0;
}

这个运行尽然通过了,敢信?
我定义的函数指针是没有参数的,但是传过来的函数是有参数的。
我想知道这个怎么解释。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-11-11 10:06:47 | 显示全部楼层
在C语言(非C++)中,函数 void f();并不表明该函数没有参数,而是表名该函数可以有任意个(包括0个)参数。void f(void);才是表名该函数没有输入参数。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-11-11 10:39:03 | 显示全部楼层
仰望天上的光 发表于 2018-11-11 10:06
在C语言(非C++)中,函数 void f();并不表明该函数没有参数,而是表名该函数可以有任意个(包括0个)参数 ...

typedef (*P)();
这样呢表示它的返回值可以是任意类型的吗?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-11-11 11:04:58 | 显示全部楼层
saoqing 发表于 2018-11-11 10:39
typedef (*P)();
这样呢表示它的返回值可以是任意类型的吗?

typedef (*P)();这里省略了函数返回值的类型,C中省略的类型默认为int
所以
typedef (*P)();等价于
typedef int(*P)();
该函数的输入参数处为()即可以是任意的输入参数(任意类型,任意个数)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-11-11 11:08:28 | 显示全部楼层
仰望天上的光 发表于 2018-11-11 11:04
typedef (*P)();这里省略了函数返回值的类型,C中省略的类型默认为int
所以
typedef (*P)();等价于

那怎么才能让它的返回值类型是任意的类型
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-11-11 11:15:42 | 显示全部楼层
仰望天上的光 发表于 2018-11-11 11:04
typedef (*P)();这里省略了函数返回值的类型,C中省略的类型默认为int
所以
typedef (*P)();等价于

我现在这样写,它的返回值类型其实可以是float,int,double,char类型的
但是我刚才使了以下指针是有问题的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-11-11 15:53:17 | 显示全部楼层
saoqing 发表于 2018-11-11 09:58
//Main.c
#include
#include < stdarg.h>


传入参数个数超过函数定义时的参数个数,也就是压栈多出栈少,按理说会堆栈不平衡,不知道你这样干编译器会怎么处理。
可能需要看下汇编代码~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-11-12 08:52:56 | 显示全部楼层
风扫地 发表于 2018-11-11 15:53
传入参数个数超过函数定义时的参数个数,也就是压栈多出栈少,按理说会堆栈不平衡,不知道你这样干编译 ...

这是一段神奇的代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-11-12 10:06:29 | 显示全部楼层
本帖最后由 风扫地 于 2018-11-12 10:11 编辑
saoqing 发表于 2018-11-12 08:52
这是一段神奇的代码


返回值类型不定下来就可以用,其实好理解,毕竟都是返回值都通过eax和edx这两个通用寄存器回传给上层调用的函数,上层想怎么样用eax和edx里面的的值就怎么用,想强转成啥类型就强转成啥类型。。但这个参数个数都对不上的话,真不知道咋搞的,晚一点我试下。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-11-12 10:25:52 | 显示全部楼层
风扫地 发表于 2018-11-12 10:06
返回值类型不定下来就可以用,其实好理解,毕竟都是返回值都通过eax和edx这两个通用寄存器回传给上层调 ...

好的,谢谢了啊,
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-11-12 11:21:34 | 显示全部楼层    本楼为最佳答案   
本帖最后由 风扫地 于 2018-11-12 12:07 编辑

分析例子.zip (56.95 KB, 下载次数: 5)
看看这个附件的pdf文件在你那里能不能打开。

实验结论:
在vs2017 的cl编译器的处理情况(在其他编译器其他处理平台中未必有这些结论
1.堆栈不会不平衡,因为堆栈平衡机制是谁调用谁平衡,而不是被调用者来平衡堆栈;还有一些约定是由被调用者来平衡堆栈的(比如说stdcall方式),但如果是被调用者来平衡堆栈的话,本实验中无法体现。
2.堆栈虽然不会不平衡,但是多传参或者少传参都会造成数据错位,压多了还好只是可能错位,压少了,被调用者取数据就会取到别的数据区里面当成你传入的参数,这个参数只是读取越界还好,要是你还要写这个参数(或者少传的是一个指针,还用通过这个指针去访问别的空间),那就可能嗝屁了,访问到不是自己可以访问的数据区,后果无法预测;
3.返回值类型对不上的话数据会混乱,整数类型通过eax来返回,浮点型数据通过专用寄存器来返回,我的实验例子里面如果返回值要求是int,就直接从eax中取数据了(取到了printf函数的返回值),但实际上存储浮点型数据的专用寄存器中的结果并没有用到;
4.pdf中的专用栈空间应该修改为专用寄存器。
5.刚刚又在 test_function 加了__stdcall的关键字修饰,结果出了异常,堆栈不平衡了。



所以函数指针的使用要按规矩来,不要调戏编译器。
你要一个万能返回值和万能输入参数的函数指针类型太简单了,一般来说,其实一个 int32 型的数据都可以存下来(看汇编代码,就是存的函数首地址然后去call这个地址就调用成功了,地址值就是一个数,存一个数只要长度够,啥类型不行),用的时候取到这个数再强转成某个类型的函数指针就可以了,但是调用方式必须与函数声明的格式务必一致,务必一致。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-11-12 13:49:20 | 显示全部楼层
风扫地 发表于 2018-11-12 11:21
看看这个附件的pdf文件在你那里能不能打开。

实验结论:

厉害了,谢谢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-30 18:52

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表