指针
- 数组指针和指针数组有什么区别?
- 函数指针和指针函数有什么区别?
- 数组名和指针的区别与联系是什么?
- 指针进行强制类型转换后与地址进行加法运算,结果是什么?
- 常量指针,指向常量的指针,指向常量的常量指针有什么区别?
- 指针和引用的异同是什么?如何相互转换?
- 野指针是什么?
- 如何避免野指针?
- 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,m–1);
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中的内容,既不可以修改指针的值,也不可以修改指针指向的值。
指针和引用的异同是什么?如何相互转换?
相同
区别
#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 的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,但是不会指向引用计数的共享内存,但是可以检测到所管理的对象是否已经被释放,从而避免非法访问。






