((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) )详解



喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达
郑尔多斯
<think>好的,我现在需要帮助用户分析一段C语言代码的输出结果。用户提到代码包含结构体数组和指针操作,需要逐行解析逻辑。首先,我需要明确用户提供的代码具体内容,但根据用户提供的引用,可能存在多个代码片段,比如结构体student的定义、fun1和fun2函数,以及结构体与数组结合的示例,还有涉及指针操作的部分,比如arr[0]和a+1的情况。 首先,我需要整合这些引用中的信息。引用[1]提到结构体student存储姓名和四门成绩,fun1和fun2处理和打印成绩。引用[2]展示结构体Student的实例化和成员赋值,以及结构体与数组的结合。引用[3]是关于数组和指针的操作,比如*(a[0]+1)sizeof(a+1)的输出。 假设用户提供的代码是这些引用的组合,可能的结构体数组和指针操作可能涉及结构体数组的初始化、指针访问成员、sizeof操作符的应用等。例如,结构体数组的每个元素是一个student结构,通过指针访问其成员,或者计算某些表达式的大小。 接下来,我需要逐步分析可能的代码结构。例如,结构体数组可能被声明为struct student arr[5];,然后通过指针如struct student *p = arr;来遍历数组。可能涉及指针算术运算,比如p++指向下一个结构体元素。同时,代码中可能使用sizeof来计算结构体的大小或指针的大小,这在不同的上下文中结果不同,比如结构体的大小取决于其成员的对齐和填充,而指针的大小通常是固定的(如4或8字节)。 另外,引用[3]中的例子提到arr[0]作为第一行的数组名,*(a[0]+1)访问元素,这里可能是一个二维数组的情况,例如int a[3][4];,那么a[0]是第一个一维数组的指针,+1后指向第二个元素,解引用得到该元素的值。sizeof(a+1)中的a是二维数组名,当a+1时,它退化为指向第二行数组的指针,所以sizeof结果为指针的大小,即4或8字节,而用户示例中的输出是4,说明是32位系统。 结合结构体数组的情况,比如计算sizeof(struct student)可能会给出结构体的总大小,包括各成员和填充字节。如果有指针指向结构体数组,如struct student *p = arr;,那么p+1会增加sizeof(struct student)的字节数。 用户可能的问题可能涉及结构体成员的访问方式,指针运算后的地址变化,以及sizeof在不同情况下的返回值。例如,给定一个结构体数组,如何通过指针访问第二个元素的成绩成员?或者,sizeof(arr)sizeof(p)的区别? 另外,函数fun1和fun2可能对结构体数组进行处理,如计算平均分、排序等,需要查看具体实现才能确定输出结果。但用户的问题可能集中在指针操作和结构体数组的内存布局上。 例如,假设有如下代码: struct student { char name[20]; float scores[4]; }; struct student arr[3]; struct student *p = arr; 然后,p++会移动到arr[1]的地址,偏移量是sizeof(struct student)。如果结构体student的大小是20(name) + 4*4(scores的4个float)= 36字节,加上可能的对齐填充,实际大小可能是40字节。因此,p++的地址会增加40字节。 在sizeof的例子中,如果代码中有sizeof(arr),则返回整个数组的大小,即3 * sizeof(struct student)。而sizeof(p)则是指针的大小,4或8字节。 另外,对于二维数组和指针的操作,如int a[3][4];,a的类型是int[3][4],a+1的类型是int(*)[4],指向第二行,所以sizeof(a+1)是4或8字节。而a[0]的类型是int[4],a[0]+1int*,指向a[0][1],所以*(a[0]+1)是a[0][1]的值。 用户可能提供的代码可能结合结构体数组和这些指针操作,需要逐行分析每个表达式的结果。例如,结构体数组元素的地址计算,指针移动后的成员访问,sizeof不同表达式的输出等。 总结来说,我需要根据用户提供的引用中的代码片段,假设具体的结构体数组和指针操作,并逐步解析每个关键代码行的逻辑,以确定输出结果。可能需要考虑结构体内存对齐、指针运算、sizeof在不同上下文中的行为,以及数组到指针的隐式转换等问题。</think>### 分步解析示例代码输出 假设存在以下C语言代码(综合引用内容): ```c #include <stdio.h> struct student { char name[20]; int scores[4]; }; void fun1(struct student *p) { printf("%d\n", sizeof(p)); printf("%d\n", sizeof(p->scores)); } int main() { struct student arr[3]; struct student *p = arr; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(p)); fun1(p); int a[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}; printf("%d\n", *(a[0] + 1)); printf("%d\n", sizeof(a + 1)); return 0; } ``` #### 逐行分析 1. **结构体大小计算** - `struct student` 包含 `char name[20]`(20字节)和 `int scores[4]`(4×4=16字节)。假设无内存对齐填充,总大小为 $20+16=36$ 字节。但实际编译器可能按4字节对齐,因此 `name[20]` 实际占用20字节(已经是4的倍数),整体结构体大小为 $36$ 字节[^2]。 2. **`sizeof(arr)`** - `arr` 是包含3个结构体的数组,总大小为 $3 \times 36 = 108$ 字节,输出 `108`[^2]。 3. **`sizeof(p)`** - `p` 是指针,32位系统输出 `4`,64位系统输出 `8`。假设为32位环境,输出 `4`[^3]。 4. **`fun1(p)` 内部** - `sizeof(p)`:指针大小,输出 `4`[^3]。 - `sizeof(p->scores)`:`scores` 是 `int[4]`,大小为 $4 \times 4 = 16$ 字节,输出 `16`。 5. **二维数组操作** - `*(a[0] + 1)`:等价于 `a[0][1]`,值为 `2`,输出 `2`。 - `sizeof(a + 1)`:`a` 退化为指针,`a + 1` 是 `int(*)[4]` 类型,指针大小输出 `4`(32位系统)。 #### 最终输出结果 ``` 108 4 4 16 2 4 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值