C++/C学习笔记(五)

C++/C学习笔记(五)

1.指针

1)指针类型及支持的运算

指针是变量,在二进制层面,指针的值就是内存单元的地址,而变量又是引用内存单元的值的别名,因此在语言层面指针的值就是变量的地址。

指针的类型为一个类型名和字符“*”的组合。但编译器解释的时候,“*”是和其后的变量名结合的。例如:int* a,b,c; 编译器会理解为:int *a,b,c; 即只有aint类型的指针,而bc仍然是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}; //元素个数为5sizeof(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()

原文地址:https://www.cnblogs.com/yingying0907/p/2624903.html