xml地图|网站地图|网站标签 [设为首页] [加入收藏]

正规赌博平台

当前位置:网上十大正规赌博平台 > 正规赌博平台 > C语言内存对齐详解,C语言变参函数解析

C语言内存对齐详解,C语言变参函数解析

来源:http://www.nb-machinery.com 作者:网上十大正规赌博平台 时间:2019-07-10 23:26

接上一篇:C语言内存对齐详解(1)

本文实例讲述了thinkPHP内置字符串截取函数用法。分享给大家供大家参考,具体如下:

1 函数声明
   首先,要实现类似printf()的变参函数,函数的最后一个参数要用 ... 表示,如
     int log(char * arg1, ...)
这样编译器才能知道这个函数是变参函数。这个参数与变参函数的内部实现完全没有关系,只是让编译器在编译调用此类函数的语句时不计较参数多少老老实实地把全部参数压栈而不报错,当然...之前至少要有一个普通的参数,这是由实现手段限制的。
2 函数实现
   C语言通过几个宏实现变参的寻址。下面是Linux2.18内核源码里这几个宏的定义,相信符合C89,C99标准的C语言基本都是这样定义的。

  VC对结构的存储的特殊处理确实提高CPU存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。VC 中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:

thinkphp内置了一个可以媲美smarty的模板引擎,给我们带来了很大的方便。调用函数也一样,可以和smarty一样调用自己需要的函数,而官方也内置了一些常用的函数供大家调用。

   typedef char *va_list;

第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式;

比如今天我们说的截取字符串函数,在thinkphp模板引擎里可以这样写:{$vo.title|msubstr=0,5,'utf-8′,false} 至于{$vo.title}这个大家肯定不陌生了。就说说后边的函数msubstr吧。它代表的意思是截取字符串$vo.title,从0个字符开始截取,截取5个字符。使用的是utf-8编码,默认截取后不显示省略号,如果要显示省略号,直接把false改成true就可以啦。

/*
   Storage alignment properties -- 堆栈按机器字对齐
*/
#define _AUPBND            (sizeof (acpi_native_uint) - 1)
#define _ADNBND            (sizeof (acpi_native_uint) - 1)

第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。

函数解释:

/*
   Variable argument list macro definitions -- 变参函数内部实现需要用到的宏
*/
#define _bnd(X, bnd)          (((sizeof (X)) (bnd)) & (~(bnd)))
#define va_arg(ap, T)        (*(T *)(((ap) = (_bnd (T, _AUPBND)))

结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。下面举例说明其用法:

msubstr($str, $start=0, $length, $charset="utf-8", $suffix=true)
  • (_bnd (T,_ADNBND))))
    #define va_end(ap)          (void) 0
    #define va_start(ap, A)        (void) ((ap) = (((char *) &(A)) (_bnd (A,_AUPBND))))
#pragma pack(push) //保存对齐状态   #pragma pack(4)//设定为4字节对齐   struct test   {   char m1;   double m4;   int m3;   };   #pragma pack(pop)//恢复对齐状态 

参数功能:

   下面以x86 32位机为例分析这几个宏的用途
   要理解这几个宏需要对C语言如何传递参数有一定了解。与PASCAL相反,与stdcall 相同,C语言传递参数时是用push指令从右到左将参数逐个压栈,因此C语言里通过栈指针来访问参数。虽然X86的push一次可以压2,4或8个字节入 栈,C语言在压参数入栈时仍然是机器字的size为最小单位的,也就是说参数的地址都是字对齐的,这就是_bnd(X,bnd)存在的原因。另外补充一点 常识,不管是汇编还是C,编译出的X86函数一般在进入函数体后立即执行
   push ebp
   mov ebp, esp
   这两条指令。首先把ebp入栈,然后将当前栈指针赋给ebp,以后访问栈里的参数都使用ebp作为基指针。
  
   一一解释这几个宏的作用。
   _bnd(X,bnd) ,计算类型为X的参数在栈中占据的字节数,当然是字对齐后的字节数了。acpi_native_unit是一个机器字,32位机的定义是:typedef u32 acpi_native_uint;
   显然,_AUPBND ,_ADNBND 的值是 4-1 == 3 == 0x00000003 ,按位取反( ~(bnd))就是0xfffffffc 。
因此,_bnd(X,bnd) 宏在32位机下就是
   ( (sizeof(X) 3)&0xfffffffc )
很明显,其作用是--倘若sizeof(X)不是4的整数倍,去余加4。
   _bnd(sizeof(char),3) == 4
   _bnd(sizeof(struct size7struct),3) == 8

以上结构的大小为16,下面分析其存储情况,首先为m1分配空间,其偏移量为0,满足我们自己设定的对齐方式(4字节对齐),m1占用1个字节。接着开始为 m4分配空间,这时其偏移量为1,需要补足3个字节,这样使偏移量满足为n=4的倍数(因为sizeof(double)大于n),m4占用8个字节。接着为m3分配空间,这时其偏移量为12,满足为4的倍数,m3占用4个字节。这时已经为所有成员变量分配了空间,共分配了4 8 4=16个字节,满足为n的倍数。如果把上面的#pragma pack(4)改为#pragma pack(16),那么我们可以得到结构的大小为24。

$str:要截取的字符串
$start=0:开始位置,默认从0开始
$length:截取长度
$charset="utf-8":字符编码,默认UTF-8
$suffix=true:是否在截取后的字符后面显示省略号,默认true显示,false为不显示

   va_start(ap,A) ,初始化参数指针ap,将函数参数A右边第一个参数的地址赋给ap。 A必须是一个参数的指针,所以此种类型函数至少要有一个普通的参数啊。像下面的例子函数,就是将第二个参数的指针赋给ap。

再看下面这个例子:

ps:如果不能正常调用,那说明你没有加载函数库,可以使用 Load('extend');来加载函数,把它放到action中就可以啦~!

   va_arg(ap,T) ,获得ap指向参数的值,并使ap指向下一个参数,T用来指明当前参数类型。
   注意((ap) = (_bnd (T, _AUPBND))) 是被一对括号括起来的,然后才减去(_bnd (T, _ADNBND),
而_AUPBND和_ADNBND是相等的。所以取得的值是ap当前指向的参数值,但是先给ap加了当前参数在字对齐后所占的字节数,使其指向了下一个参数。

本文由网上十大正规赌博平台发布于正规赌博平台,转载请注明出处:C语言内存对齐详解,C语言变参函数解析

关键词: