指针的基础知识总结

"本文详细介绍了C++中的指针,包括定义与赋值、运算符"&"和"*"的用法、指针与数组、二维数组、函数参数、字符串、函数指针、返回指针的函数、指针数组、指向指针的指针以及const指针的概念。通过实例展示了如何使用指针进行动态存储分配、数组排序和字符串操作。此外,还探讨了引用作为函数参数实现值交换的功能。"
摘要由CSDN通过智能技术生成

(复习计划)--------C++程序设计(第三版)【谭浩强老师著】

第六章

目录

1.定义指针变量

2.对“&”和“*”运算符的说明

3.用指针变量做函数参数 

4.数组与指针

4.1通过指针访问一维数组

4.2用指针访问二维数组

5.用指针作函数形参接收数组地址

6.字符串与指针

7.函数与指针

8.返回指针的函数

9.指针数组

10.指向指针的指针 


在程序中使用指针有许多优点,例如:提高程序效率;实现动态存储分配;可以通过调用函数,得到多个可改变的值etc.

接下来就是干货 ^ ^

  • “ & ”  取地址运算符&i 表示变量 i 的地址)
  • “ * ” 表示 指向 *a 表示 a 所指向的变量)
  • 变量的地址称为该变量的指针
  • 用来存放变量地址的变量是指针变量

下面两行语句作用相同

a=1;
*a=1; 
//将 1 赋给指针变量 a 所指向的值

1.定义指针变量

//定义
int *point1 ; //指针变量名为 point
char *point2 ;
float *point3 ;

//赋值
point1 = &i; //将 i 的地址存放于指针变量point1中,i 为整型变量
point2 = &a; //a 为字符型变量
point3 = &b; //b 为浮点型变量

注意:

  •  定义指针变量时必须指定基类型 (不同类型的数据在计算机中存储方式和占用的字节数是不相同的)
  • 定义指针变量时要有 * 标识  (指向整型数据的指针类型表示为 “ int * ”,简称“ int 指针 ”)
  • 不能用整数直接给指针变量赋值 
int *a = 2000;
//这是错误的
  • 一个指针变量只能指向同一类型变量
  • 说明变量类型时要完整 (a是指向整型数据的指针变量 或 int* 型变量)

 让我们现在来使用一下指针变量

#include <iostream>
using namespace std;
int main()
{
    int a=2,b=3;
    int *point1,*point2;

    point1=&a; //把 a 的地址赋给了指针变量point1
    point2=&b; //把 b 的地址赋给point2
    
    cout<<a<<" "<<b<<endl;
    cout<<*point1<<" "<<*point2;

    return 0;
}


运行结果应为:
2 3
2 3

不难发现程序第11,12行,两个cout语句作用相同

2.对“&”和“*”运算符的说明

“ & ”与“ * ”的优先级别相同,同时出现时按照“自右向左”的方向结合

int *point1,*point2;
point1 = &a;

&*point1;
//*point1 就等于a, 所以&*point就相当于 &a

point2 = &*point1;
//相当于 point2=&a

*&a;
//*&a 和*point1 作用相同,都等价于变量 a

让我们来运用一下 (输入两个数,按从大到小顺序输出)

#include <iostream>
using namespace std;
int main()
{
    int a,b,*point1,*point2 ;
    int *p; //用来存放地址
    cin >> a >> b ;
    point1 = &a ;
    point2 = &b ;

    if(*point1 >= *point2)
    {
        cout<<*point1<<" "<<*point2;
    }
    else
    {
        p = point1;
        point1 = point2;
        point2 = p;
        cout<<*point1<<" "<<*point2;
    }
    return 0 ;
}

3.用指针变量做函数参数 

  • 当指针类型做函数参数时,作用是 将一个变量的地址传给被调用函数形参
  • 实参变量和形参变量之间的数据传递是“单向传递”,指针变量做函数参数时也不例外 
  • 用指针函数做参数时,调用函数不会改变实参指针变量的值,但可以改变实参指针变量所指向的值。利用这一性质,我们就可以通过调用函数,来改变主调函数中的变量的多个值。 

 我们来进行实际运用一下(输入两个数,按从大到小顺序输出)

#include <iostream>
using namespace std;
int main()
{
    void change(int *a,int *b);    

    int a,b,*point1,*point2 ;
    cin >> a >> b ;
    point1 = &a ;
    point2 = &b ;

    if(*point1<*point2)
        change(point1,point2);

    cout<<*point1<<" "<<*point2;
    
    return 0;
}
void change(int *a,int *b)
{
    int temp;
    temp=*a;
    *a=*b;
    *b=temp;
}

但若函数写为下面的格式,就会出现错误

void change(int *a,int *b)
{
    int *temp;
    temp=a;
    a=b;
    b=temp;
}
//值传递只会单方向进行

Tip:若想通过调用函数来使若干变量的值发生改变,可以在主函数中为相应的变量创建指向它的指针变量(n个),编写函数时,其形参即为n个指针变量,在函数中改变其指向的值即可。 

4.数组与指针

4.1通过指针访问一维数组

  • 指针变量既然可以指向变量,当然也可以指向数组元素(把某个元素的地址放到一个指针变量中)
  • 数组元素的指针就是数组元素的地址
int a[10];
int *p;
p = &a[0];

在C和C++中,数组名代表数组中第一个元素的地址(即序号为 0 的元素) 

因此,下面两行代码等价

p = *a[0];
p = a;     //把 a 数组首元素的地址赋给 p

要注意:数组名 a 并不能代表整个数组,这里只代表数组首元素地址

定义指针变量 (可以在定义时就赋初值)

int *p = &a[0];  //将a[0]的地址赋给p
int *p = a;       //与上一行等价

此时可以对 p 所指向的数组元素赋值
*p = 10;    //让 p 所指向的数组元素等于 1
  • 若指针变量 p 已经指向数组中某一元素,则 p+1 指向该数组的下一个元素
int a[3] = {1,2,3};
int *p = a;

cout<<*p<<endl;
cout<<*(p+1)<<ednl;
cout<<*(p+2);

运行结果应为:
1
2
3
  • 同样的,数组名 a 代表第一个数组的地址,a+1 也就代表第二个,即 a[1] 的地址 
  • 指向数组元素的指针变量也可以带下标,p[ i ] *( p+i ) 等价

小结:

若 p 的初值为 &a[0] ,则:

  1. p+i a+i 就代表 a[ i ] 的地址
  2. *(p+i) *(a+i) 即为 a[ i ]
  3. 指向数组元素的指针变量也可以带下标,eg:p[ i ]=*(p+i)[ ],实际为变址运算符
int a[10] = {0,1,2,3,4,5,6,7,8,9};
int *p = a;

1.以下表述(地址)等价
p+i;
a+i;

2.以下表述(元素)等价
a[i];
p[i];
*(a+i);
*(p+i);

 我们来运用一下(输出数组中全部元素)

#include <iostream>
using namespace std;
int main()
{
    int a[10] = {0,1,2,3,4,5,6,7,8,9};
    int *p;     // int *p = a
    p = &a[0];
    for(int i=0;i<10;i++)
        cout<<*(p+i)<<endl;
    return 0;
}

//输出的其他格式(1)
for(int i=0;i<10;i++)
    cout<<*(a+i)<<endl;
//(2)
for(int i=0;i<10;i++)
    cout<<a[i]<<endl;
//(3)
for(int i=0;i<10;i++)
    cout<<p[i]<<endl;
//(4)
for(p=a;p<(a+10);p++)    //p 先后指向a[0]~a[9]
    cout<<*p<<endl;
  • 这里要注意(4),不能执行操作 “ a++ ”操作,因为实参数组名 a 代表一个固定的地址(或者说是指针常量),因此改变 a 的值是不可能的 
//也可以自定义数组元素
for(int i=0;i<10;i++)
    cin>>a[i];  //cin>>p[i]
//(2)
for(int i=0;i<10;i++)
    cin>>*(a+i); //cin>>*(p+i) 

C++编译系统将 a[ i ] 转换为 *(a+1) 进行处理,对每个 a[ i ] 都分别计算地址 a+i*dd是一个元素所占的字节数) ,然后访问该元素  

4.2用指针访问二维数组

 4.2.1 指向元素的指针

int a[3][4]={{1,2,3,4},{2,3,4,5},{4,5,6,7}};
int *p =&a[0][0];

在这种定义下,对于元素的访问就需要一个一个向后推,假若想要访问 a[ i ][ j ]号元素,第 ij 列元素实际上就是第 i*L+j 号元素(L为二维数组的列数),由4.1可知,*(p+i*L+j) = a[ i ][ j ]

4.2.2 指向行的指针

int a[3][4]={{1,2,3,4},{2,3,4,5},{4,5,6,7}};
int *p=a[0];

二维数组实际上是一个一维数组且每个元素都是一个一维数组,让 p 指向a[0],就相当于指向 a[0]的第一个元素,即a[0][0],若让 p 指向第 i 行,那么p +j 就是第 i j 列 元素的地址了,即 *(p+j)=a[ i ][ j ];

5.用指针作函数形参接收数组地址

通过对(3.用指针变量做函数参数)的学习,我们知道用指针变量做形参时,也可以接收从实参传递来的数组首元素地址

我们来实践一下(将一组数按从小到大顺序排列)

#include <iostream>
using namespace std;
int main()
{
    void pailie(int *a, int n);
    int a[10];
    int* p = a;
    for (p = a; p < (a + 10); p++)
        cin >> *p;
    pailie(a, 10);
    for (int i = 0; i < 10; i++)
        cout << *(a + i) << " ";

    return 0;
}
void pailie(int *a, int n)
{
    void change(int *b, int *c);
    int* q;
    for (int i = 0; i < n; i++)
    {
        q = &a[i];
        for (int o = i + 1; o < n; o++)
        {
            if (*q > a[o])
                change(q, a+o);
        }
    }
}
void change(int *b, int *c)
{
    int n = *b;
    *b = *c;
    *c = n;
}

我们发现,C++编译系统将形参数组名一律当作指针处理 

因此以下两种写法完全等价:

void pailie(int *a, int b);
void pailie(int a[],int b);//两种表述完全等价

( 这就清楚地说明:实际上在函数调用时并不存在一个占有存储空间的形参数组,只有指针变量 

6.字符串与指针

 我们从定义一个字符指针开始

char *a;
char k[] =" kkJiaYou!";
a = k;

我们也可以直接对指针变量进行初始化

char *k = "kkJiaYou!";

这里直接用字符串变量 “kkJiaYou!”k 进行初始化,实际上是把字符串中第一个字符的地址赋给

我们可以对其进行输出

char *k = "kkJiaYou!";
cout << k;

运行结果应为:
kkJiaYou!
  • 在输出时,系统会先输出 k 所指向的第一个字符数据,然后使 k 自动 加 1 ,使 k 指向下一个字符并输出......直到遇到字符串结束的标志 ’\0‘ 为止

我们来练习一下(将字符串a1的内容,复制到字符串a2中去)

#include <iostream>
using namespace std;
int main()
{
    char a1[]="kkJiaYou!",a2[20],*a,*b;
    a = a1;
    b = a2;

    for(;*a!='\0';a++,b++)
        *b = *a;        
    *b = '\0';  //输出b时可以自动结束

    a = a1;     //让指针指向的值变回首字母
    b = a2;

    cout<<a<<endl;   //指针所指向的值一直在变化
    cout<<b;
    return 0;
}

7.函数与指针

  • 指针变量也可以指向函数
  • 一个函数在编译时被分配给一个入口地址,这个函数的入口地址就称为函数的指针
  • 定义指向函数的指针变量的一般定义式为:函数类型 (*变量名) (函数形参);
  • 形如  :int ( *p )(int , int );     char( *p )(char,char);

让我们来实际操作一下(把一组数进行从大到小排列)  

#include <iostream>
using namespace std;
int main()
{
    void paixu(int *a, int n);
    void(* pai)(int*, int);    //声明指针函数 pai
    pai = paixu;               //使 pai 指向函数 paixu 

    int a[10], * p;
    p = a;
    for (int i = 0; i < 10; i++)
        cin >> p[i]; 
    pai(p, 10);              //调用函数
    for (int i = 0; i < 10; i++)
        cout << *(p + i)<<" ";
    return 0;
}
void paixu(int *a, int n)
{
    for (int i = 0; i < 10; i++)
    {   
        for (int k = 0; k < n - i-1; k++)
        {
            if (*(a + k) < *(a + k + 1))
            {
                int v = *(a + k);
                *(a + k) = *(a + k + 1);
                *(a + k + 1) = v;
            }
        }
    }
}
  • 注意:定义函数的指针变量 pai 时,两侧的括号不可以省略,表示 p 先与 * 结合,是指针变量,然后再与后面的括号结合,表示该指针变量指向函数

8.返回指针的函数

  • 返回指针的函数称为 指针函数 (调用后得到一个指向某类型数据的指针,即地址)
  • 定义指针函数的一般形式为:类型名 *函数名 (参数列表);
  • 形如:int  *a (int x,int y) ; (要注意 a 左右没有括号)

9.指针数组

  • 元素均为指针类型数据的数组,称为指针数组
  • 数组中每一个元素都相当于一个指针变量,值为地址
  • 一维数组定义式:类型名 * 数组名[数组长度];[ ] * 优先级高 )

               int (*a)[ 4 ];    这是指向一维数组的指针变量

下面来应用一下 (将若干字符串按首字母顺序输出,从大到小)

#include <iostream>
#include <string>
using namespace std;
int main()
{
	void sort(const char * a[], int n);
	const char *name[] = {"Laowang","Xindi","Dage","Rongrong",
    "Tianyu","Xiaodaun","Fage","Ernu"};
    //在指针数组name中,8个元素的初值均为字符串的起始地址

	sort(name, 8);
	for (int i = 0; i < 8; i++)
	{
		cout << name[i] << endl;;
	}
	return 0;
}
void sort(const char* a[], int n)
{
	const char* temp;
	for (int i = 0; i < n - 1; i++)
	{
		for (int k = i + 1; k < n; k++)
		{
			if (strcmp(a[i], a[k]) < 0)
			{
				temp = a[i];
				a[i] = a[k];
				a[k] = temp;
			}
		}
	}

}

输出数组也可以这样写 :

int i=0;
while(i<8)
{
    cout<<*(name+i++)<<endl; //先求*(name+i),再进行i++操作
}

补充三种字符串函数(使用时必须加头文件 “#include <string> ”)

  1. 字符串连接函数:strcat( );
  2. 字符串复制函数:strcpy( );
  3. 字符串比较函数:strcmp( );

10.指向指针的指针 

  • 指向指针型数据的指针,简称为指向指针的指针
  • 也可以这样理解:一个指针变量存放的又是另一个指针变量的地址,则称这个变量为指向指针的指针变量(指向指针的指针)
  • 定义指向指针型数据的指针变量的一般形式:类型名 **变量名 (* 运算符的结合性是从右到左)
  • 形如:int *(*p);    int **p;

 int(*p)p 是指向整型数据的指针变量,现在(*p)前还有一个“ * ”,变为 int **p,就表示 p 指向的是整型指针数据*p 就代表 所指向的整型指针

(指向字符型数据的指针变量)

#include <iostream>
using namespace std;
int main()
{
    const char** p;        //定义指向字符指针数据的指针变量 p
    const char* name[] = { "Laowang","Xindi","Dage","Rongrong",
    "Tianyu","Xiaodaun","Fage","Ernu" };

    p = name + 2;

    cout << *p << endl;    //输出 name[2]指向的字符串
    cout << **p;           //输出 name[2]的字符串中的第一个字符
    return 0;              
}
运行结果应为:
Dage                       //*p 代表 name[2],指向字符串“Dage”
D                          //**p 是 *p 指向的“Dage”第一个字符的内容,即'D'

11.const 指针

11.1指向常量的指针变量

  • 定义这种指针变量的一般形式:const 类型名 * 指针变量名(经定义后,不允许通过指针变量改变他所指对象的值)
  • 常用于做函数的形参,以防止指针形参所指对象的值改变而影响实参
int a = 12, b = 15;
const int * p = &a;
*p = 15; //错误,不合法

//但直接改变 a 的值,是合法的
a = 15;  //合法  

 虽然指针 p 所指向的值不能发生改变,但指针变量p的值是可以改变的:p = &b ;

(改变指针形参所指对象的值)

#include <iostream>
using namespace std;
void chan(int *a)
{
    *a=5*(*a);
}

int main()
{
    int a=10;
    chan(&a);
    cout<<a<<endl;
    return 0;
}
输出结果为:
50

11.2常指针 (注意与指向常量的指针变量的区别)

  • 常指针变量,简称常指针,定义这种指针变量的一般形式:类型名 *const 指针变量名
  • 必须在定义时初始化,指定其指向
  • 指向不能改变,但指向变量的值可以改变
int *const a = 12,b = 15;
*a = 15;   //合法
a = &b;    //不合法

11.3指向常量的常指针(把以上两种作用叠加)

  • 定义这种指针变量的一般形式:const 类型名 *const  指针变量名
  • 指针指向固定对象,该对象的值不能改变
int a = 12, b = 15;
const int *const p = &a;
p = &b;     //不合法
*p = 15;    //不合法
a = 15;     //合法

*12.void指针类型

  • 基类型为void的指针变量,即(void *)型变量
  • 它不指向任何类型数据(可理解为 “指向空类型” 或 “不指向确定的类型” 的数据)可理解为“过渡型”
  • 实际使用该指针时,要对他进行强制转换,使之适合于被赋值的变量类型
  • 可以把非void型的数据直接赋给void型指针变量,但不能把void型指针直接赋给非void型指针变量,必须先进行强制转换
int a;
int *p = &a;
void *q;
q = (void *)p;    //将 p 强转为void型,再赋给q
cout<<*q<<endl;   //错误,q 不指向确定类型数据,不合法
cout<<*(int *)q;  //把 q 强转为 int 类型,合法


注意:
q = p;      //合法
p = q;      //不合法
p =(int *)q;//合法

*13.引用(本质依旧为指针)

  • 对一个数据可以建立一个 “引用” ,作用为一个变量起一个别名(是C++对C的一个重要扩充)
int a = 12;
int &b = a;     
//声明 b 是 a 的 “引用”
//经过声明后 a || b 的作用相同,都代表同一变量 

相当于:
int A = 12;
int *B;
B = &A;     //B 为指向 A 的指针
  •  & 是引用声明符,并不代表地址

注意:

  1. 对于“引用”,只有声明,没有定义
  2. 声明一个“引用”的同时,必须同时初始化
  3. 在声明一个引用后,不能再使之作为另一个变量的引用
  4. 不能建立 引用数组/引用的引用/引用的指针 
  5. 可以取引用的地址
  6. 要区别 引用声明符 和 地址运算符

(通过以引用为形参,实现两个变量的值互换(适用于C++) )

#include <iostream>
using namespace std;
int main()
{
    void Change(int &,int &);
    int A = 12,B = 15;
    change(A,B);        
//虚实结合,调用时a成了A的别名,b成了B的别名

    cout<<A<<" "<<B;
    return 0;
}
void Change(int &a,int &b)
{
    int temp;
    temp = a;
    a = b;
    b = temp;
}

运行结果为:
15 12

14.其他

  • 指针变量可以有空值,即该指针变量不指向任何变量  (表示为:p = NULL;

 ~the end.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Space-shuttle-time

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值