文章目录

一、地址与指针

日常生活中我们会问一个人住在哪,比如小明家住在XX省XX市XX县XX镇XX村,这就是其详细住址,也就是地址。
那么在程序中定义的任何变量实际上都是存在内存中的,那么他们的具体位置是多少呢,这里就涉及到了地址。就是这个变量真正存在的位置。
下面我们用代码举个例子,例如定义一个字符:
char ch = ‘a’;
我们用vs的监视窗口来看下字符 ch 的地址是多少呢?
那么在使用 cout << ch; 进行输出的时候,cout 也会去从 ch 的地址开始读取一个字符,之后打印输出出来。

变量的地址一般都是比较难记的,例如:0x007f2eab 之类的,那么能不能用专门的一个东西,或者说一种特殊类型的变量来保存这个地址呢?这个特殊类型的变量就是指针。

定义指针及初始化

int* p; //声明了一个指向int类型的指针,就在类型的后面加上一个*,代表是指针类型变量的意思。
但是他没有指向任何变量的地址。

#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
//定义的时候初始化
int a = 5;
int* ptr = &a;
*ptr = 6; //间接修改了a的值
cout << "a=" << a << endl;

// 先定义后初始化
int a = 5;
int *q;
q = &a;
return 0;
}
===============
以上输出:a=6
  1. 将一个int类型的指针p指向了int类型变量a的地址。&符号是取地址符号,&a 就是取变量a的地址。
    指针p也是一个变量,只不过是一个指针类型的变量,所以指针变量p的值就是 &a,指针变量p的地址是 &p。也就是说指针变量也是变量,他也有内容和地址。
  2. 在指针变量的前面加上一个​​*​​​就能得到指针指向的变量自身。
    所以对一个变量的修改,既可以通过该变量自身(直接修改),也可以通过指针/地址来实现修改(间接修改)。

说明:
①、指针变量的定义格式如下:基类型 ​​​*​​​ 指针变量名;
②、符号​​​*​​​ 既可以靠近基类型,也可以靠近指针变量名,例如:​​int* p​​​; 和 ​​int *p​​​; 都是正确的。
③、指针变量可以在定义的时候就初始化,也可以先定义后初始化,也可以在以后的任意一个时间去指向某个变量的地址:
④、基类型就是该指针变量指向的变量的类型。例如: int* ptr; 就是定义了一个指向int类型的指针变量ptr;你就不能把一个float类型的变量地址赋给他。
⑤、指针变量可以指向同一类型的变量,例如:
int a = 5, b = 6;
int *p = &a;
p = &b;
即:指针变量p既可以指向变量a的地址,也可以指向变量b的地址。

定义一个函数,实现交换两个参数的值:

void swap(int* pa, int* pb)
{
int t = *pa;
*a = *b;
*b = t;
}
二级指针

如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针。

假设有一个 int 类型的变量 a,p1是指向 a 的指针变量,p2 又是指向 p1 的指针变量,它们的关系如下图所示:

C++(七)指针_指针变量


int a =100;

int *p1 = &a;

int **p2 = &p1;

指针变量也是一种变量,也会占用存储空间,也可以使用&获取它的地址。C语言不限制指针的级数,每增加一级指针,在定义指针变量时就得增加一个星号*。p1 是一级指针,指向普通类型的数据,定义时有一个*;p2 是二级指针,指向一级指针 p1,定义时有两个*。

二、指针与数组

之前我们知道如何 ​​定义数组​​,在这里我们定义一个指针的数组,用来保存之前那个数组的所有元素的地址。如下:

int num[100] = {0};
int* pnum = &num[0]; // 或者这样写:int* pnum = num;
for(int idx = 0; idx < 100; ++idx)
{
p_num[idx] = &num[idx];
}

①、C/C++中规定数组名字就是数组的首地址。也就是数组的第0个元素的地址。
②、C/C++中规定如果指针变量pnum已经指向数组中的一个元素,则pnum+1指向同一数组中的下一个元素的地址(而不是将pnum的值简单+1)。
③、访问数组的两种方法:
  下标法:num[0];
  指针法:*(pnum+idx)
  小案例:指针遍历数组:

#include "stdafx.h"
#include <iostream>
using namespace std;

int main()
{
int num[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* pnum = num;
for (int idx = 0; idx < 10; ++idx)
{
cout << *(pnum + idx) <<",";
// cout << *pnum++ << ","; 效果同上一行
}
cout << endl;
return 0;
}
===========运行输出============
0,1,2,3,4,5,6,7,8,9,

④指针运算(算术)
从上例你也可以得出:比如 int 类型指针pnum,pnum++ 他会指向下一个int的地址。

int num_aaa[11] = {5,2,3,4,5,6,7,8,9,1,0};
int *pt = num_aaa; // 指向第一个元素 5;假设首地址为5000
pt++; // 此时指向第二个元素 2;此时指向地址为5004
int *pe = &num_aaa[9]; // 指向第十个元素 1;此时指向地址为5036
pe--; // 指向第九个元素 9,即num_aaa[8];此时指向地址为5032
int diff = pe - pt // diff=7,此时获取num_aaa[8]~num_aaa[1]之间的元素
字符指针与字符数组

定义两个字符数组输入如下:
用指针的方式,实现将 str2 拷贝到 str1 中。

int main()
{
char *p = new char[6];
char *a = "hallo"; //或 char a[] = "hallo";
strcpy(p, a);
cout << p << endl;
}

小案例:字符指针数组

void print_char(char* array[], int len);  //函数声明  


int main(void)
{
char *a[] = { "abc","cde","fgh" }; //字符指针数组
char* *b = a; //定义一个指向指针的指针,首地址指向字符串的首地址 `abc\0`
cout << *b << "," << *(b + 1) << "," << *(b + 2) << endl;
cout << "================" << endl;

char* test[] = { "abc","cde","fgh" }; //指针数组元素为字符串
int num = sizeof(test) / sizeof(char*); //计算字符串个数
print_char(test, num);
return 0;
}

void print_char(char* array[], int len) //传递指针数组地址,每加1也就是加上sizeof(char*)的长度
{
for (int i = 0; i < len; i++)
{
cout << *array++ << endl;
}
}
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;



struct student
{
int num;
char *name;
char sex;
float score;
};

int main()
{
student stu_01[5] = {
{ 101,"Zhou ping",'M',45 },
{ 102,"Zhang ping",'M',62 },
{ 103,"Liou fang",'F',92 },
{ 104,"Cheng ling",'F',87 },
{ 105,"Wang ming",'M',58.0 },
};
student *ps = stu_01; //定义结构指针变量
for (ps = stu_01; ps < stu_01 +(sizeof(stu_01)/sizeof(stu_01[0])); ps++) //结构变量指针指向结构数组首地址(也就是函数名)
{
cout << "ps->num, ps->name, ps->sex, ps->score:" << (ps->num, ps->name, ps->sex, ps->score)<< endl;
}
return 0;
}
==================运行输出======================
ps->num, ps->name, ps->sex, ps->score:45
ps->num, ps->name, ps->sex, ps->score:62
ps->num, ps->name, ps->sex, ps->score:92
ps->num, ps->name, ps->sex, ps->score:87
ps->num, ps->name, ps->sex, ps->score:58

三、指针与动态数组

new 分配内存

在C语言中,使用库函数malloc()来分配内存,在C++中仍然可以使用,但还要更好的方法new运算符。
new其实就是告诉计算机开辟一段新的空间,但是和一般的声明不同的是,new开辟的空间在堆上,而一般声明的变量存放在栈上。通常来说,当在局部函数中new出一段新的空间,该段空间在局部函数调用结束后仍然能够使用,可以用来向主函数传递参数。另外需要注意的是,new出来的是一段空间的首地址。所以一般需要用指针来存放这段地址。

通用格式如下:
typeName * pointer_name = new typeName;

示例如下:

#include <iostream>
#include <string>
using namespace std;

struct student
{
string name;
int age;
};


int main()
{
int *pn = new int; //申请 int 类型内存
int *pm = new int(3); //可以在new后面直接赋值

int *pl = new int; //先声明再赋值
*pl = 3;

int q = *new int; //也可以定义一个变量,在new之前用“*”表示new出来的内容
q = 1;


int *qo = new int[3]; //new一个数组(包含3个int元素的数组)

//new一个结构体数组
student *stu = new student[3]{{"abc", 90}, {"bac", 78}, {"ccd", 93}};
return 0;
}
delete 释放内存

释放通过new申请的内存

#include <iostream>
#include <string>
using namespace std;

struct student
{
char name[20];
float height;
double age;
};


int main()
{
student *ps = new student; // 申请内存
cout << "请依次输入姓名(20个字符);身高(小数);年龄(整数):" << endl;
cin.get(ps->name, 20);
cin >> (*ps).height;
cin >> ps->age;

cout << "Name:" << (*ps).name << endl;
cout << "Height:" << ps->height << endl;
cout << "Age:" << ps->age << endl;
delete ps; // 释放内存
return 0;
}

申请指向动态存储的结构数组的指针。参考​​类的动态内存申请与释放​​。