C++中指针问题

 一、基础知识

1、声明指针:typeName * pointName;

  • 一般初始化(3种)
int a=10;
int *p;
p=&a;
  • 也可以在声明中初始化
int a=5;
int *pt=&a;

 *运算符两边的空格是可以选的,传统C程序员使用这种格式:

int  *p 这强调的是*p是一个int类型的值。但很多C++程序员使用这种格式:
int*  p,这强调的是:int*是一种类型---指向int的指针,在哪里添加空格没有任何区别,甚至可以这样做int*p;

但是要知道下面的声明是创建一个指针P1和一个int变量p2

int* p1,p2;

今天,在练习C++书上的习题时,发现一个问题,原题如下:C++允许按值传递结构,也允许传递结构的地址。如果glitz是一个结构变量,如何按值传递它?如何传递它的地址?这两种方法有何利弊?

我们在定义子函数的形参时,为什么调用时用到的参数与定义时用到的形参不同? 我想可以用上面的论述解释。【int*  p,这强调的是:int*是一种类型---指向int的指针,在哪里添加空格没有任何区别,甚至可以这样做int*p;】

  • 用new运算符初始化:typeName * pointer_name=new typename;
int * pn=new int;

new int  告诉程序寻找一块int类型的地址,用来存储数据(类似于int a=10;)。

要注意一点:new分配的内存位于堆(heap)或自由存储区(free store)中,而常规变量分配的内存是在栈(stack)中。堆和栈的区别在于:栈(stack)由编译器自动分配释放,而堆(heap)一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。

同时在使用new时,需要用delete来释放内存。方法十分简单,在delete后加指向待释放内存的指针即可,比如:

    int* p = new int;
    delete p;

这样就可以释放掉通过new为开辟的内存了。

需要注意的是,delete只能释放由new开辟的内存。注意以下三种情况:
1、如果试图释放常规变量声明分配的内存,会报错。如:

  //delete不能释放常规变量声明分配的内存
    int i =3;
    int* p = &i;

2、如果试图释放已被delete释放的内存,会报错。如:

    //delete释放后,p指针不会被删除,而是重新指向了一处新的地址。
    int* p = new int;
    delete p;
    delete p;

3、delete可以释放空指针,这是安全的,不报错。如:

    int* p = NULL;
    delete p;
    delete p;
 

#include<iostream>
using namespace std;
int main()
{
    int a=10;
    int *p1;
    p1=&a;

    int b=1;
    int *p2=&b;


    int *p3=new int;
    *p3=1001;


    cout<<p1<<" "<<*p1<<endl;
    cout<<p2<<" "<<*p2<<endl;
    cout<<p3<<" "<<*p3<<endl;

return 0;
}
  • a与*p1的值一样,&a与p1的值一样。
  • 运行结果:程序运行了两次,每次输出的结果不一样,原因很简单,程序每次运行的时候,向系统申请内存,系统随机分配内存,就像您去宾馆开房,如果您不提前预约指定房号,每次得到的房间编号大概率不会相同。

 二、指针的应用场景

1、值传递

所谓值传递,就是函数调用时实参将数值传入形参,值传递时,如果形参发生改变,并不会影响实参。

#include <iostream>
using namespace std;

void swap(int num1, int num2)

{
     cout << "交换前:" << endl;
     cout << "num1 = " << num1 << endl;
     cout << "num2 = " << num2 << endl;
int temp = num1;
num1 = num2;
num2 = temp;
cout << "交换后:" << endl;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl; 
//return ; 当函数声明时候,不需要返回值,可以不写return
}


int main()
{
    int a = 10;
    int b = 20;
     swap(a, b);
      cout << "mian中的 a = " << a << endl;
      cout << "mian中的 b = " << b << endl;
//      system("pause");
      return 0;
}

2、地址传递:利用指针作函数参数,可以修改实参的值。

#include <iostream>
using namespace std;

//值传递
 void swap1(int a ,int b)
 {
    int temp = a;
    a = b;
    b = temp;
}


//地址传递
void swap2(int * p1, int *p2)
{
   int temp = *p1;
   *p1 = *p2;
   *p2 = temp;
 }
 
 
 int main()
{
    int a = 10;
    int b = 20;
    swap1(a, b);// 值传递不会改变实参
    swap2(&a, &b); //地址传递会改变实参
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
//    system("pause");
    return 0;
}

三、空指针(NULL指针)

本质上,定义指针时,要进行初始化,如果没有确定的值,就让它指向NULL。

定义:指针变量指向内存中编号为0的空间
用途:初始化指针变量
注意:空指针指向的内存是不可以访问的
#include <iostream>
using namespace std;

int main()
{
//指针变量p指向内存地址编号为0的空间
int * p = NULL; //访问空指针报错
                //内存编号0 ~255为系统占用内存,不允许用户访问
cout << *p << endl;
return 0;
}

四、野指针

定义:指针指向的内存已经释放,但指针的值不会清零,对野指针的操作将不可预知

#include <iostream>
using namespace std;

int main()
{
//指针变量p指向内存地址编号为0x1100的空间
int * p = (int *)0x1100; //访问野指针报错
cout << *p << endl;
return 0;
}

五、函数指针


每一个函数都有一个入口地址,函数指针是指向函数入口地址的指针变量,有了指向函数的指针变量后,就可以利用函数指针变量来调用函数。

作用:调用函数和做函数的参数

声明格式:类型说明符 (*函数名) (参数)

int (*fun)(int x,int y);

函数指针是需要把一个函数的地址赋值给它,有两种写法:

fun = &Function;
fun = Function;

取地址运算符&不是必需的,因为一个函数标识符就表示了它的地址,如果是函数调用,还必须包含一个圆括号括起来的参数表。

调用函数指针有两种方法
 

x=(*fun)();
x=fun();

两种方式均可,其中第二种看上去和普通的函数调用没啥区别,如果可以的话,建议使用第一种,因为可以清楚的指明这是通过指针的方式来调用函数。当然,也要看个人习惯,如果理解其定义,随便怎么用都行啦。

#include <iostream>
using namespace std;


int add(int x,int y)
{
    return x+y;
}
int sub(int x,int y)
{
    return x-y;
}


//函数指针
int (*fun)(int x,int y);


int main()
{
//第一种写法
fun = add;
cout<< "(*fun)(1,2) = " << (*fun)(1,2)<<" "<<fun(1,2)<<endl;
//第二种写法
fun = &sub;
cout<< "(*fun)(5,3) = " << (*fun)(5,3)<<" " << fun(5,3);
return 0;
}

六、指针函数

简单的来说,就是一个返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。返回值为地址的函数即为指针函数.
声明格式为:*类型标识符 函数名(参数表)

看看下面这个函数声明:

int fun(int x,int y);

这种函数应该都很熟悉,其实就是一个函数,然后返回值是一个 int 类型,是一个数值。
接着看下面这个函数声明:

int *fun(int x,int y);

这和上面那个函数唯一的区别就是在函数名前面多了一个*号,而这个函数就是一个指针函数。其返回值是一个 int 类型的指针,是一个地址。

指针函数的写法:

int *fun(int x,int y);
int * fun(int x,int y);
int* fun(int x,int y);

 这个写法看个人习惯,其实如果*靠近返回值类型的话可能更容易理解其定义。

示例:

#include <iostream>
using namespace std;


typedef struct _Data{
    int a;
    int b;
}Data;


//指针函数
Data* f(int a,int b)
{
    Data * data = new Data;
    data->a = b;
    data->b = a;
    return data;
}

int main()
{
    //调用指针函数
    Data * myData = f(4,5);
    cout<< "f(4,5) = " << myData->a<<" " << myData->b;
    return 0;
}

注意:在调用指针函数时,需要一个同类型的指针来接收其函数的返回值。

 参考资料:函数指针和指针函数用法和区别_luoyayun361的博客-CSDN博客_指针函数  

七、指针数组与数组指针

指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。
数组指针:首先它是一个指针,它指向一个数组。在32 位系统下永远是占4 个字节,至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称。

下面到底哪个是数组指针,哪个是指针数组呢:
A)
int *p1[10];
B)
int (*p2)[10];
每次上课问这个问题,总有弄不清楚的。这里需要明白一个符号之间的优先级问题。

“[]”的优先级比“*”要高。p1 先与“[]”结合,构成一个数组的定义,数组名为p1,int *修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含10 个指向int 类型数据的指针,即指针数组。至于p2 就更好理解了,在这里“()”的优先级比“[]”高,“*”号和p2 构成一个指针的定义,指针变量名为p2,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚p2 是一个指针,它指向一个包含10 个int 类型数据的数组,即数组指针。我们可以借助下面的图加深理解:

C语言指针数组和数组指针_C语言中文网

使用new来创建动态数组 

1、 语法:typeName pointer=new type[arraySize];

new 运算符返回第一个元素的地址,并将该地址赋给指针;

2、当使用完new分配的内存块时,应使用delete来释放他们。

语法:delete []pointer;

3、用指针访问数组中的其他元素时,只要把指针当作数组名使用即可。

#include<iostream>
using namespace std;
int main()
{
	double* p = new double[3];
	p[0] = 0.2;
	p[1] = 0.5;
	p[2] = 0.6;
	cout << "p[1] is " << p[1] << ".\n";
	p = p + 1;
	cout << "Now p[0] is " << p[0] << "and" << "p[1] is " << p[1] << ".\n";
	p = p - 1;
	delete[] p;
	return 0;
}

数组名与指针 

#include<iostream>
#include<cstring>
using namespace std;
int main()
{
	using namespace std;
	double wages[3] = { 1000.0,2000.0,3000.0 };
	short stack[3] = { 3,2,1 };
	double* pw = wages;                   //C++将数组名解释为地址
	short* ps = &stack[0];

	cout << "pw=" << pw << ", *pw= " << *pw << endl;
	cout << "ps=" << ps << ", *ps= " << *ps << endl;

	pw = pw + 1;
	ps = ps + 1;
	cout << "指针pw和ps加1" << endl;
	cout << "pw=" << pw << ", *pw= " << *pw << endl;
	cout << "ps=" << ps << ", *ps= " << *ps << endl;
}

1、C++将数组名解释为地址,且此地址为第一个数组元素的地址

wages=&wages[0]=address of first element of array

在初始化指针时我们令

double *pw=wages;

 因为此时指针与数组的地址值相同,我们可以把指针当成数组

 2、将指针变量加1后,其增加的值等于指向的类型占用的字节数

由上述程序可知,当pw增加1时,其地址增加8;ps增加1时,其地址增加2;

3、*(stacks+1)等价于stacks[1]

4、指针是变量,而数组名是常量;

 八、其他

在学习C++的过程中我们经常会用到.和::和:和->,在此整理一下这些常用符号的区别。 

    1、A.B则A为对象或者结构体;

    2、A->B则A为指针,->是成员提取,A->B是提取A中的成员B,A只能是指向类、结构、联合的指针;

    3、::是作用域运算符,A::B表示作用域A中的名称B,A可以是名字空间、类、结构;

    4、:一般用来表示继承;

初始化:包括声明与赋值-----调用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值