《C++高效编程》笔记(壹)

《C++高效编程》笔记

标签(空格分隔):《C++高效编程》


本博客是看《C++高效编程》的笔记,还在更新中。。。

书张这样:

《C++高效编程》笔记(壹)_第1张图片
第一部分扯蛋,没怎么看,直接看第二部分。的确是好书,值得一看。

从语言开始

《C++高效编程》笔记(壹)_第2张图片
三条指令教你做人:

  • 产生汇编文件hello.s

gcc -s hello.c

  • 产生二进制文件

gcc -c hello.c

  • 产生预处理文件

gcc -E hello.c

gcc -E hello.c >> output.txt 输出结果放入一个文件中【>> 流操作】

预处理器干了啥

  1. 替换#include头文件
  2. 替换#define宏

汇编器

c++内嵌汇编实例

#include
#include

using namespace std;
const int mmax = 100;

void change_xyz(int x, int y)
{
    int z = 3;
    printf("x, y, z %d %d %d\n", x, y, z);

    asm("movl $4,8(%ebp)");
    asm("movl $5,12(%ebp)");
    asm("movl $6,12(%esp)");
    printf("x, y, z %d %d %d\n", x, y, z);
}

int main()
{
    int x = 1, y = 2;
    change_xyz(x, y);
    return 0;
}

连接器干了啥

生成可执行代码

小技巧

  • 时间分析器(没成功)

gprof test > test.out

据说能显示程序运行时间。

  • 静态源代码分析器

gcc -Wall hello.c -o hello

简直就是某些VS用户的福利呀,clion自带检查所以不需要,哈哈

优化开始

循环的优化

优化1:循环要用到某些量放外面

  1. 判定素数
int isprime(int n)
{
    for(int i = 2; i < sqrt(n); i++)
    {
        if(n % i == 0)
            return 0;
    }
    return 1;
}

换为:

int isprime(int n)
{
    int nn = sqrt(n);
    for(int i = 2; i < nn; i++)
    {
        if(n % i == 0)
            return 0;
    }
    return 1;
}
  1. 增量法(学图形学时知道的,随便举个例子)
int f1(int a)
{
    return a*a-2*a-10;
}

void f2(int n)
{
    for(int i = 0; i < n; i++)
    {
        if(f(i)>0)
            printf("-.-");
    }
}

换成:

int f1(int a)
{
    return a*a-2*a-10;
}

void f2(int n)
{
    int d = f(0);
    for(int i = 1; i < n; i++)
    {
        if(d>0)
            printf("~(-V-)~\n");
        d = d + 2*i-1;//少算一个乘法,嘿嘿
    }
}

优化2:少用成员变量和成员函数

书上提到:存取成员变量是局部变量的两倍,这是因为要用this指针来获得成员变量的基地址。

void f(int n)
{
    for(int i = 0; i < XX.GetSize(); i++)
    {
        sum += XX.num;
    }
}

改为:

void f(int n)
{
    int XXsize = XX.GetSize();
    int XXnum = XX.num;
    for(int i = 0; i < XXsize; i++)
    {
        sum += XXnum;
    }
}

组合的基本类型

你有没有想过一个结构会因为写法不同而产生不同的大小?
我们看一段简单的程序:

#include
#include

using namespace std;

struct A
{
    char a;
    long b;
    char c;
    long d;
};

struct B
{
    char a;
    char c;
    long b;
    long d;
};
#pragma pack(push, 1)
struct C
{
    char a;
    long b;
    char c;
    long d;
};
#pragma  pack(pop)

int main()
{
    printf("%d\n", sizeof(A));
    printf("%d\n", sizeof(B));
    printf("%d\n", sizeof(C));

    return 0;
}

结果是32, 24, 18

为什么呢?

我们看一下储存结构:(long 类型字节顺序随系统不同而不同)

A地址 内容
00 字符a
01 未用
02 未用
03 未用
04 long b, byte 0
05 long b, byte 1
06 long b, byte 2
07 long b, byte 3

结构B更为紧凑

A地址 内容
00 字符a
01 字符c
02 未用
03 未用
04 long b, byte 0
05 long b, byte 1
06 long b, byte 2
07 long b, byte 3

A,B的区别告诉我们:原来我们储存struct时会以最大的数据类型作为基准来放数据。这叫类型对齐

结构C没有空隙

A地址 内容
00 字符a
01 long b, byte 0
02 long b, byte 1
03 long b, byte 2
04 long b, byte 3
05 字符c
06 long d, byte 0
07 long d, byte 1

编译指令#pragma pack可以让编译器暂时调整对齐

先用push命令把当前的对齐方式放入编译器栈堆中,最后pop回到初始对齐方式。

1是两个变量的间距。例:当把char换为short时,我们的命令为:

#pragma pack(push, 2)
struct C
{
    short a;
    long b;
    short c;
    long d;
};
#pragma  pack(pop)

比特域

比特域是一种极端的变量对齐方式。它直接指定变量的比特大小。

比特域举例

#include
#include

using namespace std;

struct A
{
    int n1:11;//long1
    int n2:11;
    int n3:10;

    int n4:11;//long2
    int n5:11;
    int n6:10;
};

int main()
{
    printf("%d", sizeof(A));//8
    return 0;
}

编译器会把整个结构压缩在两个long类型中,因此结构体内变量的顺序是十分重要的。

错误的举例

#include
#include

using namespace std;

struct A
{
    int n1:11;//long1
    int n2:11;
    int n3:11;//long2

    int n4:11;
    int n5:10;
    int n6:10;//long3
};

int main()
{
    printf("%d", sizeof(A));//12
    return 0;
}

比特域节省空间,那它的效率如何呢?书上举了一个例子,同一个程序用struct Bitfield{unsigned num: 11;};与struct Structure{short num;};两种不同的结构,比特域的汇编长度是short的好几倍(书上例子中是三倍)

另外书上补充说明了比特域的写法。

错误举例

struct F
{
    unsigned a, b : 4;//创建了一个32位的比特域和4位的比特域。
}

可以在一个结构中包含比特域和基本类型,但不提倡,因为只要结构中出现比特域,所有变量都会以long为基准。

struct C
{
    char a;//long
    unsigned b :4;
}

就像上面这个结构体,a,b存在一个long中。

最后,比特域无法使用地址,无法对比特域初始化引用。

联合

联合可以使几个变量共享一块存储空间,联合的大小就是它所包含的最大元素的大小。

同时联合的优点也是它的缺点,当我们经常使用联合中大小较小的变量时,就不考虑使用联合了。

你可能感兴趣的:(=====大学=====,我的书单)