• C/C++知识点


    二维数组

    意义:

    1. int **Ptr 表示指向"一群"指向整数的指针的指针。
    2. int *Ptr[ 5 ] 表示指向 5 个指向整数的指针的指针,或者说Ptr有5个指向"一群"整数的指针,Ptr是这5个指针构成的数组的地址
    3. int ( *Ptr )[ 5 ] 表示指向"一群"指向 5 个整数数组的指针的指针。

    所占空间:

    1. int **Ptr 和int ( *Ptr )[ 5 ] 一样,在32位平台里,都是4字节,即一个指针。
    2. 但int *Ptr[ 5 ] 不同,它是 5 个指针,它占5 * 4 = 20 个字节的内存空间。

    用法:

    1. int **Ptr

    因为是指针的指针,需要两次内存分配才能使用其最终内容。首先,Ptr = ( int ** )new int *[ 5 ];这样分配好了以后,它和(2)的意义相同了;然后要分别对 5 个指针进行内存分配,例如:

    Ptr[ 0 ] = new int[ 20 ];

    它表示为第 0 个指针分配 20 个整数,分配好以后, Ptr[ 0 ] 为指向 20 个整数的数组。这时可以使用下标用法 Ptr[ 0 ][ 0 ] 到Ptr[ 0 ][ 19 ] 了。

    2. int *Ptr[ 5 ]

    这样定义的话,编译器已经为它分配了 5 个指针的空间,这相当
    于(1)中的第一次内存分配。根据对(1)的讨论可知,显然要对其进行一次内存分配的。否则就是"野"指针。

    3. int ( *Ptr )[ 5 ]

    它的意义是"一群"指针,每个指针都是指向一个 5 个整数的数组。如果想分配 k 个指针,这样写: Ptr = ( int ( * )[ 5 ] ) new int[ 5 * k ]。这是一次性的内存分配。分配好以后,Ptr 指向一片连续的地址空间,其中 Ptr[ 0 ] 指向第 0 个 5 个整数数组的首地址,Ptr[ 1 ] 指向第1 个 5 个整数数组的首地址。

    综上所述,可以这样理解它们:

    int ** Ptr <==> int Ptr[ x ][ y ];

    int *Ptr[ 5 ] <==> int Ptr[ 5 ][ x ];

    int ( *Ptr )[ 5 ] <==> int Ptr[ x ][ 5 ];

    这里 x 和 y 是表示若干的意思。

    sizeof

    功能:计算数据空间的字节数。

    1. 与strlen()比较;

    strlen()计算字符数组的字符数,以""为结束判断,不计算为''的数组元素。而sizeof计算数据(包括数组、变量、类型、结构体等)所占内存空间,用字节数表示。

    2. 指针与静态数组的sizeof操作;

    指针均可看为变量类型的一种。所有指针变量的sizeof操作结果均为4。

    注意:int *p; sizeof(p)=4;

    但sizeof(*p)相当于sizeof(int);     

    对于静态数组,sizeof可直接计算数组大小;

    例:int a[10];char b[]="hello";

    sizeof(a)等于4*10=40;

    sizeof(b)等于6;

    注意:数组做型参时,数组名称当作指针使用!!

    void  fun(char p[])

    {sizeof(p)等于4}    

    经典问题: 

    double* (*a)[3][6]; 

    cout<<sizeof(a)<<endl; // 4  a为指针

    cout<<sizeof(*a)<<endl; // 72  *a为一个有3*6个指针元素的数组

    cout<<sizeof(**a)<<endl; // 24  **a为数组一维的6个指针

    cout<<sizeof(***a)<<endl; // 4  ***a为一维的第一个指针

    cout<<sizeof(****a)<<endl; // 8  ****a为一个double变量

    3. 格式的写法;

    sizeof操作符,对变量或对象可以不加括号,但若是类型,须加括号。

    4. 使用sizeof时string的注意事项;

    string s="hello";

    sizeof(s)=4,sizeof(s.c_str())=4。

    注意string的大小与实现有关。

    5. union 与struct的空间计算;

    总体上遵循两个原则:

    1)  整体空间是占用空间最大的成员(的类型)所占字节数的整倍数

    2)  数据对齐原则----内存按结构成员的先后顺序排列,当排到该成员变量时,其前面已摆放的空间大小必须是该成员类型大小的整倍数,如果不够则补齐,以此向后类推。。。。。

    3)  注意:数组按照单个变量一个一个的摆放,而不是看成整体。如果成员中有自定义的类、结构体,也要注意数组问题。

    struct s1

    {

    char a[8];

    };

     

    struct s2

    {

    double d;

    };

     

    struct s3

    {

    s1 s;

    char a;

    };

     

    struct s4

    {

    s2 s;

    char a; 

    };

    struct s5

    {

    s3 s;

    char a;

    };

    cout<<sizeof(s1)<<endl; // 8

    cout<<sizeof(s2)<<endl; // 8

    cout<<sizeof(s3)<<endl; // 9

    cout<<sizeof(s4)<<endl; // 16;

    cout<<sizeof(s5)<<endl; // 10;

     

    define

    defineconst

    1. 编译器处理方式不同。

    define宏是在预处理阶段展开。

    const常量是编译运行阶段使用。

    2. 类型和安全检查不同。

    define宏没有类型,不做任何类型检查,仅仅是展开。只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误

    const常量有具体的类型,在编译阶段会执行类型检查。有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。

    3. 存储方式不同。

    define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。

    const常量会在内存中分配(可以是堆中也可以是栈中)。

    4. const  可以节省空间,避免不必要的内存分配。

    例如:  

    #define PI 3.14159 //常量宏  

    const doulbe Pi=3.14159; //此时并未将Pi放入ROM中 ...... 

    double i=Pi; //此时为Pi分配内存,以后不再分配! 

    double I=PI; //编译期间进行宏替换,分配内存  

    double j=Pi; //没有内存分配 

    double J=PI; //再进行宏替换,又一次分配内存! 

    const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而 #define定义的常量在内存中有若干个拷贝。 

    5. 提高了效率。

    编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

    definetypedef

    #define是C中定义的语法,typedef是C++中定义的语法,二者在C++中可以通用,但#define成了预编译指令,typedef当成语句处理。typedef和define都可以用来给对象取一个别名,但是两者却有着很大不同。

    1. 首先,二者执行时间不同。

    关键字typedef在编译阶段有效,由于是在编译阶段,因此typedef有类型检查的功能。

    define则是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机械的字符串替换,而不进行任何检查。

    #define用法例子: 

    #define f(x) x*x  

    main( )  

    {  

    int a=6,b=2,c;  

    c=f(a) / f(b);  

    printf("%d  ",c);  

    程序的输出结果是: 36,根本原因就在于#define只是简单的字符串替换,应当加个括号“(X*X)”。

    2. 功能不同。

    typedef用来定义类型的别名,这些类型不只包含内部类型(int,char等),还包括自定义类型(如struct),可以起到使类型易于记忆的功能。 

    如:

    typedef int (*PF) (const char *, const char *); 

    定义一个指向函数的指针的数据类型PF,其中函数返回值为int,参数为const char *。

    typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以i获得最高的精度:

    typedef long double REAL; 

    在不支持 long double 的机器上,该 typedef 看起来会是下面这样:

    typedef double REAL; 

    并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样:

    typedef float REAL; 

    #define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。

    3. 作用域不同。

    #define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。而typedef有自己的作用域。

    void fun()   

    {   

    #define A int   

    }  

    void gun()   

    {   

    //在这里也可以使用A,因为宏替换没有作用域,   

    //但如果上面用的是typedef,那这里就不能用A ,不过一般不在函数内使用typedef  

    4. 对指针的操作。

    二者修饰指针类型时,作用不同。

    typedef int * pint;  

    #define PINT int *  

    const pint p;//p不可更改,p指向的内容可以更改,相当于 int * const p;  

    const PINT p;//p可以更改,p指向的内容不能更改,相当于 const int *p;或 int const *p;  

    pint s1, s2; //s1和s2都是int型指针  

    PINT s3, s4; //相当于int * s3,s4;只有一个是指针。 

    其实,typedef和define末尾的标号也是不一样的,希望大家不要忽略这一点。通过本文的分析,相信你已经了解了这两者之间的区别。掌握了区别之后,运用起来会更加的灵活。

    static

    1. 第一个作用:隐藏;

    当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。为理解这句话,我举例来说明。我们要同时编译两个源文件,一个是a.c,另一个是main.c。

    下面是a.c的内容

    #include<cstdio>增加这条语句

    char a = ‘A‘; // global variable

    void msg()

    {

         printf("Hello ");

    }

    你可能会问:为什么在a.c中定义的全局变量a和函数msg能在main.c中使用?前面说过,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。

    如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。Static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用。

    2. static的第二个作用是保持变量内容的持久;

    存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。

    3. static的第三个作用是默认初始化为0;

    其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。

    4. 在C++类中的独特作用;

    C++类中被static修饰的成员变量是类变量,被static修饰的成员函数是类函数。

    常量指针和指针常量

    常量指针:指向常量的指针,指针所指向的内容不能改变,但是可以改变其所指向的地址。

    const 类型 *指针常量名=&变量名;或者 类型 const *指针常量名=&变量名;

    如:const int k=5,t=8;

    const int *p=&k;

    *p=12;(错误) //常量指针指向的内容是常量,不可以赋值

    p=&t;(正确) //常量指针的地址可以修改

    指针常量:指针常量是指针所指向的位置不能改变,即指针本身是一个常量。但是指针常量可以通过间接引用修改内存中的数据。

    定义指针常量的语句格式为:

    指针类型 *const 指针常量名=&变量名;

    例如: int a=5,b=7;

    int *const p=&a;

    *p=b;(正确)//指针常量指向的内容可以修改

    p=&b(错误)//指针常量是指针所指向的位置不能改变

    mallocnewfreedelete

    1. new 是c++中的操作符,malloc是c中的一个函数。

    2. new 不止是分配内存,而且会调用类的构造函数,同理delete会调用类的析构函数,而malloc则只分配内存,不会进行初始化类成员的工作,同样free也不会调用析构函数。

    3. new出来的指针是直接带类型信息的。而malloc返回的都是void指针。

  • 相关阅读:
    【Java学习笔记】java.lang包学习
    winform的md5加密
    地铁线路图的设计与实现
    asp.net的jQuery 表格展开伸缩
    关于批量数据更新的问题(C#高性能)
    JQuery用户名无刷新验证
    Linq分页
    用jQuery写的好的动态显示本机时间的代码
    asp.net防止页面刷新弹出“需要重新发送您以前提交的信息”
    aspx中的html代码调用CS文件中的方法
  • 原文地址:https://www.cnblogs.com/yanenquan/p/3705535.html
Copyright © 2020-2023  润新知