1.#ifdef、#else、#endif、#ifndef 指示符
#include<stdio.h>
#define DEBUG //定义DEBUG预处理常量
int main()
{
#ifdef DEBUG //判断预处理常量是否被定义
printf("定义了名为DEBUG的预处理常量");
#else
printf("没有定义名为DEBUG的预处理常量");
#endif
return 0;
}
#ifndef与#ifdef作用相同,即判断预处理常量是否没被定义
2.宏定义,良好的宏定义能提高代码的可移植性和可读性。在宏中需要把参数小心的用括号括起来,因为宏是简
单的文本替换,如果不注意容易引起歧义。如下:
#define SQR(x) (x*x)
int main()
{
int a,b = 3;
a = SQR(b 2);
printf("a = %d\n",a); //输出为:11
return 0;
}
本例中原本想求5即(b 2)的平方,由于宏定义展开是在预处理时期就是编译之前。此时b并没有被赋值,b只是一个符号,因此展开为:a = (b 2*b 2),于是输出为11。为了达到本意需要将SQR(x)定义如下:
#define SQR(x) ((x)*(x))
3.用#define实现求最大最小值(注意:参数都用括号括起来)
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIX(x,y) ((x)<(y)?(x):(y))
4.用#define得到一个字的高位和低位字节
#define WORD_LO(xxx) ((byte)((word)(xxx)&0xff))
#define WORD_HI(xxx) ((byte)((word)(xxx)>>8))
5.用#define得到数组元素个数
#define ARR_SIZE(a) (sizeof((a))/sizeof((a[0])))
总结以上2、3为宏定义如何使用及注意事项,4描述了宏定义与位运算的使用,5描述了宏定义与sizeof的使用。
6.const是C的一个关键字,限定变量不允许被改变,增强程序的健壮型。
const关键字在指针声明时的作用,const位于*的左侧,是修饰指针所指向的变量,即为指针常量;const位
于*号的右侧,是修饰指针本身,即为常量指针。如下:(法则:用const修饰的对象它是一个常量,*号右侧
修饰本身)
char* const p1; //*号右侧,const修饰p1即p1为常量,p1又是指针,所以p1为指针常量。假设p1指向的变
量为0x00005895,此值为一个地址值,p1永远指向0x00005895本身不能再修改,但是0x00005895地址中的
内容可以改变。
char const* p2; //*号左侧,const修饰p2指向的变量,假设p2指向的地址为0x00005800,那么const修饰
的为0x00005800地址中的内容,所以0x00005800中的内容不能再修改,但是p2可以再指向其他的地址如
0x00005700,这里p2就是一个常量指针,它指向的是一个常量。
const char* p3; //常量指针
const char* const p4; //p4是常量,它指向的内容也是常量。但p4依然为常量指针
********************************
#include<stdio.h>
int main()
{
const int x = 1; //定义常量x
int b = 10; int c = 20;
const int* a1 = &b; //定义a1为常量指针
int* const a2 = &b; //定义a2为指针常量
const int* const a3 = &b;
x = 2; //错误,x为整型常量,值不能改变
a1 = &c; //可以,*号左侧,修饰的为指针指向的变量,即变量为常量。而指针可以指向其他变量。
*a1 = 1; //错误,原因同上
a2 = &c; //错误,*号右侧,修饰的为指针,即指针为常量不能改变,它只能始终如一的指向b
*a2 = 1; //可以,*a2表示指针指向的内容
a3 = &c; //错误,const出现在*号左右两侧,表示指针不能改变其指向的内容也不能改变
*a3 = 1; //错误,同上
}
***************************************************
7.const与#define区别
(1)#define存在于程序的代码段,const常量存在于程序的数据段。
#define只用来做文本替换,在程序进行编译时,编译器先进行宏替换再进行编译,因此#define常量生命
周期至于编译期;const常量在堆栈中分配了空间,它在程序中确确实实地存在着并可以被调用、传递。
(2)#define宏常量没有数据类型,const常量有数据类型并能进行类型安全检查。
8.C 中const有什么作用?
(1)用于定义常量。如:const int x = 1;
(2)用于修饰函数参数,如:void fun(int const &n);
(3)用于修饰函数的返回值,如:const char *GetChar(void){};
(4)用于修饰类的成员函数,如:int GetCount(void) const;任何不需要修改数据成员的函数都应该用
const修饰。
9.static代表静态,它可以作用于变量和函数。
静态局部变量,即在局部变量前加上static关键字;
静态函数,即在函数返回类型前加上static关键字;
10.C 中static关键字3个明显的作用为:
(1)在函数体内声明静态局部变量;
(2)在模块内声明本地的全局变量;
(3)在模块内声明静态函数;
11.静态全局变量与普通全局变量区别,普通全局变量前面加上static关键字就构成了静态的全局变量。普通全
集变量作用域是整个源程序,而静态全局变量作用域是只在定义该变量的源文件内有效。静态全局变量只被初
始化一次,不能在其他文件中被引用。
12.静态局部变量与普通局部变量区别,生存期不同、作用域不同。静态局部变量只被初始化一次,下一次依据上
一次结果的值。
13.静态函数与普通函数区别,静态函数在内存中只有一份,普通函数在每个被调用中维持一份复制品。
class widget
{
public:
widget()
{
count ;
}
~widget()
{
count--;
}
//一个静态方法
static int num()
{
return count;
}
private:
//一个静态成员
static int count;
}
//静态成员、方法不属于类的实例,属于类本身并在所以类的实例间共享。
//调用方法为:类名加上操作符"::"来引用
int widget::count = 0;
int main()
{
widget x,y;
cout<<"The Num.is"<<widget::num()<<endl;
if(widget:num()>=1)
{
wodget x,y,z;
cout<<"The Num.is"<<widget::num()<<endl;
}
widget z;
cout<<"The Num.is"<<widget::num()<<endl;
return 0;
}
1.sizeof操作符
sizeof是单目操作符而不是函数。操作数可以是一个表达式或者括在括号内的类型名,返回操作数占用存储空
间的字节数。
2.sizeof使用方法
(1)用于数据类型(必须用括号括住):sizeof(数据类型);如 sizeof(int)
(2)用于变量:sizeof(变量名) 或者 sizeof 变量名;
(3)不能用户函数类型、void类型等
3.sizeof用途
(1)与存储分配和I/O系统的例程进行通信。
如,用malloc()申请一块长度为length的整型数组的内存,代码如下:
int *p = (int*)malloc(sizeof(int) * length);
(2)计算数组中元素的个数,如,char str[] = "sdfefs"; int num = sizeof(str)/sizeof(str[0]);
4.sizeof与strlen有哪些区别
(1)sizeof是运算符,strlen是C语言的库函数;
(2)功能上,sizeof是求指定变量或变量类型所占用内存的大小;strlen用来求以'\0'结尾的字符串长度
(3)sizeof可以用类型做参数,strlen只能用char*做参数;
(4)计算指针指向的字符串的长度一定要使用strlen。
(5)使用示例如下
对于静态数组处理:
char str[20]="0123456789";
strlen(str)=10; //表示数组中字符串的长度
sizeof(str)=20; //表示数组变量分配的长度
对于指针处理:
char *str="0123456789";
strlen(str)=10; //表示字符串的长度
sizeof(str)=4; //表示指针变量的所占内存大小
sizeof(*str)=1; //表示'0'这个字符变量的所占内存大小
5.关于sizeof的计算
(1)计算普通变量所占内存
char str[] = "hello"; char *p = str; int = 10;
sizeof(str) = ?; //str表示数组,表示数组占用内存总空间,sizeof(str) = strlen(str) + 1
sizeof(p) = ?;
sizeof(n) = ?;
void Fun(char str[100])
{
sizeof(str) = ?;//数组是"传址"的,函数Fun在栈上分配4个字节的指针指向数组。
}
void *p = malloc(100); //在堆内存上指定100个字节
sizeof(p) = ?;
(2)计算类对象或者结构体所占内存
class A
{
public:
int i; int ii; int iii; char ch; char chr; short j;
}
struct S
{
char c1; int i; char c2;
}
sizeof(A) = 3*4 + 2*1 + 2 = 15; 但实际上sizeof(A) = 16;
sizeof(S) = 1 + 4 + 1 = 6; 但实际上sizeof(S) = 12;
三个标准:
a.类或者结构体变量的首地址能够被其最宽的基本类型成员的大小所整除;
b.类或者结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍;
c.类或者结构体总大小为其最宽的基本类型成员的整数倍;
(3)计算含有虚函数的类对象所占内存
普通函数不占用内存,只要虚函数会占用一个指针大小的内存,原因是系统用一个指针维护这个类的虚
函数表,并且无论有多少个虚函数都是这一个指针。
(4)计算虚拟继承的类对象所占内存
a.空类,编译器会安插一个char空类标记它的每一个对象,因此占1个字节的内存;
b.虚拟继承,编译器为该类安插一个指向父类的指针,如果此时类为空编译器也不会安插一个char了,
因此占用4个字节的内存,一个虚拟继承安插一个指针,多个虚拟继承则安插多个指向父类的指针。
c.含有静态成员变量的类,静态成员的空间不在类的实例中,而是像全局变量一样在静态存储区,计算
类对象占用内存大小时不用计算它。
(5)计算联合体所占内存
联合体的大小取决于它所有的成员中占用空间最大的一个成员的大小。
6.#pragma pack作用
#pragma pack(4) 表示4字节对齐。