Skip to content

Latest commit

 

History

History
129 lines (101 loc) · 4.97 KB

File metadata and controls

129 lines (101 loc) · 4.97 KB

C++ 语言细节

负数取余

假设 y = ax + b 其中y是除数,x是被除数,a是商,b是余数 记住一点就是:b 应该与 a 同号,也就是说,余数永远和被除数同号 -5 = -1 * 3 + -2 -5 = 1 * -3 + -2

这样就能得到对应的商和余数了,不管被除数是正还是负

字节序与数据类型存储

字节序

字节序分为大小端,注意这里指的是字节的顺序,再往细分就不关注了,也就是说如果 0x11223344 的一个值在内存中以 低地址 存放 11 高地址存放 44,那么就是大端。 如果是高地址存放 11 低地址存放 44 那就是小端。

计算机处理字节序的时候,不知道什么是高位字节,什么是低位字节。它只知道按顺序读取字节,先读第一个字节,再读第二个字节。

如果是大端字节序,先读到的就是高位字节,后读到的就是低位字节。小端字节序正好相反。

字节序的判断

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    union{
        short s;
        char c[sizeof(short)];
    }un;
    un.s = 0x0102;
    if(sizeof(short) == 2)
    {
        if(un.c[0] == 1 && un.c[1]== 2)   //将高序字节地址存储在起始地址,成为大端字节序 
            printf("big-endian\n");
        else if(un.c[0] == 2 && un.c[1]== 1) //将低序字节存储在起始地址,为小端字节序 
            printf("little-endian\n");
    }
    else
    {
        printf("sizeof(short)= %d\n",sizeof(short)); 
    }
    return 0;
}

位段

位段,就是C语言允许在一个结构体中以位为单位来指定其成员所占内存长度。

位段的定义的格式为:type [varName]:num 其中的type只能为intunsigned intsigned int 三种类型中的一种。而varName 是可选的,可以没有。num是所占的位数不可以超过类型所占位数的最大值。

typedef struct node {
        unsigned int a:1;
        char b; //在一个位段存储单元中能够存下所有的成员,所以占4Byte
        int c:1;
        unsigned int :0; // 占位为0的位段必须是无名位段
        int :6; // 占6位的无名位段
        int d:2;
        unsigned int e:2;
}  S;

对于位段的对齐规则为:后面的字段将紧邻前一个字段存储,直到不能容纳为止。

整形存储

整形

一个小例子:

int a = 65;
char *s = (char *)(&a);
printf("%s",s);

这里会输出A,代码虽然少,但是考点很多,一个是整型的存储,一个是大小端,一个是%s 的结束符,首先是整型的存储,对于整数,它的补码等于原码,所以 65 的存储是 0x00041; 然后是字节序,明显s指向的是a的低地址的数据,因为是小端32位,所以指向的也就是a的最低有效位的数据 0x41,然后后面是 0x00(结束符),所以会输出0x41=65为ASCII码的字符后遇到结束符然后就退出了。

浮点型存储

浮点型有 float 和 double 两种,前面是32位,后面是64位,对于float类型,是 1 符号 + 8 指数 + 23 尾数,而对于64位则是 1 符号 + 11 指数 + 52 尾数。对于浮点数的存储就是把首位都变为1,比如 8.25 = 1000.01,就是 变成 1.00001 * 2^3,其中符号位为0,指数位为3+127=130=1000 0010(相当于其中的首位1是符号位),其中加127就是因为指数部分会出现负的所以对于正数就应该+127,尾数位为00001因为首位肯定是1所以省略了。

数组做参数退化成指针 求其大小

很明显,如果使用数组名作为形参,会退化为指针进行传值,如何解决这个问题。

例如下面的代码:

void arrFun(char a[])
{
    cout<<sizeof(a)<<endl;
}

void arrRefFun(auto &a)
{
    cout<<sizeof(a)<<endl;
}

template <unsigned N>
void arrTemplateFun(const char (&a)[N])
{
    cout<<N<<endl;
}

int main(int argc, char *argv[])
{

    char a[10] = "abcd";
    char *b = new char[10] ();

    arrFun(a);// 4
    arrRefFun(a);// 10
    arrTemplateFun("abcd");// 5
    arrTemplateFun(a);// 10

    arrFun(b);// 4
    arrRefFun(b);// 4
    //arrTemplateFun(b);// 错误用法,不能实例化模板

    return 0;
}

其中第一种方式就是使用的是数组名传参,可以看到输出的是指针的大小为4byte,数组的维度信息丢失了

第二种方式使用新标准的auto的方式,得到的类型就是实参的类型,也就是说,如果又两个 数组 char a[10] char b[20] 使用auto对两者进行类型推断得到的两种类型是不一样的,一个是 维度为10 的数组,一个是维度是20的数组

第三种方式是通过非类型模板的形式进行模板实例化,这种方式有限制,只有维度是字面值,可以推断出N 的大小的形式才能使用,不过对于解决这个问题这种限制可以忽略。也就是说我们不能使用一个 普通局部变量动态对象 作为 指针引用非类型模板参数实参

模板相关