第二章

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第二章相关的知识,希望对你有一定的参考价值。

Z2S2

用#define实现宏并求最大值和最小值

1 #define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )

2 #define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )

在宏中需要把参数小心地用括号括起来。因为宏只是简单的文本替换,如果不注意,很容易引起歧义。

 

Z2S6

用宏定义得到一个数组所含的元素个数

#define ARR_SIZE(a) (sizeof((a)) / sizeof((a[0])))

假设一个数组定义如下。

int array[100];

它含有100个int型的元素。如果int为4个字节,那么这个数组总共有400个字节。sizeof(array)为总大小,即400个字节;sizeof(array[0])为一个int大小,即4个字节。两个大小相除就是 100,也就是数组的元素个数。这里,为了保证宏定义不会发生“二义性”,在a以及a[0]上都加了括号

 

Z2S7

找错——const的使用

 1 #include <stdio.h>
 2 
 3 
 4 
 5 int main()
 6 
 7 {
 8 
 9   const int x = 1;
10 
11   int b = 10;
12 
13   int c = 20;
14 
15   const int* a1 = &b;
16 
17   int* const a2 = &b;
18 
19   const int* const a3 = &b;
20 
21   x = 2;
22 
23   //a1定义为const int*类型,注意这里的const在int*的左侧,它是用修饰指针所指向的变量,即指针指向为常量。故 a1 指向变量 c是允许的,因为这修改的是指针a1本身。
24 
25   a1 = &c;
26 
27   //但改变a1指向的内容是不允许的。
28 
29   *a1 = 1;
30 
31   //a2定义为int* const类型,注意这里的const在int*的右侧,它是用修饰指针本身,即指针本身为常量。因此下行中修改指针 a2 本身是不允许的。
32 
33   a2 = &c;
34 
35   //但修改a2指向的内容是允许的。
36 
37   *a2 = 1;
38 
39   //a3 定义为const int* const类型,这里有两个const,分别出现在int*的左右两侧,因此它表示不仅指针本身不能修改,并且其指向的内容也不能修改。所以下面2行都会出现编译错误。
40 
41    a3 = &c;
42 
43   *a3 = 1;
44 
45   return 0;
46 
47 }
48 
49 //注意:变量x、a2和a3在声明的同时一定要初始化,因为它们在后面都不能被赋值。而变量a1可以在声明的时候不初始化。

 

Z2S8

说明const与#define的特点及区别

#define只是用来做文本替换的。#define常量是一个Compile-Time概念,它的生命周期止于编译期,它存在于程序的代码段,在实际程序中它只是一个常数、一个命令中的参数,并没有实际的存在。

const常量存在于程序的数据段,并在堆栈分配了空间。const常量是一个Run-Time的概念,它在程序中确确实实地存在着并可以被调用、传递。const常量有数据类型,而宏常量没有数据类型。编译器可以对const常量进行类型安全检查。

 

Z2S9

C++中const有什么作用

(1)const用于定义常量:const定义的常量编译器可以对其进行数据静态类型安全检查。

(2)const修饰函数形式参数:当输入参数为用户自定义类型和抽象数据类型时,应该将“值传递”改为“const &传递”,可以提高效率。比较下面两段代码:

  1 void fun(A a);

  2 void fun(A const &a);

第一个函数效率低。函数体内产生A类型的临时对象用于复制参数a,临时对象的构造、复制、析构过程都将消耗时间。

而第二个函数提高了效率。用“引用传递”不需要产生临时对象,节省了临时对象的构造、复制、析构过程消耗的时间。但光用引用有可能改变a,所以加const。

(3)const修饰函数的返回值:如给“指针传递”的函数返回值加const,则返回值不能被直接修改,且该返回值只能被赋值给加const修饰的同类型指针。例如。

  1 const char *GetChar(void){};

  2 char *ch = GetChar();     // error

  3 const char *ch = GetChar();  // correct

(4)const修饰类的成员函数(函数定义体):任何不会修改数据成员的函数都应用const修饰,这样,当不小心修改了数据成员或调用了非const成员函数时,编译器都会报错。

const修饰类的成员函数形式为:

  int GetCount(void) const;

 

Z2S10

static有什么作用

在C语言中,关键字static有3个明显的作用:

(1)在函数体,一个被声明为静态的变量在这一函数被调用的过程中维持其值不变。

(2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所有函数访问,但不能被模块外其他函数访问。它是一个本地的全局变量。

(3)在模块内,一个被声明为静态的函数只可被这一模块内的其他函数调用。那就是这个函数被限制在声明它的模块的本地范围内使用。

 

Z2S11

static全局变量与普通的全局变量有什么区别

static全局变量与普通全局变量的区别是,static全局变量只初始化一次,防止在其他文件单元中被引用。(作用域不同,静态全局变量只在当前源文件内有效)

static局部变量和普通局部变量的区别是,static局部变量只被初始化一次,下一次依据上一次结果值。(生存期不同)

static函数与普通函数的区别是,static函数在内存中只有一份,普通函数在每个被调用中维持一份复制品。(作用域不同)

 

 Z2S13

使用sizeof计算普通变量所占空间大小

void Func ( char str[100] )

{

  //str 是函数的参数,它在做sizeof 运算时被认为是指针。这是因为当我们调用函数 Func(str)时,由于数组是“传址”的,程序会在栈上分配一个4字节的指针来指向数组,因此结果也是4。

  sizeof(str) = ____;

}

void *p = malloc(100);

//p 首先指向一个100 字节的堆内存。这里还是对指针做sizeof 运算,结果仍然是4。

sizeof(p) =______;

 

Z2S15

使用sizeof计算含有虚函数的类对象的空间大小

普通函数不占用内存,只要有虚函数,就会占用一个指针大小的内存,原因是系统多用了一个指针维护这个类的虚函数表,并且注意这个虚函数无论含有多少项(类中含有多少个虚函数)都不会再影响类的大小。

 

Z2S20

使用sizeof计算联合体的大小

union u2

{

  char a[13];

  int b;

};

对于 u2 来说,最大的空间是 char[13]类型的数组。这里要注意,由于它的另一个成员int b 的存在,u2 的对齐方式变成4,也就是说,u2 的大小必须在4的对齐上,所以占用的空间变为最接近13的对齐,即16。

以编译器会尽量把数据放在它的对齐上以提高内存的命中率。对齐是可以更改的,使用#pragma pack(x)可以改变编译器的对齐方式。C++固有类型的对齐取编译器对齐方式与自身大小中较小的一个。

例如,指定编译器按 2 对齐,int类型的大小是4,则int的对齐为2和4中较小的2。

 

Z2S23

内联函数相比于宏定义的优越之处

inline定义的类的内联函数,函数的代码被放入符号表中,在使用时直接进行替换(像宏一样展开),没有了调用的开销,效率也很高。

类的内联函数也是一个真正的函数。编译器在调用一个内联函数时,首先会检查它的参数的类型,保证调用正确;然后进行一系列的相关检查,就像对待任何一个真正的函数一样。这样就消除了它的隐患和局限性。

inline 可以作为某个类的成员函数,当然就可以在其中使用所在类的保护成员及私有成员。

 

Z2S25

为什么不把所有的函数都定义成内联函数

内联是以代码膨胀(复制)为代价的,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。以下情况不宜使用内联。

如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。

如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。

另外,类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。

所以不要随便地将构造函数和析构函数的定义体放在类声明中。一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这说明了 inline 不应该出现在函数的声明中)。

 

Z2S26

理解内联函数与宏定义的区别

内联函数在编译时展开,宏在预编译时展开

在编译的时候,内联函数可以直接被镶嵌到目标代码中,而宏只是一个简单的文本替换。

内联函数可以完成诸如类型检测、语句是否正确等编译功能,宏就不具备这样的功能。

宏不是函数,inline函数是函数。

宏在定义时要小心处理宏参数(一般情况是把参数用括号括起来),否则容易出现二义性。而内联函数定义时不会出现二义性。

以上是关于第二章的主要内容,如果未能解决你的问题,请参考以下文章

第二次在对话框中膨胀片段时出错

用片段替换时操作栏向下移动

想要使用 SharePreferences 更新第二个片段中的数据,但第二个片段没有更新

使用选项卡第二次返回片段显示空白片段

从片段类中的对话框添加项目到recyclerview。

Huawei_Netconf_Ncclient