C_C++软件工程师就业求职手册学习笔记---第四章

本文详细介绍了C语言中的预处理概念,包括宏定义、文件包含、条件编译等,并探讨了const常量、static变量及sizeof操作符的用法与注意事项。此外,还对比了宏定义与内联函数的异同。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第四章

4.1 预处理

预处理是指通过预处理的内建功能对一个资源进行等价替换,常见的预处理包括:文件包含#include,条件编译: #ifdef,#ifndef,#else,以及布局控制#progma,还有宏替换#define。

#progma 用法较为复杂,可参照http://baike.baidu.com/link?url=707UZxhAcIKODivQADGQHMTd9dyQ8oELZ122PJ6siDvkQUt_DVatnG2SvqdkD6ONL9va5TKVV0dJAJe76ptb7q

 

例子1:

使用宏定义计算最大值:

#defineMAX(x,y) (((x)>(y))?(x):((y))

由于宏定义只是简单的替换,所以x 和 y 分别为j加上括号,避免产生错误

例如:#define Sq(x)(x*x)  ,当我们调用时,如果是x 是表达式,a+3,那么计算结果就是a+3*a+3,而不是a+3的平方,所以改为(x)*(x)可以消除这种错误。

例子2,连接宏参数:

#defineSTR(s) #s
#defineCon(a,b) (int)(a##e##b)
cout<<STR(abc)<<endl;
cout<<Con(2,3)<<endl;

输出 :

abc

2000

例子2:

使用宏定义去高低位,数组长度

#defineHigh(x) ((x)&255)
#define Arr(a) (sizeof(a)/sizeof(a[0]))

4.2const 常量

const是C语言的关键字,表示不可更改。

例子3:

constint a=2;
a=3;//error
 
constint *b=&a;
*b=3;//error
 
intconst *c=b;
c=&a;//error
 
constint const *d=&a;
*d=3;//error
d=b;//error

例子3

#define和const区别:

#define 只是进行文本替换,不会分配空间,编译器也不会进行检查。

Const 会在堆栈中分配空间,并会进行安全检查。

例子4:const的用处

1、const定义的常量,编译器会对其进行数据静态类型检查。

2、在函数形参定义中,用const可以提高效率。尤其是自定义类型或是对象类型,如f(A a)和f(const A &a),后者省去了赋值构造等过程。

3、const修饰函数返回值,不能被修改,如果返回指针,则不能直接修改,且必须为同类型指针才能接收。例如 const A operator *(A &a,A&b){},这样就不会出现 a*b=c这样的错误了。

4、const修饰成员函数。如果成员函数不需要改变数据成员,那么应该将其定义为const,这样如果不小心修改了数据成员,或者是调用了nonconst函数,编译器就会报错,定义格式如下:int get() const;

4.3static 变量

Static是关键字,表示静态,可以修饰变量和函数。在类中,如果成员定义为static,那么该成员将不再属于类的对象,只属于类。

例子4:static有什么作用

1、在函数体内,被定义为static的变量,在调用过程中,其值保持不变。

2、在模块内,定义为static的变量,只能在模块内被使用,不能被模块外使用。

3、在模块内,定义为static的函数,也只能被模块内的函数调用,不能被模块外使用。

例子5:全局变量、局部变量和函数被定义为static后有什么不同?

对于全局变量来说,改为static后,存储方式没有改变,适用范围改变了。但对于局部变量来说,改变了其生命周期,存储方式发生了改变。

1、static的全局变量,只能在当前文件中使用,只能初始化一次。

2、static局部变量只初始化一次,下一次依照上一次结果

3、static 函数在内存中只保留一份,而普通函数在每个被调用中都维持一份复制品。其只能在当前文件中使用,成为内部函数。

例子6:

在类内部定义的static成员,为各类实例共享,而分单属于某一个实例。

4.4sizeof 操作符

例子7:

void ff(char s[10])
{
         cout<<sizeof(s)<<endl;
}
void main()
{
         char str[]="abcd";
         char *p=str;
         void *q=(void *)malloc(100);
         cout<<sizeof(str)<<”,”<<sizeof(&str[0])<<","<<sizeof(p)<<","<<sizeof(q)<<endl;
}

结果为 4,5,4,4,4

在不把数组传入函数内时,其与指针有区别,传入后,作用与指针相同。

例子8:字符对齐的问题

字符对齐与编译器有关系,但是一般有以下三个原则。

1、结构体变量的首地址能够被其最宽的基本类型成员的大小整除;

2、结构体每个成员相对于首地址的偏移量都是成员大小的整数倍,如果有需要,会在成员之间加上填充字节。

3、结构体的总大小为其最宽基本类型成员的整数倍,如有需要,在末尾添加填充字节。

注意:如果想让结构体按照给定字节数对齐,可以使用 #progma pack(n);

例子9:

1、对于类的sizeof 函数。空类的存储空间是一个字节,编译器会安插一个char类,标记其每一个对象。

2、类内部数据依然遵循对其原则

3、对于虚函数,因为需要一个指针指向虚函数表,因此存储空间多了一个指针,与虚函数个数无关。

4、对于虚继承,编译器会自动为其添加一个指向父类的指针,若是空类,则不再需要char型

5、static数据成员放在静态存储区域中,不占用类的存储空间。

例子10:sizeof 和strlen区别:

1、sizeof 是运算符,而strlen是函数,sizeof的返回值为size_t,在头文件中typedef为unsigned int 型。

2、sizeof 可以用类型做参数,而strlen只能是char* 且以\0结尾的字符串。

3、数字传递给sizeof不退化,但是给strlen就退化为指针 例如

charstr[]="abcd";

cout<<sizeof(str)<<"  "<<strlen(str)<<endl;

结果为 5 4

4、大部分程序在编译阶段,就已经计算好了sizeof,而strlen必须要到程序运行时在可以知道,这就是为什么sizeof 可以用来定义数组。Strlen计算的是字符串的长度,而不是占用内存的大小.例如 str[20]=”0123”;sizeof(str) 和strlen(str)就会不同

5、sizeof 如果是类型,必须要加括号,如果是变量,可以不加

4.5inline 与宏定义

内联函数用来解决宏定义的缺点。他可以像宏定义一样展开,但编译器会对他进行安全检查,不想宏定义一样只是简单的替换,因此可靠性更强。缺点是会增加代码量。

在类内部定义的函数,默认为inline,如果内部声明,外部定义,则需声明为inline

例子9:二者区别

1、宏定义是在预编译阶段展开,而inline是在编译阶段展开;

2、宏定义是简单的文本替换,而inline是在调用位置展开。

3、inline函数本质上还是函数,而宏定义不是,因此inline函数会有基本的安全检查,宏定义没有


第四章结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值