欢迎光临
我们一直在努力

嵌入式知识点学习篇三(C\\C++)

指针

  • 数组指针和指针数组有什么区别?
  • 函数指针和指针函数有什么区别?
  • 数组名和指针的区别与联系是什么?
  • 指针进行强制类型转换后与地址进行加法运算,结果是什么?
  • 常量指针,指向常量的指针,指向常量的常量指针有什么区别?
  • 指针和引用的异同是什么?如何相互转换?
  • 野指针是什么?
  • 如何避免野指针?
  • C++中的智能指针是什么?
  • 智能指针的内存泄漏如何解决?

数组指针和指针数组有什么区别?

数组指针就是指向数组的指针,它表示的是一个指针,这个指针指向的是一个数组,它的重点是指针。 例如, int(*pa)[8] 声明了一个指针,该指针指向了一个有8个int型元素的数组。下面给出一个数组指针的示例。

#include <stdio.h>
#include <stdlib.h>
void main()
{
int b[12]={1,2,3,4,5,6,7,8,9,10,11,12};
int (*p)[4];
p = b;
printf("%d\\n"**(++p);
}

程序的输出结果为 5。 上例中,p是一个数组指针,它指向一个包含有4个int类型数组的指针,刚开始p被初始化为指向数组b的首地址,++p相当于把p所指向的地址向后移动4个int所占用的空间,此时p指向数组{5,6,7,8},语句*(++p):表示的是这个数组中第一个元素的地址(可以理解p为指向二维数组的指针,{1,2,3,4},{5,6,7,8},{9,10,11,12}。p指向的就是{1,2,3,4}的地址,*p 就是指向元素,{1,2,3,4},**p指向的就是1),语句**(++p)会输出这个数组的第一个元素5。

指针数组表示的是一个数组,而数组中的元素是指针。下面给出另外一个指针数组的示例。

#include <stdio.h>
int main()
{
int i;
int *p[4];
int a[4]={1,2,3,4};
p[0] = &a[0];
p[1] = &a[1];
p[2] = &a[2];
p[3] = &a[3];
for(i=0;i<4;i++)
printf("%d",*p[i]);
printf("\\n");
return 0;
}

程序的输出结果为1234。

函数指针和指针函数有什么区别?

  • 函数指针
  • 如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址。既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针。

    int(*p)(int, int);

    这个语句就定义了一个指向函数的指针变量 p。首先它是一个指针变量,所以要有一个“*”,即 (*p) ;其次前面的 int 表示这个指针变量可以指向返回值类型为 int 型的函数;后面括号中的两个 int 表示这个指针变量可以指向有两个参数且都是 int 型的函数。所以合起来这个语句的意思就是:定义了一个指针变量 p,该指针变量可以指向返回值类型为 int 型,且有两个整型参数的函数。p 的类型为 int(*)(int,int) 。

    我们看到,函数指针的定义就是将“函数声明”中的“函数名”改成“(指针变量名)”。但是这里需要注意的是:“(指针变量名)”两端的括号不能省略,括号改变了运算符的优先级。如果省略了括号,就不是定义函数指针而是一个函数声明了,即声明了一个返回值类型为指针型的函数。

    最后需要注意的是,指向函数的指针变量没有 ++ 和 – 运算。

    # include <stdio.h>

    int Max(int, int); //函数声明

    int main(void)
    {
    int(*p)(int, int); //定义一个函数指针
    int a, b, c;
    p = Max; //把函数Max赋给指针变量p, 使p指向Max函数
    printf("please enter a and b:");
    scanf("%d%d", &a, &b);
    c = (*p)(a, b); //通过函数指针调用Max函数
    printf("a = %d\\nb = %d\\nmax = %d\\n", a, b, c);
    return 0;
    }

    int Max(int x, int y) //定义Max函数
    {
    int z;
    if (x > y)
    {
    z = x;
    }
    else
    {
    z = y;
    }
    return z;
    }

  • 指针函数
  • 首先它是一个函数,只不过这个函数的返回值是一个地址值。函数返回值必须用同类型的指针变量来接受,也就是说,指针函数一定有“函数返回值”,而且,在主调函数中,函数返回值必须赋给同类型的指针变量。

    类型名 *函数名(函数参数列表);

    其中,后缀运算符括号“()”表示这是一个函数,其前缀运算符星号“*”表示此函数为指针型函数,其函数值为指针,即它带回来的值的类型为指针,当调用这个函数后,将得到一个“指向返回值为…的指针(地址),“类型名”表示函数返回的指针指向的类型”。

    “(函数参数列表)”中的括号为函数调用运算符,在调用语句中,即使函数不带参数,其参数表的一对括号也不能省略。其示例如下:

    int *pfun(int, int);

    由于“*”的优先级低于“()”的优先级,因而pfun首先和后面的“()”结合,也就意味着,pfun是一个函数。 即:

    int *(pfun(int, int));

    接着再和前面的“*”结合,说明这个函数的返回值是一个指针。由于前面还有一个int,也就是说,pfun是一个返回值为整型指针的函数。

    #include <stdio.h>

    float *find(float(*pionter)[4],int n);//函数声明

    int main(void)
    {
    static float score[][4]={{60,70,80,90},{56,89,34,45},{34,23,56,45}};
    float *p;
    int i,m;
    printf("Enter the number to be found:");
    scanf("%d",&m);
    printf("the score of NO.%d are:\\n",m);
    p=find(score,m1);
    for(i=0;i<4;i++)
    printf("%5.2f\\t",*(p+i));
    return 0;
    }

    float *find(float(*pionter)[4],int n)/*定义指针函数*/
    {
    float *pt;
    pt=*(pionter+n);
    return(pt);
    }

    共有三个学生的成绩,函数find()被定义为指针函数,其形参pointer是指针指向包含4个元素的一维数组的指针变量。pointer+n指向score的第n+1行。*(pointer+1)指向第一行的第0个元素。pt是一个指针变量,它指向浮点型变量。main()函数中调用find()函数,将score数组的首地址传给pointer。

    数组名和指针的区别与联系是什么?

    1.数据保存方面

    指针保存的是地址(保存目标数据地址,自身地址由编译器分配),内存访问偏移量为4个字节,无论其中保存的是何种数据均已地址类型进行解析。

    数组保存的数据。数组名表示的是第一个元素的地址,内存偏移量是保存数据类型的内存偏移量;只有对数组名取地址(&数组名)时数组名才表示整个数组,内存偏移量是整个数组的大小(sizeof(数组名))。

    2.数据访问方面

    指针对数据的访问方式是间接访问,需要用到解引用符号(*数组名)。

    数组对数据的访问则是直接访问,可通过下标访问或数组名+元素偏移量的方式

    3.使用环境

    指针多用于动态数据结构(如链表,等等)和动态内存开辟。

    数组多用于存储固定个数且类型统一的数据结构(如线性表等等)和隐式分配。

    指针进行强制类型转换后与地址进行加法运算,结果是什么?

    假设在32位机器上,在对齐为4的情况下,sizeof(long)的结果为4字节,sizeof(char*)的结果为4字节,sizeof(short int)的结果与sizeof(short)的结果都为2字节, sizeof(char)的结果为1字节, sizeof(int)的结果为4字节,由于32位机器上是4字节对齐,以如下结构体为例:

    struct BBB
    {
    long num;
    char *name;
    short int data;
    char ha;
    short ba[5];
    }*p;

    当 p=0x100000; 则 p+0×200=? (ulong)p+0x200=? (char*)p+0x200=?
    其实,在32位机器下, sizeof(struct BBB)=sizeof(*p)=4+4+2+2+1+3/*补齐*/+2*5+2/*补齐*/=24字节 ,
    而p=0x100000 ,那么p+0x200=0x1000000+0x200*24 指针加法,
    加出来的是指针所指类型的字节长度的整倍数,就是p偏移sizeof(p)*0x200。
    (ulong)p+0x200=0x10000010+0x200 经过ulong后,已经不再是指针加法,而变成一个数值加法了。
    (char*)p+0x200=0x1000000+0×200*sizeof(char) 结果类型是char*。

    常量指针,指向常量的指针,指向常量的常量指针有什么区别?

    1.指针常量

    int * const p;

    先看const再看 * ,p是一个常量类型的指针,不能修改这个指针的指向,但是这个指针所指向的地址上存储的值可以修改。

    2.常量指针

    const int *p;
    int const *p;

    先看*再看const,定义一个指针指向一个常量,不能通过指针来修改这个指针指向的值。

    3.指向常量的常量指针

    const int *const p;

    对于“指向常量的常量指针”,就必须同时满足上述1和2中的内容,既不可以修改指针的值,也不可以修改指针指向的值。

    指针和引用的异同是什么?如何相互转换?

    相同

  • 都是地址的概念,指针指向某一内存、它的内容是所指内存的地址;引用则是某块内存的别名。
  • 从内存分配上看:两者都占内存,程序为指针会分配内存,一般是4个字节;而引用的本质是指针常量,指向对象不能变,但指向对象的值可以变。两者都是地址概念,所以本身都会占用内存。
  • 区别

  • 指针是实体,而引用是别名。
  • 指针和引用的自增(++)运算符意义不同,指针是对内存地址自增,而引用是对值的自增。
  • 引用使用时无需解引用(*),指针需要解引用。
  • 引用只能在定义时被初始化一次,之后不可变;指针可变。
  • 引用不能为空,指针可以为空。
  • “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小,在32位系统指针变量一般占用4字节内存。
  • #include "stdio.h"

    int main()
    {
    int x = 5;
    int *p = &x;
    int &q = x;
    printf("%d %d\\n",*p,sizeof(p));
    printf("%d %d\\n",q,sizeof(q));
    return 0;
    }

    //结果
    5 8
    5 4

    由结果可知,引用使用时无需解引用(*),指针需要解引用;我用的是64位操作系统,“sizeof 指针”得到的是指针本身的大小,及8个字节。而“sizeof 引用”得到的是的对象本身的大小及int的大小,4个字节。

    转换

  • 指针转引用:把指针用*就可以转换成对象,可以用在引用参数当中。
  • 引用转指针:把引用类型的对象用&取地址就获得指针了。
  • int a = 5;
    int *p = &a;
    void fun(int &x){}//此时调用fun可使用 : fun(*p);
    //p是指针,加个*号后可以转换成该指针指向的对象,此时fun的形参是一个引用值,
    //p指针指向的对象会转换成引用X。

    野指针是什么?

    1.野指针是指向不可用内存的指针,当指针被创建时,指针不可能自动指向NULL,这时,默认值是随机的,此时的指针成为野指针。

    2.当指针被free或delete释放掉时,如果没有把指针设置为NULL,则会产生野指针,因为释放掉的仅仅是指针指向的内存,并没有把指针本身释放掉。

    3.第三个造成野指针的原因是指针操作超越了变量的作用范围。

    如何避免野指针?

    1.对指针进行初始化。

    //将指针初始化为NULL。
    char * p = NULL;
    //用malloc分配内存
    char * p = (char * )malloc(sizeof(char));
    //用已有合法的可访问的内存地址对指针初始化
    char num[ 30] = {0};
    char *p = num;

    2.指针用完后释放内存,将指针赋NULL。

    delete(p);
    p = NULL;

    注:malloc函数分配完内存后需注意:

    a. 检查是否分配成功(若分配成功,返回内存的首地址;分配不成功,返回NULL。可以通过if语句来判断)。

    b. 清空内存中的数据(malloc分配的空间里可能存在垃圾值,用memset或bzero 函数清空内存)。

    //s是 需要置零的空间的起始地址; n是 要置零的数据字节个数。
    void bzero(void *s, int n);
    // 如果要清空空间的首地址为p,value为值,size为字节数。
    void memset(void *start, int value, int size);

    C++中的智能指针是什么?

    智能指针是一个类,用来存储指针(指向动态分配对象的指针)。

    C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。程序员自己管理堆内存可以提高了程序的效率,但是整体来说堆内存的管理是麻烦的,C++11中引入了智能指针的概念,方便管理堆内存。使用普通指针,容易造成堆内存泄露(忘记释放),二次释放,程序发生异常时内存泄露等问题等,使用智能指针能更好的管理堆内存。

    智能指针的内存泄漏如何解决?

    为了解决循环引用导致的内存泄漏,引入了弱指针 weak_ptr , weak_ptr 的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,但是不会指向引用计数的共享内存,但是可以检测到所管理的对象是否已经被释放,从而避免非法访问。

    赞(0)
    未经允许不得转载:171主机测评 » 嵌入式知识点学习篇三(C\\C++)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址