((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))的含义

  • 微信公众号:郑尔多斯
  • 关注「郑尔多斯」公众号 ,回复「领取资源」,获取IT资源500G干货。
    升职加薪、当上总经理、出任CEO、迎娶白富美、走上人生巅峰!想想还有点小激动
  • 关注可了解更多的Nginx知识。任何问题或建议,请公众号留言;
    关注公众号,有趣有内涵的文章第一时间送达!

前言

nginx的代码中经常出现类似((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))的代码,这部分代码的作用是什么呢?本文分析一下它的神奇之处。
本文主要参考文章末尾的两个链接,并稍作整理。

引子

先看一个日常生活中的问题,
问题1:假设有要把一批货物放到集装箱里,货物有12件,
一个箱子最多能装6件货物,求箱子的数目。
解答:显然我们需要12 / 6 = 2个箱子,并且每个箱子都是满的。这个连小学生都会算:-)
问题2: 把问题1的条件改一下,假设一个箱子最多能装5件货物,那么现在的箱子数是多少?
解答:12/5=2.4个,但是根据实际情况,箱子的个数必须为整数,(有不知道这个常识的就不要再往下看了,回小学重读吧,哈哈)自然我们就要取3
下面把问题一般化

一般数学模型

问题3:设一个箱子最多可以装M件货物,且现有N件货物,
则至少需要多少个箱子,给出一般的计算公式。
这里要注意两点
1、箱子的总数必须为整数
2、N不一定大于M,很显然,很显然,即使N ≤ M ,也需要一个箱子

通项公式

1、预备知识
在讨论之问题3的解答之前,我们先明确一下/运算符的含义。
定义/运算为取整运算,即
对任意两个整数N,M,必然有且只有唯一的整数X,满足
X * M <= N < (X + 1) * M,那么记N / M=X
这个也正是c语言/运算的确切含义。x的存在性和唯一性的严格证明可以见数论教材。
以后如无额外说明, / 运算的含义均和本处一致。
/ 运算有一个基本的性质
N=MX+Y,则N/M = X+Y/M,证明略
注意:N不是可以随便拆的,设N= A + B,那么一般情况下N/M不一定等于A/M + B / M,如果AB至少有一个是M的倍数,才能保证式子一定成立。
2、分步讨论
根据上面的/运算符的定义,我们可以得到问题三的解答,分情况讨论一下
已知N/M=X,那么当
(1)、当N正好是M的倍数时即N=M*X时,那么箱子数就是X=N/M
(2)、如果N不是M的倍数,即N=M*X+Y(1 <=Y < M 那么显然还要多一个箱子来装余下的Y件货物 ),则箱子总数为X+1 = N/M+1
3、一般公式
上面的解答虽然完整,但是用起来并不方便,因为每次都要去判断N和M的倍数关系,
我们自然就要想一个统一的公式,于是,下面的公式出现了
箱子数目为 ( N + M - 1) / M.
这个式子用具体数字去验证是很简单的,留给读者去做。
我这里给一个完整的数学推导:
现在已经假定/运算的结果为取整(或者说取模),即
N/M=X,则XM <= N <(X+1)M
那么,
(1)、当N=MX时,(N+M-1)/M = MX/M + (M-1)/M = X
(2)、当N=MX+Y(1 <=Y < M)时,
由于 1 <=Y < M, 同时加上M-1,得到 M <= Y + M - 1 < 2M-1 < 2M
根据 / 运算的定义 (Y + M - 1) / M = 1
所以 (N+M-1) / M = (MX+Y+M-1)/M= MX / M+(Y+M-1) / M= X+1
显然 公式 (N+M-1)/M2中的分步讨论结果一致。
可能有的读者还会问,这个公式是怎么想出来的,怎么就想到了加上那个M-1?
这个问题可以先去看看数论中的余数理论。

五、对齐代码的分析
有了上面的数学基础,我们再来看看开头所说的对齐代码的含义
((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))
意义就很明显了
这里。机器字长度sizeof(int)相当于箱子的容量M, 变量的真实字节 sizeof(n) 大小相于货物总数N,整个代码就是求n所占的机器字数目。

顺便仔细的解释一下
~(sizeof(int)-1))
这里用到了一个位运算的技巧,即若M2的幂,M=power(2,Y);
N / M = N >> Y ,另根据数论中的余数定理,有N=M*X+Z (1 < = Z < M)
而注意到这里的N,M,Z都是二进制表示,所以把N的最右边的Y位数字就是余数Z.
剩下的左边数字就是模X.
而内存对齐要计算的是占用的总字节数(相当于箱子的最大容量),所以总字节数 = ( N / M) * M = ( N>>Y)
注意,这里的右移和左移运算并未相互抵消,最后的结果实际上是把N中的余数Z去掉(被清0)。

#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) – 1) & ~(sizeof(int) – 1)
[此问题的推荐答案]
~是位取反的意思。
_INTSIZEOF(n)整个做的事情就是将n的长度化为int长度的整数倍。
比如n5,二进制就是101bint长度为4,二进制为100b,那么n化为int长度的整数倍就应该为8
~(sizeof(int) – 1) )就应该为~(4-1)=~(00000011b)=11111100b,这样任何数& ~(sizeof(int) – 1) )后最后两位肯定为0,就肯定是4的整数倍了。
(sizeof(n) + sizeof(int) – 1)就是将大于4m但小于等于4(m+1)的数提高到大于等于4(m+1)但小于4(m+2),这样再& ~(sizeof(int) – 1) )后就正好将原长度补齐到4的倍数了。

原文链接

((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))边界对齐
& ~(sizeof(int) - 1) )详解


喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达
郑尔多斯

原文地址:https://www.cnblogs.com/zhengerduosi/p/10607753.html

时间: 2024-11-03 21:07:22

((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))的含义的相关文章

what is difference in (int)a,(int&amp;)a,&amp;a,int(&amp;a) ?

This interview question come from a famous communication firm of china. : ) 1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <conio.h> 5 using namespace std; 6 7 int main() 8 { 9 float a = 1.0f; 10 11 cou

int *a[] 与(int *)a【5】的区别

*a[5] 是指针数组  可以指向5个值 (*a)[5]  是一个指针,但这个指针只能指向包含5个元素的一维数组 a是一个数组,每个元素都是个指针. b是一个指针,指向一个数组 1.int *a[5]:指针数组.本质是数组.sizeof(a)=20.sizeof计算数组的大小.5个int型即为20. sizeof(*a)=4.数组元素是指针.*a代表的是一个指针,此求的是int  型指针的大小为4. 2.int (*a)[5]:数组指针.本质是指针.sizeof(b)是求int型指针的大小为4.

c语言将字符串转成int,将int转成字符串。附带任意进制转换函数

在网上看到各种将字符串转成int,和int转成字符串的方法.我自己比较喜欢用的方法是下面的,记住模板就行,最开始我也老是记不住,找到规律,多用几遍就好了. 1.将字符串转成int char s[20] = "123456";   // 注如果此处不是数字字符串类型,如下面的字符串  12abc -->  12        ab12 --> 0 int a = 0; sscanf(s,"%d",&a);   // a = 123456   就得到

const int *a和int *const a的区别

关键问题点:const 属于修饰符 ,关键是看const 修饰的位置在那里1.const int *a 这里const 修饰的是int,而int定义的是一个整值 因此*a 所指向的对象 值 不能通过 *a 来修改,但是 可以重新给 a 来赋值,使其指向不同的对象 eg:        const int *a = 0;        const int b = 1;        int c = 1;       a = &b  //  额外:注意不能通过a 来修改 b值       a = &a

C++中 int i 与 int &amp;i 注意事项

来源:http://blog.csdn.net/qianchenglenger/article/details/16949689 1.int i 传值,int & i 传引用 int i不会回带参数,而int &i可以回带参数,如 [cpp] view plain copy #include <iostream> void test1(int i) { i = 7; } void test2(int &i) //要限制参数改动,可以加const限制 { i = 7; }

mysql中int(1)与int(10)的区别

INT[(M)] [UNSIGNED] [ZEROFILL] 普通大小的整数.带符号的范围是-2147483648到2147483647.无符号的范围是0到4294967295. INT(1) 和 INT(10)本身没有区别,但是加上(M)值后,会有显示宽度的设置. 如代码所示: mysql> create table test(id int(3)); Query OK, 0 rows affected (0.47 sec) mysql> insert into test values(12)

int(1)和int(11)的区别

在cmd中进入数据库中 creata table t(x int(1) zerofill,y int(11) zerofill); insert into t(x,y) values(1,1); select x,y from t; 然后我们再创建一张表 我们比较一下可以发现int(1)和int(11)使用zerofill后两者才会有所区别,当没有加zerofill时候两者是没有任何区别的. 因此当结合可选扩展属性zerofill使用时, 默认补充的空格用零代替.例如,对于声明为INT(5) z

第三题 有如下Student 对象, private String name; private int age; private int score; private String classNum; 其中,classNum 表示学生的班号,例如“class05”。 有如下List List list = new ArrayList();

list.add(new Student(“Tom”, 18, 100, “class05”)); list.add(new Student(“Jerry”, 22, 70, “class04”)); list.add(new Student(“Owen”, 25, 90, “class05”)); list.add(new Student(“Jim”, 30,80 , “class05”)); list.add(new Student(“Steve”, 28, 66, “class06”));

Java-集合-第三题 有如下Student 对象, private String name; private int age; private int score; private String classNum; 其中,classNum 表示学生的班号,例如“class05”。 有如下List List list = new ArrayList(); l

第三题 有如下Student 对象, private String name; private int age; private int score; private String classNum; 其中,classNum 表示学生的班号,例如“class05”. 有如下List List list = new ArrayList(); list.add(new Student(“Tom”, 18, 100, “class05”)); list.add(new Student(“Jerry”,