[C++] C++基础(三) 指针

本文详细介绍了C++中的指针,包括指针的声明与初始化、指针注意事项、指针与数组的关系、指针算术、指针与字符串的交互、如何通过指针访问结构中的成员以及二级指针的概念。内容覆盖了指针的基础知识和常见用法,有助于理解C++中的指针操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.概述

程序在运行时,所有的程序和数据都存放在内存中。内存是以字节为单位的连续的存储空间,每个内存单元有唯一的内存编号,称为内存地址,每个变量都有自己的内存地址,系统对内存单元进行操作时,需要通过内存地址找到相应的内存单元。

一般来说,对于简单数据类型,通过变量名就可以对内存单元进行操作,如:i=400,不需要知道内存地址,很多高级程序语言都不允许通过内存地址访问内存单元,C和c++中则可以通过指针类型对允许通过地址来访问内存单元,只不过这个地址的确切值不需要程序员了解;

对于变量i,如果用另一个变量p记住他的内存地址,变量p的数据类型就是指针类型。如果p的内容是I的地址,则称为p指向i;

int i = 20;
int* p = &i;

如果有定义int*p,则说明p是一个指针变量,p的内容应该是一个内存地址,通过取地址符&可以使得p指向另一个存储单元,p本身要占用一个内存单元;

1.指针的声明和初始化

由于指针是变量,需要在使用之前先定义,定义格式:

指针所指的数据类型 *指针变量名1,2.....

如:

int * p_int;

*称为解除引用运算符,*两侧的空格可选,一般来说,C语言倾向于使用这种方式:

int *p;//强调*p是一个int 类型的值

c++倾向使用这种方式:

int* p;//强调p是一个指向int类型数据的指针

指针运算符&和*:&是取地址运算符,*是解除引用运算符,二者都是单目运算符。

2.指针注意事项

  • 1.在c++中创建指针时,计算机分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存,如:
int *age;
*age = 21;//bad

由于没有说明age指向哪里,那么21将被随机地放在内存中的一个地址中,从而可能会导致错误的发生。

因此,一定要在对指针使用*运算符之前,将指针初始化为一个确定的地址。

  • 2.在定义指针变量时,可以将其定义为指向任何一种基本数据类型的存储单元,当使用取地址运算符为其赋值时,所指向的数据类型要一致,如:
int i;
float *p;
p=&i;

这样是错误的.

  • 3.*的含义不同,它可以作为算术运算符,还可以作为定义指针使用,还可以作为指针取内容运算符;比如如下示例中:
int i;
int *p= &i;
*p=i*3
  • 4.指针只是概念上的地址,不必关心它的值是什么,因为对于某个变量来说,内存空间是由系统分配的,在C语言中不能将内存地址赋值给一个指针变量,如:int *p;p=3000;这是错误的;

  • 5.指向相同的数据类型的指针可以相互赋值,如:

int x = 2;
int *pi;
int *pj;
pi=&x;
pj=pi;

这样一来,pj也就指向x了。

3.指针和数组

我们知道,可以通过new来动态申请一个数组,并返回第一个元素的地址:

int* arr = new int[size];

如此一来,指针arr就指向arr[0].
我们又知道,数组是一段连续的存储空间,数组名是该存储空间的首地址:

int arr2[5];

arr2[0]是数组arr2中的第一个元素,所以数组名arr2表示arr2[0]的首地址。

因此在C++中,大多数情况下,指针和数组基本等价,两者可以相互替代。
在如下示例中,可以通过指针来操作数组:

#include <iostream>

using namespace std;

const int Size = 5;
int main()
{
        int arr[Size] = {1,2,3,4,5};
    
        for(int i =0;i< Size;i++)
        {
                cout << "arr[" << i<< "]:" << *(arr+i) << endl;
        }
        return 0;
}

然而也有例外,在以下三种情况下,数组名和指针不同:

  • 1.数组声明使用数组名来标记存储位置;
  • 2.数组名使用sizeof()运算符时,返回整个数组的长度;
  • 3.将地址运算符&用于数组名时,将返回整个数组的地址;

一般情况下,以下语句都是允许的:

int arr[3] = {1,2,3};
int * p = arr;
cout << arr[0] << endl;//数组索引访问元素
cout << p[0] << endl;
cout << *(p+1) << endl;//指针访问元素
cout << *(arr+2) << endl;

4.指针算数

将整数变量加1,其值将加1,但将指针变量加1后,增加的量为它指向的类型的字节数。

#include <iostream>

int main()
{
	using namespace std;
	double wages[3] = {10000.0,20000.0,30000.0};
	short stacks[3] = { 3,2,1 };

	double * pw = wages;
	short * ps = &stacks[0];

	cout << "pw = " << pw << ", *pw = " << *pw << endl;
	pw += 1;
	cout << "add one to the pw pointer:" << endl;
	cout << "pw = " << pw << ", *pw = " << *pw << endl;
	cout << "ps = " << ps << ", *ps = " << *ps << endl;

	ps += 1;
	cout << "add one to the ps pointer:" << endl;
	cout << "ps = " << ps << ", *ps = " << *ps << endl;

	cout << "access two element with array notation:" << endl;
	cout << "stacks[0] =" << stacks[0] << ",stacks[1] = " << stacks[1] << endl;

	cout << "access two element with pointer notation:" << endl;
	cout << "*stacks =" << *stacks<< ",*(stacks+1) = " << *(stacks + 1) << endl;

	cout << "sizeof(wages):" << sizeof wages << endl;
	cout << "sizeof(pw)" << sizeof pw << endl;

	system("pause");
	return 1;
}

从以上示例中可得到如下结论:

  • 1.array[i] => *(array+1),arrsy表示数组
  • 2.*(pointer+1) => pointer[i],pointer表示指针;
  • 3.c++允许将指针和一个整数相加,加1的结果为原来的地址加上指向对象的类型占用的字节数。

因此,很多情况下,可以以相同的方式使用指针和数组名。

5.指针和字符串

由于数组和指针的特殊关系,因此这种关系同样使用于C风格的字符串:

char name[20] = {'h','e','l','l','o',' ','w','o','r','l','d','\0'};
char name[20] = "hello world";
const char * p_name = "hello world";//将字符串常量地址赋给了p_name.

在c++中,用引号括起的字符串也像数组一样,是第一个元素的地址。因此,对于数组中的字符串、用引号括起的字符串、指针所描述的字符串,处理方式都一样,传递他们的地址。

注意上面最后一种指针类型的字符串,使用了const来修饰,因为字符串字面值是常量,c++中禁止将字符串常亮赋给char *指针,如果是char *p_name = "hello world";,则会编译出现如下错误:

ptrstr2.cpp: In function ‘int main()’:
ptrstr2.cpp:7:18: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]

一般来说,给cout提供一个指针,它将打印地址,但如果是char * 类型的指针,则将打印指向的字符串:

#include <iostream>
#include <cstring>

int main()
{
        using namespace std;
        const char * animal = "pigge";
        int * pint = new int;
    
        cout << "animal: " << animal << endl;
        cout << "(int *)animal: " <<(int *) animal << endl;
        cout << "pint: " << pint << endl;
    
        return 1;
}
/**
animal: pigge
(int *)animal: 0x400a65
pint: 0x195ac20
/

初始化char数组时使用=,但将字符串赋值给char数组时,应使用cstring中提供的strcpy()strncpy()函数.(这也侧面反映出c++的string类较C风格更简单)。

6.指针访问结构中的成员

可以使用new创建一个未命名的结构类型变量,其创建格式和创建动态数组一样。但访问结构成员,需要一种新的运算符->来进行,如下示例:

//定义一个结构体
struct inflatable
{
        char name[20];
        float volume;
        double price;
};
int main()
{
    //创建一个静态结构
    inflatable inf = {"test",20.8,23.4};
    //创建一个动态结构
    inflatable * pstruct = new inflatable;
    return 1;
}
访问结构成员

当创建好一个动态结构时,将把该结构的一块内存地址赋给指针。当访问结构内成员时,k可以有如下两种方式:

  • 1.不能使用成员运算符(因为不知道结构名称,只知道地址),应使用箭头成员运算符->:
cout << pstruct->name << endl;
  • 2.由于pstruct是指针,则*pstruct就是指针指向的值,即结构本身,因此如下形式也可以:
cout << (*pstruct).name << endl;

下面是一个示例:

#include <iostream>

struct inflatable
{
        char name[20];
        float volume;
        double price;
};

int main()
{

        using namespace std;
        inflatable * ps = new inflatable;
        cout << "Enter name of inflatable:";
        cin.get(ps->name,20).get();
        cout << "Enter volume in cubic feet: ";
        cin >> ps->volume;
        cout << "Enter price: " << endl;
        cin >> ps->price;
     
        cout << "name: " << (*ps).name << endl;
        cout << "Volume: " << ps->volume << endl;
        cout << "Price: $" << ps->price << endl;
        delete ps;//释放内存,必须和new配对使用
        return 1;
}
/**
编译并运行时
$ ./newstruct 
Enter name of inflatable:booker
Enter volume in cubic feet: 12
Enter price: 
34.3
name: booker
Volume: 12
Price: $34.3
$ 
/

7.二级指针

指向指针的指针称为二级指针,如:

int i = 3;
int *p = &i;
int **pp = &p;

因此可以得到:

*pp = p;
**pp = *p = i;

关于二级指针更多的内容会在学习二维数组时说明。如下是一个指针和二级指针的示例:

#include <iostream>

int main()
{
        using namespace std;
        int arr[10] = {1,2,3,4,5,6,7,8,9,10};
        int * p = arr;
        cout << "arr at " << arr << " address." << endl;
        cout << "p at " << p << " address." << endl; 
        cout << p[3] << endl;
        cout << "arr[1]:" << arr[1] << ", *(p+1):" << *(p+1) << endl;
     
        int ** pp = &p; 
     
        cout << "pp at " << pp << " address." << endl;
        cout << "*pp address:" << *pp << ",p address:" << p << endl;
        cout << "**pp value:" << **pp << ",*p value:" << *p << endl;
        cout << "*(*pp+1) value:" << *(*pp+1) << ",p+1 value:" << *(p+1) << endl;
        return 1;
}
/**
arr at 0x7ffcf3258310 address.
p at 0x7ffcf3258310 address.
4
arr[1]:2, *(p+1):2
pp at 0x7ffcf3258300 address.
*pp address:0x7ffcf3258310,p address:0x7ffcf3258310
**pp value:1,*p value:1
*(*pp+1) value:2,p+1 value:2
/

下图表示指针和二级指针的加法图示:
在这里插入图片描述

以下示例中包括了new创建动态数组、动态结构以及和指针之间的使用:

#include <iostream>

struct antaractica_years_end
{
        int year;
};

int main()
{
        antaractica_years_end s01,s02,s03;
        s01.year = 1998;
        antaractica_years_end * pa = &s02;
        pa->year = 1999;
    
        antaractica_years_end trio[3];
        trio[0].year = 2003;
        std::cout << "trio->year:" <<trio->year << std::endl;
    
        antaractica_years_end * arp[3] = {&s01,&s02,&s03};
        std::cout << "arp[1]->year:" << arp[1]->year << std::endl;
        antaractica_years_end ** ppa = arp;
        auto ppb = ppa;
        std::cout << "(*ppa)-year:" << (*ppa)->year << std::endl;
        std::cout << "(*(ppb+1))->year:" << (*(ppb+1))->year << std::endl;
    
        return 1;
}
/**
trio->year:2003
arp[1]->year:1999
(*ppa)-year:1998
(*(ppb+1))->year:1999
/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值