C/C++位域(Bit-fields)之我见(不错)

基于大语言模型的络威胁情报自动化提取与检测规则生成实践 络流量分析是络安全领的核心基础,通过对数据包(PCAP)的深度解析,可以识别攻击模式、提取威胁指标(IOC)。传统方法依赖安全分析师手动编写检测规则,效率低下且门槛较高。随着大语言模型(LLM)技术的发展,其强大的自然语言理解和逻辑生成能力为自动化安全运营提供了新范式。通过将非结构化的流量特征描述转化为结构化的检测逻辑,能够实现威胁情报的快速提取与规则生成,显著提升威胁狩猎和事件响应的效率。本文聚焦于利用领微调模型(如SecGPT-14B),构建从PCAP预处理、特征描述、到IOC逻辑生成与验证的完 阅读详情

转载:http://blog.csdn.net/ztz0223/article/details/3599016

前言

很早想说说这个问题了,经常也会有很多公司拿位域出来考人,呵呵要真的想弄清楚还要一点点的分析。

这里先看看网宿的一道笔试题目,这道题目我之前是复制网上的,结果不对,修改了一下,可以正确运行了,谢谢(imafish_i )提醒:

//假设硬件平台是intel x86(little endian)  

typedef unsigned int uint32_t; 
void inet_ntoa(uint32_t in)  
{  
    char b[18];  
    register char *p;  
    p = (char *)∈  
#define UC(b) (((int)b)&0xff)  
    sprintf(b, "%d.%d.%d.%d/n", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));  
    printf(b);  
}  
int main()  
{  
   inet_ntoa(0x12345678);
   inet_ntoa(0x87654321);  
} 

有点难度的一道题目,其实理解的也很简单。

位域(Bit-fields)分析

 

位域是c++和c里面都有的一个概念,但是位域有一点要注意的有很多问题我们一样样的看:

 

大端和小端字节序


这个很简单,就是起始点该怎么确定。
先看一个程序:

  1. union {
  2.     struct  
  3.     {
  4.         unsigned char a1:2;
  5.         unsigned char a2:3;
  6.         unsigned char a3:3;
  7.     }x;
  8.     unsigned char b;
  9. }d;
  10. int main(int argc, char* argv[])
  11. {
  12.     d.b = 100;
  13.     return 0;
  14. }

那么x的a1,a2,a3该怎么分配值,100的二进制是:0110 0100,那么a1到a3是不是就是依次取值恩?
不是!
我们先看看100分配位的低端是左边的0还是右边的0?很明显是右边的0,那么我们再看a1到a3的分配是从低端到高端的
那么,对应的应该是
<<<<<<--内存增大
a3   a2  a1
011  001 00


内存增大之所以这么写是因为,011是在高位!
而不是通常认为的的:
a1   a2  a3
011  001 00

还有一个情况多见就是一个二进制的数字转化为点分十进制数值,如何进行,这里涉及到大端还是小端的问题,上面没有涉及,主要是因为上面是一个字节,没有这个问题,多个字节就有大端和小端的问题了,或许我们应该记住这一点就是,在我们的计算机上面,大端和小端都是以字节为准的,当然严格来说更应该以位为准不是吗?具体可以参考维基百科上面的一篇文章,他给出了一个以位为准的大小端序的图:

http://en.wikipedia.org/wiki/Endianess

 

下面研究字节为单位的大小端序,继续看代码吧,如下:

  1. int main(int argc, char* argv[])
  2. {
  3.     int a = 0x12345678;
  4.     char *p = (char *)&a;
  5.     char str[20];
  6.     sprintf(str,"%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
  7.     printf(str);
  8.     return 0;
  9. }

这个程序假设是小端字节序,那么结果是什么?
我们看看应该怎么放置呢?
每个字节8位,0x12345678分成4个字节,就是从高位字节到低位字节:12,34,56,78,那么这里该怎么放?如下:
---->>>>>>内存增大
78 56 34 12

 

因为这个是小端,那么小内存对应低位字节,就是上面的结构。

接下来的问题又有点迷糊了,就是p怎么指向,是不是指向0x12345678的开头--12处?不是!12是我们所谓的开头,但是不是内存

的开始处,我们看看内存的分布,我们如果了解p[0]到p[1]的操作是&p[0]+1,就知道了,p[1]地址比p[0]地址大,也就是说p的地址

也是随内存递增的!

12 ^ p[3]
    |
34 | p[2]
    |
56 | p[1]
    |
78 | p[0]
内存随着箭头增大!同时小端存储也是低位到高位在内存中的增加!
这样我们知道了内存怎么分布了

那么:

  1. sprintf(str,"%d.%d.%d.%d", p[0], p[1], p[2], p[3]);

str就是这个结果了:
120.86.52.18

那么反过来呢?

  1. int main(int argc, char* argv[])
  2. {
  3.     int a = 0x87654321;
  4.     char *p = (char *)&a;
  5.     char str[20];
  6.     sprintf(str,"%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
  7.     printf(str);
  8.     return 0;
  9. }

依旧是小端,8位是一个字节那么就是这样的啦:

87 ^ p[3]
     |
65 | p[2]
    |
43 | p[1]
    |
21 | p[0]

结果是:
33.67.101.-121
为什么是负的?因为系统默认的char是有符号的,本来是0x87也就是135,大于127因此就减去256得到-121
那么要正的该怎么的弄?
如下就是了:

  1. int main(int argc, char* argv[])
  2. {
  3.     int a = 0x87654321;
  4.     unsigned char *p = (unsigned char *)&a;
  5.     char str[20];
  6.     sprintf(str,"%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
  7.     printf(str);
  8.     return 0;
  9. }

用无符号的!
结果:
33.67.101.135

 

位域的符号(正负)

 

看完大端和小端以后,再看看位域的取值的问题,上面我们谈到了一些,首先就是位域是按照位来取值的跟我们的int是32位char是8

位一样,很简单,但是,要注意一点就是位域也有正负,指有符号属性的,就是最高位表示的,也会涉及到补码这个一般被认为非常

恶心的东西,看看程序吧:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. int main(int argc, char** argv)
  5. {
  6.     union
  7.     {
  8.         struct
  9.         {
  10.             unsigned char a:1;
  11.             unsigned char b:2;
  12.             unsigned char c:3;
  13.         }d;
  14.         unsigned char e;
  15.     } f;
  16.     f.e = 1;
  17.     printf("%d/n",f.d.a);
  18.     return 0;
  19. }

<小端>
那么输出是什么?
换一下:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. int main(int argc, char** argv)
  5. {
  6.     union
  7.     {
  8.         struct
  9.         {
  10.             char a:1;
  11.             char b:2;
  12.             char c:3;
  13.         }d;
  14.         char e;
  15.     } f;
  16.     f.e = 1;
  17.     printf("%d/n",f.d.a);
  18.     return 0;
  19. }

输出又是什么?

小端的话,那么,再d.a上面分得1,而这个是无符号的char,那么前者输出是1,没有问题,第二个输出是-1,哈哈。
为什么?
第二个是无符号的,就一个位分得1,那么就是最高位分得1,就是负数,负数用的补码,实际的值是取反加1,就是0+1=1,再取符

号负数,就是-1.

 

整型提升

 

最后的打印是用的%d,那么就是对应的int的打印,这里的位域肯定要提升,这里有一点,不管是提升到有符号还是无符号,都是自

己的符号位来补充,而不改变值的大小(这里说的不改变值大小是用相同的符号属性来读取),负数前面都补充1,正数都是用0来补充

,而且也只有这样才能保证值不变,比如,char提升到int就是前面补充24个char的最高位,比如:

  1.  char c = 0xf0; 
  2.  int p = c;
  3.  printf("%d %d/n",c,p);

输出:-16 -16
p实际上就是0xfffffff0,是负数因此就是取反加1得到
c是一个负数那么转化到x的时候就是最高位都用1来代替,得到的数不会改变值大小的。
再看:

  1.  char c = 0xf0;
  2.  unsigned int x = c;
  3.  printf("%u/n",x);

得到的结果是4294967280,也就是0xfffffff0,记住,无符号用%u来打印。

地址不可取


最后说的一点就是位域是一个字节单元里面的一段,是没有地址的!

 

附录

 

最后附上《The C Book》这本书的一段说法:
While we're on the subject of structures, we might as well look at bitfields. They can only be declared inside a

structure or a union, and allow you to specify some very small objects of a given number of bits in length. Their

usefulness is limited and they aren't seen in many programs, but we'll deal with them anyway. This example should

help to make things clear:

  1. struct {
  2.       /* field 4 bits wide */
  3.       unsigned field1 :4;
  4.       /*
  5.        * unnamed 3 bit field
  6.        * unnamed fields allow for padding
  7.        */
  8.       unsigned        :3;
  9.       /*
  10.        * one-bit field
  11.        * can only be 0 or -1 in two's complement!
  12.        */
  13.       signed field2   :1;
  14.       /* align next field on a storage unit */
  15.       unsigned        :0;
  16.       unsigned field3 :6;
  17. }full_of_fields;

Each field is accessed and manipulated as if it were an ordinary member of a structure. The keywords signed and

unsigned mean what you would expect, except that it is interesting to note that a 1-bit signed field on a two's

complement machine can only take the values 0 or -1. The declarations are permitted to include the const and

volatile qualifiers.

The main use of bitfields is either to allow tight packing of data or to be able to specify the fields within some

externally produced data files. C gives no guarantee of the ordering of fields within machine words, so if you do

use them for the latter reason, you program will not only be non-portable, it will be compiler-dependent too. The

Standard says that fields are packed into ‘storage units’, which are typically machine words. The packing order, and

whether or not a bitfield may cross a storage unit boundary, are implementation defined. To force alignment to a

storage unit boundary, a zero width field is used before the one that you want to have aligned.

Be careful using them. It can require a surprising amount of run-time code to manipulate these things and you can

end up using more space than they save.

Bit fields do not have addresses—you can't have pointers to them or arrays of them.

最后

了解了这些我想网宿的那道题目也很简单了。希望大家指正。

 

基于本地大语言模型的终端安全智能分析系统设计与实践 终端安全检测与响应(EDR)是络安全体系的核心环节,其核心原理在于对终端进程、络连接、文件操作等行为数据进行持续监控与分析,以识别异常和威胁。传统基于规则或特征匹配的方法在面对新型、复杂的攻击时,存在规则滞后、误报漏报等问题。大语言模型(LLM)凭借其强大的上下文理解和推理能力,为安全分析带来了新的技术价值:它能够像人类分析师一样,理解行为序列背后的意图和上下文关联,从而更精准地识别高级持续性威胁(APT)和内部威胁等复杂攻击模式。在应用场景上,结合本地化部署的LLM,可以在保障数据隐私和低延迟响应的前 阅读详情

相关推荐

(络见的.)

有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制。例如在存放一个开关量时,只有0和1 两种状态, 用一二进即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“”或“段”。所谓“”是把一个字节中的二进划分为几个不同的区, 并说明每个区数。每个有一个名,允许在程序中按名进行操作。 这样就可以把几个不同的对象用一个字节的

1451

C++面试基础系列-bit_field

而在 “联合” 中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。在一个 “联合” 内可以定义多种不同的数据类型, 一个被说明为该 “联合” 类型的变量中,允许装入该 “联合” 所定义的任何一种数据,这些数据共享同一段内存,以达到节省空间的目的。一个成员不允许跨越两个 unsigned int 的边界,如果成员声明的总数超过了一个 unsigned int 的大小, 那么编译器会自动移成员,使其按照 unsigned int 的边界对齐。在C和C++编程中,

wesigj的博客 1616

C/C++ 字节序/(Bit-fields)之我见

前言很早想说说这个问题了,经常也会有很多公司拿出来考人,呵呵要真的想弄清楚还要一点点的分析。这里先看看宿的一道笔试题目,这道题目我之前是复制上的,结果不对,修改了一下,可以正确运行了,谢谢(imafish_i )提醒://假设硬件平台是intel x86(little endian) typedef unsigned int uint32_t; void inet_ntoa(uin

大周辞寒的专栏 2万+

大雄的学习笔记

写在前面, 这是我读其他人的文章写下的笔记,第一版和原文几乎相同。侵联删, 后续我也会加入我在其他地方学到的相关的东西,也就是说,这是我自己的一个学习笔记。 当然了,既然是学习笔记,除了链接原文外肯定还有其他内容 原文链接:https://github.com/Light-City/CPlusPlusThings/tree/master/basic_content/bit 1、是什么? 1)定义 “ “ 或 “ 段 “(Bit field)为一种数据结构,可以把数据以的形式紧凑的储存,并允许程

Nobita_Nobi的博客 573

C++ 中的(bit field)定义方式

但是需要注意的是,只能用于整型数据类型,而且每个变量的取值范围必须是固定的。(bit field)C++ 中一种特殊的数据类型,它可以用来定义一个或多个二进制的变量。在中,每个二进制都有自己的存储空间,可以单独进行读写操作。一个无符号整型变量,占用 4 个二进制。一个无符号整型变量,占用 4 个二进制。一个无符号整型变量,占用 1 个二进制。一个无符号整型变量,占用 1 个二进制。一个无符号整型变量,占用 1 个二进制。一个无符号整型变量,占用 1 个二进制

T20151470的博客 679

大模型量化从0到1(九):用 llama.cpp 把模型转成 GGUF 并跑本地推理

这篇文章详细介绍了如何将HuggingFace模型转换为GGUF格式并进行量化推理的完整流程。主要内容包括: 整体流程:从HuggingFace模型到GGUF格式转换(FP16),再进行量化压缩,最后本地推理运行 关键步骤: 编译安装llama.cpp工具链 使用convert_hf_to_gguf.py脚本将模型转换为FP16的GGUF格式 用llama-quantize工具进行不同档的量化(推荐Q4_K_M) 通过llama-cli进行本地CPU/GPU推理 实用技巧: 支持一次转换多个量化档 提供

weixin_49520696的博客 390

深入理解#pragma pack:内存对齐原理、应用场景与跨平台实践

内存对齐是计算机系统中提升数据访问效率与保证程序正确性的基础机制。其原理源于CPU以固定字长(如4字节、8字节)访问内存,非对齐访问可能导致性能下降甚至硬件异常。编译器默认会插入填充字节以实现对齐,但这可能造成内存浪费。在嵌入式开发、络通信等场景中,精确控制内存布局至关重要。通过#pragma pack指令,开发者可以指定结构体成员的对齐边界,从而匹配硬件寄存器或协议定义的二进制格式。本文结合络协议栈、硬件寄存器映射等热词,解析如何利用这一特性优化内存使用并规避跨平台陷阱,为编写高效、健壮的系统级代码提

weixin_34226706的博客 6158

嵌入式数据翻译官NanoPb:Protocol Buffers在MCU上的极简高效实现

在嵌入式系统开发中,数据序列化是实现设备间可靠通信的核心技术。其原理是将结构化数据转换为可存储或传输的格式,关键在于平衡效率、资源占用与跨平台兼容性。Protocol Buffers作为一种高效的二进制序列化机制,通过预定义Schema和编译生成代码,提供了出色的性能与向前兼容性。对于资源受限的微控制器(MCU)应用,其价值在于以极小的内存和计算开销实现结构化数据交换。NanoPb正是为嵌入式环境量身定制的Protobuf C语言实现,它采用无动态内存分配和回调驱动模型,确保了在实时操作系统(RTOS)或裸

weixin_30940783的博客 388

树莓派驱动64x32 RGB LED矩阵屏:从HUB75接口到图像显示的完整实践

嵌入式图形显示是物联设备、信息终端和创意交互项目的核心技术之一,其核心原理在于通过微控制器或单板计算机,将数字图像数据高效、准确地转换为物理像素的亮灭与色彩。RGB LED矩阵屏作为一种常见的显示载体,通过HUB75并行接口接收数据,利用行扫描和视觉暂留效应实现全屏画面的呈现。这项技术的工程价值在于,它能够在有限的硬件资源下,实现高刷新率、高色彩深度的动态显示,广泛应用于桌面摆件、信息看板、频谱可视化等场景。本文以一块经典的64x32 P4间距RGB矩阵屏为例,详细解析了使用树莓派和Python进行驱动的

weixin_34214500的博客 337

Java技术体系之基础能力篇(五)——深入理解JVM原理

1、Java概述 1.1 Java技术体系   从广义上讲,Kotlin、Clojure、JRuby、Groovy等运行于Java虚拟机上的编程语言及其相关的程序都属于Java技术体系中的一员。如果仅从传统意义上来看,JCP官方所定义的Java技术体系包括了以下几个组成部分: Java程序设计语言 各种硬件平台上的Java虚拟机实现 Class文件格式 Java类库API 来自商业机构和开源社区的第三方Java类库   以上是根据Java各个组成部分的功能来进行划分,如果按照技术所服务的领来划分,或者

qsmiley10的博客 3242

C++ Bit fields

【代码】C++ Bit fields

每一个不曾起舞的日子,都是对人生的辜负。 352

Bit field

c 语言中的bit field 是一种节省内存的方式, 用于struct 或者 union 的成员变量的声明。基本的语法是:struct BF { ... type_specifier [declarator] : constant_expression; ... };采用这样一种方式的好处是,我们可以明确地指定某一个成员变量在内存中占用的空间(constant_expression

techx的专栏 4919

C/C++知识点整理四

6.assert() 断言,是宏,而非函数。assert 宏的原型定义在&lt;assert.h&gt;(C)、&lt;cassert&gt;(C++)中,其作用是如果它的条件返回错误,则终止程序执行。可以通过定义NDEBUG来关闭 assert,但是需要在源代码的开头,include &lt;assert.h&gt;之前。 使用assert的缺点是,频繁的调用会极大的影响程序的性能,...

码码的小海绵 304

C/C++详解整理

一、定义: 有些信息在存储时,并不需要占用一个完整的字节,而只需要占一个或几个二进制。例如在存放一个开关量时,只有0和1两种状态,只需要用一二进制即可。为了节省存储空间,并且为了让处理变得更便捷,C语言又提供了一种数据结构,称为“”或者“段”。所谓,就是把一个字节中的二进制划分为不同的区,并说明每个区数。每个都有一个名,允许程序中按照名进行操作。这样就可以把几个不同的对象用一个字节的二进制来表示。 二、结构: 的结构与结构体相似 struct <结构名

m0_46526726的博客 1万+

C++结构中的字段

例如,对于一个字节(8 )的存储单元,若有两个字段a : 3和b : 5,如果是从低开始存储,a将占用最低的 3 ,b占用高 5 ;例如,在一个存储学生成绩等级(A - F,共 6 个等级)的系统中,如果使用普通的char类型来存储每个学生的成绩等级,每个学生需要占用 8 (1 字节)。例如,在某些硬件设备的寄存器映射或者通信协议的数据封装中,数据可能是以为单进行组织的,字段就可以很好地用于表示这些数据。3)协议数据封装:在络协议或者数据存储协议中,数据可能是以特定的模式进行封装的。

wgf350357114的博客 1261

C杂讲 段(

目录 知识点1【段概念】 知识点2【操作】 知识点3【无意义段】 知识点3【应用场景】 知识点1【段概念】 在结构体中,以为单的成员,咱们称之为() struct packed_data{ unsigned int a:2; //成员a只占unsigned int类型中的2 unsigned int b:6; unsigned int c:4; unsigned int d:4; // a b c d相邻 unsigned int i; //..

万里晴空 645

C++BitFields

的定义使用 struct 关键字,但是和定义 struct 不同的是,它需要为每个分量(属性)指定长度,但是每个分量可以和数据结构中其他正常的元素一样使用和初始化。

oRbIt 的专栏 1857

C语言中结构体的bit-fields

有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制。例如在存放一个开关量时,只有0和1 两种状态, 用一二进即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“”或“段”。所谓“”是把一个字节中的二进划分为几个不同的区,并说明每个区数。每个有一个名,允许在程序中按名进行操作。这样就可以把几个不同的对象用一个字节的二进制来表示。一、的定义和变量的说明定义与结构定义相仿,其形式为: struct 结构名 { 列表

一口Linux的专栏 1564
上一篇: Qt——路径设置问题
下一篇: C语言面试题之一——程序阅读题
liming0931
博客等级 码龄15年 482粉丝 264原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值