C++/C学习笔记(五)
1.指针
(1)指针类型及支持的运算
指针是变量,在二进制层面,指针的值就是内存单元的地址,而变量又是引用内存单元的值的别名,因此在语言层面指针的值就是变量的地址。
指针的类型为一个类型名和字符“*”的组合。但编译器解释的时候,“*”是和其后的变量名结合的。例如:int* a,b,c; 编译器会理解为:int *a,b,c; 即只有a是int类型的指针,而b和c仍然是int类型的变量。
不管指针变量是全局的还是局部的、静态的还是非静态的,应当在声明它的同时初始化它,要么赋予它一个有效的地址,要么赋予它NULL,否则为野指针,运行程序时会出现错误。
指针加减一个正整数i,其含义并不是在其值上直接加减i,还要包含指针所指对象的字节数信息,例如:
int *pInt=new int[100];
pInt+=50; //编译器改写为pInt+=50*sizeof(int);
pInt++; //pInt+=1;
void*类型的指针不能参与算术运算,只能进行赋值、比较和sizeof()操作。
当把“&”用于指针时,就是在提取指针变量的地址,不能在一个指针面前连续使用多个“&”,例如&&p,因为&p已经不是一个变量了,不能把“&”单独用于一个非变量的东西。不能对字面常量使用“&”来取其地址,因为字面常量保存在符号表中,它们没有地址概念。
当把“*”用于指针时,就是在提取指针所指向的变量。因此在将“*”用于指针时一定要确保该指针指向一个有效的合法的变量,不能对void*类型指针使用“*”来取其所指向的变量。
(2)指针传递
可以把函数的参数或返回值类型声明为指针类型,此时函数接受的参数或返回值就是地址,而不是指针所指的内存单元的值。例
void f(int *p)
{
cout<<p<<endl; //输出局部指针变量p的值
cout<<&p<<endl; //输出p在堆栈上的地址
cout<<*p<<endl; //p所指内存单元的值为0
*p=100; //修改p所指内存单元(即iCount)的值
cout<<*p<<endl; //100
p=NULL;
}
Int iCount=0;
f(&iCount);
cout<<iCount<<endl; //100
假设iCount的地址为0x0024FF42,则在调用语句f(&iCount)中传入的就是这个地址值,至于p的地址则是堆栈地址。
我们可以把一个对象的地址在整个程序中的各个函数之间传来传去,只要保证每次使用它都指向合法的和有效的内存单元,这就是指针容易被错误使用的原因:在指针传递是我过程中,可能把它指向的内存释放掉了却还继续使用该指针,或者传递了一个局部对象的地址,而该对象在函数结束时就销毁了。
2.数组
(1)数组本质
当使用“[]”来引用数组元素的时候,编译器必须把它转换为同类型的指针表示形式,然后再进行编译。例如:
a[3]=100; //转换为*(a+3)=100;
count<<a[3]<<endl; //转换为count<<*(a+3)<<endl;
因为C++/C数组本身不会保存下标值与元素对象之间的对应关系,因此无法直接通过下标索引来定位真正的数组元素对象。
数组名字本身就是一个指针,是一个指针常量,即a等价于int8 const a,因此不能试图修改数组名的值。数组名的值就是数组第一个元素的内存单元首地址,即a=&a[0]。任何两个数组之间不能直接赋值(=),即使是同类型数组,要用内存拷贝函数memcpy().
(2)数组初始化,创建,删除
int b[100]; //sizeof(b)=400 bytes,未初始化
int c[]={1,2,3,4,5}; //元素个数为5,sizeof(4)=20 bytes,初始化
Int d[5]={1,2,3,4,5.6.7}; //错误!初始值越界
Int e[10]={5,6,7,8,9}; //元素个数为10,指定了前5个元素的初始值, //剩下的元素全部自动初始化为0
Int f[10]={5,,12,,2}; //错误!不能跳过中间的某些元素
创建一维数组:char *p=new char[1025]; //分配空间
创建多维数组:int(*p5)[5][7]=new int[20][5][7] //指向数组的指针
释放数组:delete []p; //删除p
delete []p5; //删除p5
3.函数指针
函数指针就是指向函数体的指针,其值是函数体的首地址。
通过函数指针来调用函数:
A.直接把函数指针变量当做函数名,然后填入参数:
int len=fp_1("I am happy");
B.将函数指针的反引用作为函数名,然后填入参数:
double d=(*fp_3)(10.25);
4.类成员函数
类的成员函数有4种类型: inline, virtual, static, normal 示例:
class CTest{
public:
void f(void) {cout<<"CTest::f()"<<endl;} //普通成员函数
static void g(void) {cout<<"CTest::g()"<<endl;} //静态成员函数
virtual void h(void){cout<<"CTest::h()"<<endl;} //虚拟成员函数
//...
private: //...
};
void main()
{
typedef void (*GFPtr)(void); //定义一个全局函数指针类型
GFPtr fp=CTest::g; //取静态成员函数地址的方法和取一个 //全局函数的地址相似
fp(); //通过函数指针调用类静态成员函数
typedef void (CTest::*MemFuncPtr)(void); //声明类成员函数指针类型
MemFuncPtr mfp_1=&CTest::f; //声明成员函数指针变量并初始化
MemFuncPtr mfp_2=&CTest::h; //注意获取成员函数地址的方法
CTest theObj;
(theObj.*mfp_1)(); //使用对象和成员函数指针调用成员函数
(theObj.*mfp_2)();
CTest *pTest=&theObj;
(pTest->*mfp_1)(); //使用对象指针和成员函数指针调用成员函数
(pTest->*mfp_2)();
}
输出结果:
CTest::g()
CTest::f()
CTest::h()
CTest::f()
CTest::h()