C/C++程序设计03(宏,const,sizeof,内联函数)

第6章 预处理、const与sizeof
1.宏定义
格式:
#define pi (365*24*3600)
 说明:宏定义末尾没有“;”,允许使用括号,使用时直接进行替换,不进行任何的类型检查,使用带参数宏时尽量用 括号将参数括起来防止出错; 
  
宏定义是C语言的重要部分,在C++语言中,尽量使用inline替代,后面讲内联函数。
#define area(x) x*x //这样容易出问题,正确应该为 #define area(x) (x)*(x)
/*这在使用中是很容易出现问题的,看如下的程序*/
void main()
{
   int y = area(2+2);
    printf(“%d”,y);
}
上例子中,按理说给的参数是2+2,所得的结果应该为4*4=16,但是错了,因为该程序的实际结果为8,仍然是没能遵循纯粹的简单替换的规则,又是先计算再替换了,这道程序里,2+2即为area宏中的参数,应该由它来替换宏定义中的x,即替换成2+2*2+2=8了那如果遵循(1)中的解决办法,把2+2 括起来,即把宏体中的x括起来,是否可以呢?#define area(x) (x)*(x),对于area(2+2),替换为(2+2)*(2+2)=16,可以解决。
同样:
#define N (2+2) //如果是#define N 2+2  ,则容易出现问题;
void main()
{
   int a=N*N;
  printf(“%d”,a);
}

2.const
指针常量、和常量指针。先看下面例子:
	int b = 500;
	const int* a = &b;	//情况1;
	int const *a = &b;	//情况2;
	int* const a = &b;	//情况3;定义的时候必须初始化:
	const int* const a = &b;	//情况4;

关键:const位置在*的左边还是右边;如果const在星号左侧,则const就是用来修饰指针指向的变量,即指针指向的变量是常量(情况1和情况2);const在星号右侧,则const修饰指针本身,为常量指针(情况3), 此时定义的时候必须初始化;情况4两者都是常量,不能修改。

2.1类中的常量
由于#define宏定义的变量是全局的,有时候不能达到目的,于是得用const来修饰数据成员变量。const数据成员变量只是在某个对象生存期内是常量,而对于整个类而言是可变的,因为类可以创建多个对象,不同的对象可以为不同的const数据成员初始化为不同的值。
类中const数据成员的初始化,只能在构造函数的初始化参数列表中进行初始化,如下:

class A 
{
 A(int size);  // 构造函数 
 const int SIZE ;    
}; 
A::A(int size) : SIZE(size)  // 构造函数的
{ 

} 
A  a(100); // 对象 a 的 SIZE 值为 100 
A  b(200); // 对象 b 的 SIZE 值为 200
不能再类的声明中初始化const数据成员,因为类的对象未被创建,不知道SIZE的值是多少,如下是错误的:

class A 
{
  const int SIZE = 100;   // 错误,企图在类声明中初始化 const 数据成员 
  int array[SIZE];  // 错误,未知的 SIZE 
}; 
有一种比较好的的方法解决这类问题:类中用枚举来实现(枚举常量不会占用对象的存储空间,在编译时全部求值,缺点:隐含数据类型为int,有最大最小的界限,不能表示浮点数),如下:
class A 
{
  enum { SIZE1 = 100, SIZE2 = 200}; // 枚举常量
  int array1[SIZE1];  
  int array2[SIZE2]; 
};

2.2类中的const成员函数
格式如下:
class A
{
public:
	int get(int value_in) const; //const关键在一定要在后面,如果在前面,意味着返回值是const类型;
};

int A::get(int value_in) const
{
	return 1;
}
说明:
const成员函数是“只读”函数,它既不能更改数据成员的值,也不能调用能引起数据成员值变化的成员函数,只能调用const成员函数。如果在成员变量声明时有mutable关键字,如 mutable int a;则a在任何地方都能修改;


2.3 const和宏定义#define的区别
C++两者可以使用,但是const明显有更多的优势:
其一:const有数据类型,编译的时候就可以检查错误,#define没有。
其二:某些工具可以对const进行调试,在C++编程中,应该尽量多用const,少用#define;

3.内联函数inline
将函数指定为内联函数,就是将它在程序中每个调用点上“内联地”展开,在程序中避免函数调用时用到的栈的开销。关键字:inline。内联说明对于编译器来说只是一个建议,编译器可以选择忽略这个建议(比如函数是复杂的递归函数,则编译器并不会内联展开,即使有关键字inline),仅当编译器的成本/效益分析显示它是有益的时候,引发插入(称为内联展开或内联)。 内联展开缓和较大代码尺寸的潜在成本的函数调用开销。对于内联函数应该放在头文件中.
类中内联函数有两种,在类中有函数的定义,该函数隐式转化为内联函数,其二,有关键字,并且在头文件中,例子如下:
class CTest
{
public:
	CTest(void);
	~CTest(void);
	int GetValue() //隐式转化为内联函数;
	{
		return 0;
	}
};
或者:
class CTest
{
public:
	CTest(void);
	~CTest(void);
	int GetValue();//注意:此处不能用关键则inline,在函数定义中使用“内联”会导致它成为一个内联函数。但是,在调用该函数后,重新将该函数声明为“内联”是非法的,所以inline不能出现在函数的声明中
};
inline int CTest::GetValue()
{
	return 0;
}

3.1 内联函数与宏定义
差别:宏定义只是简单的替换,内联函数在编译时将内联函数直接嵌入到目标代码中,这样可以加快速度,减少栈的花销。一般将函数语句少,调用次数多的函数申明为内联函数; 内联函数需要做类型检查;宏不是函数,叫做宏定义;

4.sizeof
作用:求数据内存空间的大小;指针的大小时钟为4个字节,在前面文章中分析过原因。
sizeof是操作符,不是函数,对于变量,可以不用括号.首先看下面的基础类型的大小;
	cout << sizeof (char) << endl; // 结果=1
	cout << sizeof (short) << endl;// 结果=2
	cout << sizeof (float) << endl;// 结果=4
	cout << sizeof (unsigned int) << endl;// 结果=4,所以有符号和无符号并不影响;
	cout << sizeof (int) << endl;// 结果=4
	cout << sizeof (long) << endl;// 结果=4
	cout << sizeof (double) << endl;// 结果=8
	cout << sizeof (double long) << endl;// 结果=8
	cout << sizeof (char*) << endl;// 结果=4
4.1数组和指针的sizeof
见如下代码:
	char a[100];
	cout << sizeof a << endl; // 结果=100	
	char b[] = "asc";
	cout << sizeof b << endl; // 结果=4,结束符站一个字节;

	//指针
	char *p = "ascde";
	cout << sizeof p << endl;// 结果=4
	cout << strlen(p) << endl;// 结果=5
注意:
sizeof计算的是栈中分配的大小,静态变量存放在全局数据区域,不会计算在内。
sizeof计算数组时,数组名不会转化为指针。
sizeof计算函数时,已函数的返回类型计算大小
sizeof是运算符,strlen()是函数。
strlen(char*)输入参数必须是char*,并且以“\0”结尾的。
数组作为函数的输入参数时,永远会转化为指向首元素的指针,所以无法传入数组的大小,数组的大小只能由另外的参数传入。
4.2 结构体,类的对齐以及求sizeof
空类的大小为1,类的实例化是在内存中分配一块地址,每个实例在内存中都有独一无二的二地址。同样,空类也会实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化后就有独一无二的地址了。所以,空类的sizeof为1,而不是0.
class A{ virtual void f(){} };
class B:public A{}
此时,类A和类B都不是空类,其sizeof都是4,因为它们都具有虚函数表的地址。C++标准规定类的大小不为0,空类的大小为1,当类不包含虚函数和非静态数据成员时,其对象大小也为1,请看:
class A{};
class B:public virtual A{};
此时,A是空类,其大小为1;B不是空类,其大小为4.因为含有指向虚基类的指针。
多重继承的空类的大小也是1.
class Father1{}; class Father2{};
class Child:Father1, Father2{};
它们的sizeof都是1.
类和结构体数据对齐:
当结构体中元素的长度都小于处理器的位数(32位-4字节)时,按结构体内最长的数据对齐,结构体的长度一定是最长的数据的整数倍。
























你可能感兴趣的:(面试宝典)