• 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()

  • 相关阅读:
    ICEY修改实现ICEY锁血,修改data文件实现主之名关卡重现,顺便改金币
    gg修改器修改聚爆
    MOUSE_OVER 与 ROLL_OVER
    Tween 没有完毕 就停止
    如何将FLex AIR运行环境与AIR程序一起打包(转http://www.awsws.com/?p=94)
    做项目如做人
    flex 打开外部的swf 并调用其方法
    air 零散知识
    贝塞尔曲线的绘制
    air 读取sqlite的Date类型 解决方案
  • 原文地址:https://www.cnblogs.com/yingying0907/p/2624903.html
Copyright © 2020-2023  润新知