当前位置: 首页 > news >正文

无锡网站排名哪里有大德通众包 做网站怎么样

无锡网站排名哪里有,大德通众包 做网站怎么样,行业网站系统,贵州网站建设seo优化1、一个简单的函数 int Add(int x, int y) {return x y; }int main() {printf("%d", Add(2, 3, 4, 5, 6));return 0; } 这一段足够简单的代码,闭眼都能知道运行结果会在屏幕上打印 5 。那编译器是怎么处理后面的 4、5、6 ? 我们再看看这个函…

1、一个简单的函数

int Add(int x, int y)
{return x + y;
}int main()
{printf("%d", Add(2, 3, 4, 5, 6));return 0;
}

        这一段足够简单的代码,闭眼都能知道运行结果会在屏幕上打印 5 。那编译器是怎么处理后面的 4、5、6 ?

        我们再看看这个函数。

void MyTest(int a, int b, int c, int d)
{printf("%d", a);
}

         似乎参数 b、c、d 的设定是多余的。不论这三个参数传入什么值,都不影响结果。那上述的 Add 函数是不是也能看作后续 4、5、6 对应的参数没有用到,所以没有表现出任何现象?

        带着这个问题,再看一个函数:

void MyTest_2(int num)
{int* ptr = &num;for (int i = 0; i <= num; i++){printf("%d ", *ptr);ptr++;}
}

        是不是有点懵?对形参取地址是个什么操作?还要对形参的指针进行移动再打印出来又是什么鬼?预感上,大概率会报非法访问。

        那不妨,我们在 main 函数里调用一下?

MyTest_2(0);
MyTest_2(1);
MyTest_2(10);

        然而结果是:

        虽然返回了一堆不明所以的值,但返回代码 0 说明程序压根就没有报错。而参数 0 、 1 、 10 都被完整打印出来了,简直是毁三观。

        知道你很急,但是你先别急,再在 main 函数中试试这个足以让人懵逼的例子:

MyTest_2(5, 100, 20, 35, 40, 114514);

        结果更毁三观了: 

        什么玩意?明明 MyTest 创建时只设定了一个参数,为什么传入六个参数能全部打印出来?是不是说明,一开始的 Add 调用,后面的 4、5、6 也一并进行了传参,只是没有在函数内部进行使用?

        要弄懂这个问题,首先得了解编译器对函数调用时的参数是怎么处理,传参过程又是怎么样的。

2、函数传参过程

2.1、栈帧建立之前

        调用函数时,系统会在内存中创建对应函数的栈帧。关于栈帧建立及销毁这部分内容可以看这一篇开头部分:函数栈帧简述。

        而在进行栈帧建立之前,程序还会执行一系列的操作。以这段代码为例,直接在汇编中看看 ret 赋值时的 Add 调用,汇编指令到底做了什么:

int Add(int x, int y)
{return x + y;
}int main()
{int ret = Add(0xAB, 0xCD, 0xEF, 0xAA, 0xDD);return 0;
}

        当前栈帧是 main 函数,根据以上汇编指令,在调用 Add 函数之前,程序将 0xAB、0xCD、0xEF、0xAA、 0xDD 这五个参数逆序放入 main 函数栈顶( ESP 是栈顶寄存器)。

         这一步实际上就是函数传参。通过这一步得出结论,不论函数在创建时定义了多少个形参,甚至不定义形参,只要在调用时,函数名后的括号内写入参数,就一定会进行传参。

2.2、参数调用

        上述两句汇编代码首先是将 ebp+8 位置的值存入 eax 寄存器,再让 eax 寄存器中的值 +=  ebp+12 位置的值。而 ebp+8 的地址与 ebp+12 的地址分别储存了 0xAB 和 0xCD 。

        至此就是一次完整的传参及参数调用。 

        也就是说,只需要知道第一个参数的地址,那么剩下的参数即使不在创建函数时定义,也可以通过第一个参数的地址进行访问。就此,最开始的 MyTest_2 函数产生的现象也就解释完毕。

3、可变参数列表

3.1、定义阐述

        严格来说 C 语言的函数参数数量并不是固定的,那么在应用上根据传入的各个参数类型及第一个参数的地址,对函数传入任意参数个数,只需要通过某种方式在函数内部进行调用,那么函数的灵活性和扩展性就大大提高了。

        很好, printf 也是这么想的。在使用 printf 时,第一个参数中有几个占位符,后续就带几个参数,各位对这规则应该已经形成肌肉记忆了。而对于之前的 MyTest_2 函数,唯一定义的参数便是后续传入有效参数的个数。而为了语义上更加直观,像这类可对后续参数进行操作的函数在创建时,一般会加上三个点。当然,也是为了语义,将变量名改为 argc (argument count):

void MyTest_2(int argc, ...)
{int* ptr = &argc;for (int i = 0; i <= argc; i++){printf("%d ", *ptr);ptr++;}
}

        如以上函数中用其中某个参数确定后续参数的个数,那么这一系列参数就叫可变参数列表。

3.2、初步实现

        虽然在 MyTest_2 中已经初步实现了带可变参数列表的函数创建,但这个函数好像没什么用。所以这里再举一个例子,求若干浮点数的和:

double Sum(int argc, ...)
{double sum = 0.;//创建可变参数列表的头部指针,将指针指向列表第一个元素double* ptr = (double*)(&argc + 1);//遍历可变参数列表,求和for (int i = 1; i <= argc; i++){sum += *ptr;//指针指向下一个参数ptr++;}return sum;
}

        这代码貌似没问题,但传入的参数列表仅限于 double 类型,如果传入的参数是一个整型变量呢?由于内部只能通过指针访问,根本无法知晓外部传入的变量类型,而且编译器也不会对可变参数列表中的参数类型作检查。

        所以,如果列表的参数类型不一致,第一个参数除了附带参数的数量信息外,还应附带每个参数的类型。解决办法可以参照 printf 的第一个参数。在此之前,先了解一个点,函数在传参时,汇编指令会对参数进行类型提升和 4 字节对齐。也就是说,char、short 的类型会被提升为 int ,而 float 类型直接提升为 double 。

        修改后如下:

//format字符串只允许d或f,不区分大小写
double Sum(const char* format, ...)
{double sum = 0.;int count = strlen(format);//创建可变参数列表的头部指针,将指针指向列表第一个元素char* ptr = (char*)(&format) + sizeof(char*);for (int i = 0; i < count; i++){//遇到字符d或者D以整型处理if (format[i] == 'd' || format[i] == 'D'){sum += (double)*((int*)ptr);//指针指向下一个参数ptr += sizeof(int);}//遇到字符f或者F以双精度浮点型处理else if (format[i] == 'f' || format[i] == 'F'){sum += *((double*)ptr);//指针指向下一个参数ptr += sizeof(double);}}return sum;
}

        至此已经很接近 printf 的参数调用方式了。

3.3、可变参数列表宏

        调用 stdio.h 头文件便可以使用专用于处理可变参数列表的四个宏:

        va_list:用于创建读取可变参数列表的指针;

typedef char* va_list;

        __crt_va_start:将可变参数列表的指针指向列表第一个参数;

#define __crt_va_start_a(ap, v) ((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v)))
#define __crt_va_start(ap, x) __crt_va_start_a(ap, x)

        __crt_va_arg:获取可变参数列表的指针当前指向的参数,并将指针指向下一个参数;

#define __crt_va_arg(ap, t) (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))

        __crt_va_end:用于销毁可变参数列表的指针。

#define __crt_va_end(ap) ((void)(ap = (va_list)0))

        此外对上述 _INTSIZEOF 和 _ADDRESSOF 也需要作了解:

#define _ADDRESSOF(v) (&(v))
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))

        上述的 _INTSIZEOF 比较难以理解。它的运算结果是 4 字节对齐,这个公式有点巧妙,有兴趣可以自行理解。

        接下来先将上面的代码用这几个宏改造一下:

double Sum(const char* format, ...)
{double sum = 0.0;va_list ptr;__crt_va_start(ptr, format);for (int i = 0; i < strlen(format); i++){if (format[i] == 'd' || format[i] == 'D'){sum += __crt_va_arg(ptr, int);}else if (format[i] == 'f' || format[i] == 'F'){sum += __crt_va_arg(ptr, double);}}__crt_va_end(ptr);return sum;
}

        不过这几个宏不推荐使用,因为随着编译器的不同,很可能某些编译器并不支持这些宏,可移植性大大降低。这里主要是提供宏的思路,至于宏的实现也已经展示,各位完全可以根据这些宏通过纯 C 代码实现。

http://www.yayakq.cn/news/794051/

相关文章:

  • 做网站导航站的注意点青海 住房和建设厅网站
  • 宁波外贸建站公司裤子seo关键词
  • 怎么做淘宝客优惠券网站详情页模板psd
  • 营销公司网站模板怎么做wordpress主题
  • 正规的咨询行业网站策划h5做网站
  • c2c网站模板营销运营推广服务
  • 网站设置二级域名好吗html制作百度页面
  • 佛山 网站设计公司培训教育
  • 学院网站建设进度情况说明电子商务网站建设实训论文
  • 快速域名网站备案廊坊百度关键词优化怎么做
  • 企业网站建设需要哪些东西个人可以建设网站吗
  • 字体在线设计网站如何删除hao123主页
  • 创建网站服务器沙县建设局网站
  • 盐城网站建设系统公司免费ppt模板免费网站
  • wordpress站点维护无货源一件代发平台
  • 网站页面相关产品链接怎么做东莞房价走势
  • 网站开发与网页制作wordpress生成pdf
  • 公司网站建设是哪个部门的事情视频制作软件app下载
  • 学校网站 建设 价格app程序开发用什么编程
  • 业网站制作政务网站建设 云南 公司
  • 南谯区城乡建设局网站中文html5网站欣赏
  • 网站空间集装箱湖南省建筑设计院集团有限公司
  • 怎么做创意短视频网站移动端网站优化
  • 如何规划设计一个网站电影《设计》
  • 广州建网站白云区宁波微信公众号开发公司
  • 爱墙 网站怎么做无锡网站建设 app 微信
  • 重庆网站建设培训机构网站的二级目录怎么做
  • 金隅嘉华大厦网站建设公司商务网站安全方案设计
  • 网站负责人不是法人鼎湖网站建设
  • 建个网站视频教程仿建网站