C++017-C++指针及其应用

简介: C++017-C++指针及其应用

C++017-C++指针及其应用


在线练习:

http://noi.openjudge.cn/

https://www.luogu.com.cn/


C++指针及其应用


参考:


CSP-J目标

【 4 】指针

【 4 】基于指针的数组访问

【 4 】字符指针

【 4 】指向结构体的指针


1. 指针

C/C++指针是一种变量,其值为另一个变量的地址。指针可以通过解引用操作符(*)用于存储和检索地址所指向的值。通过指针,可以在函数之间传递和操作内存中的数据。指针在C/C++语言中是非常重要的概念。


1.指针变量的定义、赋值

在使用指针之前要先定义指针,对指针变量的类型说明,一般形式为:

类型说明符 *变量名;

其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示该指针变量所指向的变量的数据类型。先通过例子看看指针与普通的变量有什么不同。

1、普通变量定义

int a=3;

定义了变量 a,是 int 型的,值为 3。内存中有一块内存空间是放 a 的值,对 a 的存取操作就是直接到这个内存空间存取。内存空间的位置叫地址,存放 3 的地址可以用取地址操作符“&”对 a 运算得到:&a。


#include <iostream>
using namespace std;
int main()
{
    int a=3;
    cout<<a<<endl;
    cout<<&a<<endl;
    return 0;
}

b59a04fdec05970973a1a33fae3d621a_5498f095634942adb218640899b74c1d.png


2、指针变量定义

int *p=NULL;

定义了一个指针变量 p,p 指向一个内存空间,里面存放的是一个内存地址。现在赋值为 NULL(其实就是 0,表示特殊的空地址)。


#include <iostream>
using namespace std;
int main()
{
    int a=NULL;
    cout<<a<<endl; // 0
    cout<<&a<<endl; // 0x61fe1c
    int *p=NULL;
    cout<<p<<endl; // 0
    cout<<&p<<endl; // 0x61fe10
    return 0;
}

8272f59b2ff12d5db1f0fca385823202_54f5e012cb184d83a288a5549f7bc416.png


3、给指针变量 p 赋值

P=&a;

即把 a 变量的内存空间地址(比如:XXX)给了 p。显然,直接对 p 存取,操作的是地址。通过这个地址间接地操作,才是整数 3。P 的间接操作

要使用指针操作符*, 即*p 的值才是 3。设有指向整型变量的指针变量 p,如要把整型变量 a 的地址赋予 p 可以有以下两种方式:

①指针变量初始化的方法

int a; 
 int *p=&a;

②赋值语句的方法

int a; int *p; p=&a;

不允许把一个数赋予指针变量,故如下的赋值是错误的:

int *p;
 p=1000;

。被赋值的

指针变量前不能再加“*”说明符,故如下的赋值也是错误的:

*p=&a;

完整代码:


#include <iostream>
using namespace std;
int main()
{
    int a; int *p; p=&a;
    // *p = &a; // invalid conversion from 'int*' to 'int' [-fpermissive]|
    // p=1000;  invalid conversion from 'int' to 'int*' [-fpermissive]|
    cout<<&a<<" -- "<<p<<endl; //
    return 0;
}

指针的几个相关操作说明表


说明 样例
指针定义: 类型说明符 *指针变量名; int a=10; int *p;
取地址运算符: &; p=&a;
间接运算符: *指针变量名; *p=20;
指针变量直接存取的是内存地址; cout<<p; 结果可能是:0x4097ce;
间接存取的才是储存类型的值; cout<<*p; 结果是:20


指针变量同普通变量一样,使用之前不仅要定义说明,而且必须被赋值具体的值,未经赋值的指针变量不能使用。如定义了


int a; int *p=&a;
// 则*p 表示 p 指向的整型变量

而p 中存放的是变量 a 占用单元的起始地址,所以*p 实际上访问了变量 a,也就是说*p 与 a等价。


2.指针的引用与运算


465c785a1698963a9e2325615ae09a31_4456e54371734b739377d2a80d2571a4.png

【例 2】输入 N 个整数,使用指针变量访问输出。


#include<cstdio>
using namespace std;
int a[101],n;
int main()
{
    scanf("%d",&n);
    for (int i=1; i<=n; i++)
        scanf("%d",&a[i]);
    int *p=&a[1]; //定义指针变量 int *p,初始化为数组开始元素的地址,即 a[1];
    printf("&a[1] %d \n",&a[1]);
    for (int i=1; i<=n; i++)
    {
        printf("%d --> %d \n",*p,p);
        p++; //p 指向下一个数,详见说明
    }
    return 0;
}

db93649ba5b5f361dea6510e219a6fe2_0bde15f78e1c4892902e74f6c135d1cf.png

【说明】


“p++”的意思是“广义的加 1”,不是 p 的值(地址)加 1,而是根据类型 int 增加 sizeof(int),即刚好“跳过”一个整数的空间,达到下一个整数。

类似的:

①、“p–”就是向前“跳过”一个整数的空间,达到前一个整数。

②、(p+3)就是指向后面第 3 个整数的地址。


【例 3】无类型指针运用举例。

有时候,一个指针根据不同的情况,指向的内容是不同类型的值,我们可以先不明确定义它的类型,只是定义一个无类型的指针,以后根据需要再用强制类型转换的方法明确它的类型。

#include<iostream>
using namespace std;
int a=10;
double b=3.5;
void *p;
int main()
{
    p=&a; //p 的地址赋值
    cout<<*(int*)p<<endl; //必须明确 p 指向的空间的数据类型,详见说明
    p=&b;
    cout<<*(double*)p<<endl;
    p=&b;
    cout<<*(long long*)p<<endl;
    return 0;
}

9c89395da1a060d4d37df9a8b653381b_ccca4b1efabc4dfb828bc960eb07676b.png


必须明确 p 指向的空间的数据类型,类型不一样的不仅空间大小不相同,储存的格式也不同。如果把

cout<<*(double*)p<<endl;

改成

cout<<*(long long*)p<<endl;

输出的结果将

是:4615063718147915776。


2. 基于指针的数组访问

9d147a2aab0eeeacf436cbc0787fc97e_1586ce10d4b34fc7955f3b54662a4d08.png


#include<cstdio> 
using namespace std; 
int main() 
{ 
 int a[5],i,*pa=a; //定义整型数组和指针,*pa=a 可以在下一行 pa=a; 
 for (i=0;i<5;i++) 
 scanf("%d",a+i); //可写成 pa+i 和 &a[i] 
 for (i=0;i<5;i++) 
 printf("a[%d]=%d\n",i, *(a+i)); //指针访问数组,可写成*(pa+i)或 pa[i]或 a[i] 
 return 0; 
}

3d44ac91609b3099fa21da9845cac352_7e6591edfdcd4e53ac73f8afb644b3e6.png

d67703f5226d88600579deb7ee3cd717_5e582d372513402a9ef06813f28ae036.png


【例 6】动态数组,计算前缀和数组。b 是数组 a 的前缀和的数组定义:

b[i]=a[1]+a[2] +…+a[i],即 b[i]是 a 的 i 个元素的和。


#include<cstdio>
using namespace std;
int n;
int *a; //定义指针变量 a,后面直接当数组名使用
int main()
{
    scanf("%d",&n);
    a=new int[n+1]; //向操作系统申请了连续的 n+1 个 int 型的空间
    for (int i=1; i<=n; i++)
        scanf("%d",&a[i]);
    for (int i=2; i<=n; i++)
        a[i]+=a[i-1];
    for (int i=1; i<=n; i++)
        printf("%d ",a[i]);
    return 0;
}

77366f2e5d4df8a3ecd6fa6a91f021db_dd6153f1d8a94617b4336d068759da68.png


【说明】

动态数组的优点:在 OI 中,对于大数据可能超空间的情况是比较纠结的事,用小数组只的部分分,大数组可能爆空间(得 0 分)。使用“动态数组”,可以在确保小数据没问题的前提下,尽量满足大数据的需求。


3. 指针与字符串


25c2d90510fa864ca1c92c893964d81a_1f7d8467a94d45d2ba517c156b8b8621.png

#include<cstdio>
using namespace std;
int n;
int *a; //定义指针变量 a,后面直接当数组名使用
int main()
{
    char str1[]="I love China!";
    printf("%s\n", str1);
    char *str="I love China!";
    printf("%s\n", str);
    return 0;
}

二、字符串指针作函数参数

将一个字符串从一个函数传递到另外一个函数,可以用地址传递的方法,即用字符数组名作参数或用指向字符的指针变量做参数。在被调用的函数中可以改变字符串内容,在主调函数中可以得到改变了的字符串。


【例 8】输入一个长度最大为 100 的字符串,以字符数组的方式储存,再将字符串倒序储存,输出倒序储存后的字符串。(这里以字符指针为函数参数)


#include<cstdio>
#include<cstring>
using namespace std;
void swapp(char &a,char &b) //交换两个字符的位置
{
    char t;
    t=a;
    a=b;
    b=t;
}
void work(char* str)
{
    int len=strlen(str); //strlen(str)这个函数返回的是 str 的长度,
//需包含头文件 cstring
//这个函数的原型是"size_t strlen(const char* str)"
    for (int i=0; i<=len/2; ++i)
        swapp(str[i],str[len-i-1]);
}
int main()
{
    char s[110];
    char *str = s;
    gets(s);
    work(str);
    printf("%s",s);
    return 0;
}

2ca70989a040f9dfe43741134eeed611_fb6648311d054f21b234f0fc8cb598db.png


4. 结构体与指针


672828dc3603b0891d71fdf98b81a9b8_7012089c7f504ef09db7713b98e8f242.png

#include<cstdio>
using namespace std;
struct student
{
    char name[20];
    char sex;
    int score;
} s[3]= {{"xiaoming",'f',356},
    {"xiaoliang",'f',350},
    {"xiaohong",'m',0}
};
int main()
{
    student *p;
    printf("Name Sex Score\n");
    for (p=s; p<s+3; p++)
        printf("%-9s%3c%7d\n",p->name,p->sex,p->score);
    return 0;
}

78aaae3153001f0be6927e7f57f49eb2_4552284b127b451f8c5b82639845a5d3.png


f873682e4b1e01bdc4ee14f9244160f1_24bd10af2458448e9e8fb7b996d9221e.png


在线练习:


http://noi.openjudge.cn/


总结


本系列为C++学习系列,会介绍C++基础语法,基础算法与数据结构的相关内容。本文为C++指针及其应用案例,包括相关案例练习。

相关文章
|
7天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
26 4
|
21天前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
23天前
|
存储 安全 编译器
在 C++中,引用和指针的区别
在C++中,引用和指针都是用于间接访问对象的工具,但它们有显著区别。引用是对象的别名,必须在定义时初始化且不可重新绑定;指针是一个变量,可以指向不同对象,也可为空。引用更安全,指针更灵活。
|
1月前
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
38 1
|
1月前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
29 2
|
1月前
|
存储 编译器 C++
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
53 2
|
1月前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
1月前
|
存储 C++ 索引
C++函数指针详解
【10月更文挑战第3天】本文介绍了C++中的函数指针概念、定义与应用。函数指针是一种指向函数的特殊指针,其类型取决于函数的返回值与参数类型。定义函数指针需指定返回类型和参数列表,如 `int (*funcPtr)(int, int);`。通过赋值函数名给指针,即可调用该函数,支持两种调用格式:`(*funcPtr)(参数)` 和 `funcPtr(参数)`。函数指针还可作为参数传递给其他函数,增强程序灵活性。此外,也可创建函数指针数组,存储多个函数指针。
|
1月前
|
算法 C++
【算法】双指针+二分(C/C++
【算法】双指针+二分(C/C++
|
1月前
|
存储 编译器 程序员
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(二)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值