预处理、const、static和sizeof

预处理、const、static和sizeof

使用#define宏定义时需要注意的地方

1
2
3
4
5
6
7
#include<stdio.h>
#define SQR(x) (x*x)
int main()
{
int a,b=3;
a=SQR(b+2);
}

如果是上面这个方式,由于宏定义展开是在预处理时期,也就是编译之前,此时b并没有被赋值,这是b只是一个符号,则a=(b+2*b+2),所以应该将define的内容改为

1
#define SQR(x) ((x)*(x))

宏参数的连接

使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起。

const的作用

1、用于定义常量,编译器可以对其进行数据静态类型安全检查

2、修饰函数形式参数,将“值传递”改为“const &传递”可以提高效率

3、修饰函数的返回值,返回值不能被直接修改

4、修饰类的成员函数,任何不会修改数据成员的函数都应该用const来修饰

常量和指针常量

1
2
3
4
5
6
7
8
9
int j=0;
const int i=0; //i是常量,i的值不会被修改
const int *p1=&i; //指针p1所指内容是常量,可以不初始化
int * const p2=&j //指针p2是常量,所指内容可修改
const int * const p3=&i; //指针p3是常量,所指内容也是常量
p1=&j //合法
*p2=100; //合法
1const声明的变量只能被读
2、必须初始化

const与define区别

define只是用来做文本替换的。

在编译的时候,就将define的所有内容进行替换了,它的生命周期止于编译期,它存在于程序的代码段,在实际程序中,它只是一个命令中的参数或者是常数, 并没有实际的存在。

const存在于程序的数据段,并且在堆栈分配了空间,他在程序中可以被调用、传递,const常量有数据类型,而宏常量没有数据类型,编译器可以对const常量进行检查。

程序的内存分配

1
2
3
4
5
6
7
8
9
10
11
12
int a = 0; //全局初始化区 
char *p1;//全局未初始化区
void main(void)
{
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; //123456\0在常量区,p3在栈上
static int c =0; //全局(静态)初始化区
p1 = (char *)malloc(10); //堆
p2 = (char *)malloc(20); //堆
}

栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 (编译器自动分配释放,连续,高地址向低地址,空间限制,分配速度快,函数调用中先入下一条语句位置,从右到左的参数,局部变量)

堆区(heap) — 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。 (不连续空间,速度慢,有碎片,向高地址扩展,比较灵活,大)

全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量、未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放

文字常量区—常量字符串就是放在这里的。程序结束后由系统释放

程序代码区—存放函数体的二进制代码。

1
2
3
4
5
6
7
程序段(Text):程序代码在内存中的映射,存放函数体的二进制代码。
初始化过的数据(Data):在程序运行初已经对变量进行初始化的数据。
未初始化过的数据(BSS):在程序运行初未对变量进行初始化的数据。
栈 (Stack):存储局部、临时变量,函数调用时,存储函数的返回指针,用于控制函数的调用和返回。在程序块开始时自动分配内存,结束时自动释放内存,其操作方式类似于数据结构中的栈。
堆 (Heap):存储动态内存分配,需要程序员手工分配,手工释放.注意它与数据结构中的堆是两回事,分配方式类似于链表。
注:1.Text, BSS, Data段在编译时已经决定了进程将占用多少VM,可以通过size,知道这些信息:
2. 正常情况下,Linux进程不能对用来存放程序代码的内存区域执行写操作,即程序代码是以只读的方式加载到内存中,但它可以被多个进程安全的共享。

static有什么作用

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

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

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

static全局变量和普通全局变量的区别

1、static全局变量与普通全局变量的区别,static全局变量只初始化一次,防止在其他文件单元被引用。

2、static局部变量和普通局部变量的区别,static局部变量只被初始化一次,下一次依据上一次结果值。

3、static函数与普通函数的区别是,static函数在内存中只有一份,普通函数在每个被调用中维持一份复制品。

sizeof计算函数类的空间大小

普通函数不占内存,只要有虚函数,就会占用一个指针大小的内存,系统多用了一个指针维护这个类的虚函数表,无论这个类里面有多少个虚函数,都不会再影响类的大小。

空类占一个字节,编译器会插一个char给空类,用来标记它的每一个对象

sizeof和strlen的区别

1、sizeof是操作符,strlen是函数。

2、sizeof操作符的结果类型是size_t,sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以“\0”结尾。

#pragma pack的作用

可以设置对齐为其他数字,会导致sizeof结果变化。

inline函数的好处

1、inline定义的函数,函数的代码被放入符号表中,在使用的时候进行替换,没有了调用的开销,效率也很高。

2、类的内联函数是一个真正的函数,在调用的过程中,编译器会检查他的参数类型,保证调用正确。

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

inline函数的缺点

inline是以代码膨胀作为代价的。

1、如果函数体内的代码比较长,会导致内存消耗代价比较高。

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

inline函数和宏的区别

1、inline函数在编译的时候展开,宏在预编译的时候展开。

2、在编译的时候,inline可以直接被镶嵌到目标代码里面,而宏只是一个简单的文本替换。

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

4、宏不是函数,inline函数是函数

5、宏在使用的时候要注意二义性,而inline则不会出现二义性。