c++学习2|零值的判断 大端小端 柔性数组 位断 点分十进制

零值的判断

指针型

只能用NULL来判断,不能用0来判断

int main(){
    int* s =nullptr;
    if(NULL == s){...;}
    if(NULL != s){...;}
}

特殊情况:

struct Node{
    char ch;
    int num;
    char str[5];
    double dx;
    int sum;
};
int main(){
    int a =10;
    Node x{};
    int* s =nullptr;
    int Node:: * ip =nullptr;//0xffff ffff
    ip = &Node::num;//0x0000 0004
    ip = &Node::sum;//0x0000 0018
    //此处指针ip并非为0值
    return 0;
}

ip的地址存放的是结构体内成员首地址的偏移量,未定义时为0xffff ffff来作为空指针。

bool型

int main(){
    bool a;
    if(a){...}
    //直接使用a即可
}

float型

const float EPSION;
void main(){
    float ft = ...;
    if(ft>-EPSION && ft <EPSION){
        //0
    }else...
}

原因如下:

float在内存中的表示方法

float占4字节,也就是32位二进制位,有以下三部分组成:

1.符号位(Sign ,S):0代表正,1代表负

2.指数位(Exponent,E):用于储存科学计数法中的指数数据,并且采用移位储存

​ 要加上127.

3.尾数部分(Mantissa,M):尾数部分

(-1)^S * M *2^E

在这里插入图片描述

例如:float a = 12.25 怎么转化为二进制储存?

12转化为二进制为1100,0.25 = 1/4 = 1/2^2 ,为0.01

或者:

0.25*2 = 0.5 取整数部分0

0.5*2 = 1 取整数部分1

因此12.25转化为二进制为1100.01.

而尾数部分要求 1=<M<2 因此需要移动小数点

1100.01-> 1.10001*2^3

小数点左移3位,因此指数位要乘上2^3.

指数位:3+127=>(0000 0011+0111 1111) ->(1000 0010)

尾数部分: 将刚计算的1.10001的小数部分放入即可(1000 1000)

依次存入内存之中:

(0 1000 0010 1000 1000)

四位转化:

(0100 0001 0100 0100)=> 41 44

利用内存演示一下

在这里插入图片描述

44 41 的原因是小端模式

计算的时候可以发现小数部分遇到乘2化不了1的数时,将会永远乘下去,例如0.1乘2永远不可能消灭小数部分。因此M的精度会有损失。

M有精度的损失 ->范围比较,不能使用等值比较。

大端小端模式

小端模式:数据的低位放在低地址空间,数据的高位放在高地址空间。

存放的时候是以一个存储单元为单位来存放,八位为一个存储单元,也就是一个字节为一个存储单元

上面的例子中

(0100 0001 0100 0100)从左到右先是数据的高位到低位

(0100 0001)为一个存储单元(0100 0100)为一个存储单元

对应的41 44,因此41存在内存的高位,44存在内存的低位

因此呈现出了我们上面内存中44 41 的样子

大端模式相反

数据的低位存放在高地址空间,数据的高位存放在低地址空间。

柔性数组

结构体中未定义大小的数组称为柔性数组,不占用结构体大小,只是占位标记,在使用时,需要多少定义多少的大小

struct StrNode{
    int ref;
    int len;
    int size;
    char data[];//柔性数组,此时data的大小为0,只是占位、标记,不占用结构体的大小
};//此结构体的大小为12
int main(){
    StrNode* s =(StrNode*)malloc(sizeof(StrNode)+sizeof(char)*20);
    //这样来定义这个结构体,数组的大小为20
}

位断

struct SNode{
    int a:4;
    int b:3;
    int c:4;
    int d:4;
}

这种方式叫做位断。

在正常情况下,结构体的大小需要补足空间的操作,int占4字节,char占1字节等等,但位断使得:a占四字节,b占3字节。

这样的方法可以节省空间,需要多少用多少。

具体就体现在ip地址中。

涉及到计算机网络

点分十进制法

在计算机网络中 ip 地址是 32 位无符号整型(unsigned int ) ,但是在描述 IP 地址采用”点分十进制记法” 例如 ip =2148205375 ; ip 地址的点分十进制记法为:“128.11.3.31”.不使用位运算

typedef unsigned char u_char;
typedef unsigned int u_int;
union IpNode{
    u_int addr;
    u_char s[4];
    struct{
        u_char s4;
        u_char s3;
        u_char s2;
        u_char s1;
    };
};
void iptoStr(u_int xip,u_char* buff,int len){
    IpNode x;
    x.addr = xip;
 sprintf_s((char*)buff,len,"%d.%d.%d.%d",x.s1,x.s2,x.s3,x.s4);
}
int main(){
    const int iplen = 32;
    u_int xip = 2148205375;
    u_char ipstr[iplen] = {};
    iptoStr(xip,ipstr,iplen);
    cout<<"ip: "<<ipstr<<endl;
    return 0;
}

联合体中,addr s struct 共用4个字节空间

关于sprintf_s和sccanf_s的用法

printf()函数,scanf()函数我们经常会用到,一个将需要打印的呈现在屏幕之上,一个将屏幕上的截取下来到变量之中。

实际printf()函数返回的是数组的长度

在这里插入图片描述

scanf()我们可以理解为,将我们输入到会话框中的一系列字符串,转化成%d %s 等等格式存入变量。

而sprintf()与sscanf()与其相反

sscanf(const char *str, const char *format, …);

str:待解析的字符串;

format:字符串格式描述;

int year, month, day;
 
int converted = sscanf("20230423", "%04d%02d%02d", &year, &month, &day);
printf("converted=%d, year=%d, month=%d, day=%d/n", converted, year, month, day);

在这里插入图片描述

sprint()的用法:

int sprintf( char *buffer, const char *format [, argument] … );

printf 和 sprintf 都使用格式化字符串来指定串的格式,在格式串内部使用一些以“%”开头的格式说明符(format specifications)来占据一个位置,在后边的变参列表中提供相应的变量,最终函数就会用相应位置的变量来替代那个说明符,产生一个调用者想要 的字符串。

将整数123打印成一个字符串保存在s中

sprintf(s,"%d",123);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值