【学习笔记】C++语言程序设计(郑莉):数组、指针与字符串


1. 数组

数组是具有一定顺序关系的若干对象的集合体,组成数组的对象称为该数组的元素。数组元素用数组名与带方括号的下标表示,同一数组的各元素具有相同的数据类型。

1.1 数组的声明与使用

  • 数组声明

      // 数组类型声明一般形式
      数据类型 标识符[常量表达式1][常量表达式2]...;
    

    声明一个数组类型,应包含:

    • 确定数组的名称
    • 确定数组元素的类型
    • 确定数组的结构(包括数组维数,每一维的大小等)
  • 数组的使用

      // 数组使用一般形式
      数组名[下标表达式1][下标表达式2]...;
    
    • 数组元素的下标表达式可以是任意合法的算术表达式,其结果必为整数
    • 数组元素的下标值不得超过声明时所确定的上下界,否则运行时将出现数组越界错误
    // 数组声明与使用
    #include<iostream>
    using namespace std;
    
    int main() {
    	int a[10], b[10];               //定义两个有10个元素的一维数组a,b
    	for (int i = 0; i < 10; i++) {  //使用for循环对两个数组进行赋值
    		a[i] = i * 2 - 1;
    		b[10 - i - 1] = a[i];
    	}
    	for (int i = 0; i < 10; i++) {  //使用for循环输出结果
    		cout << "a[" << i << "]=" << a[i] << "   ";
    		cout << "b[" << i << "]=" << b[i] << endl;
    	}
    	return 0;
    }
    

1.2 数组的存储与初始化

  • 数组的存储
    数组元素在内存中是顺序、连续存储的。数组元素在内存中占据一组连续的存储单元,逻辑上相邻的元素在物理地址上也是相邻的。一维数组是简单的按照下标的顺序,连续存储的。二维数组在内存中是按行存放的,即先放第1行,再放第2行…每行中的元素是按列下标由小到大的次序存放
  • 数组的初始化
    数组的初始化就是在声明数组时给部分或全部元素赋初值。对于基本类型的数组,初始化过程就是给数组元素赋值,对于对象数组,每个元素都是某个类的一个对象,初始化就是调用该对象的默认构造函数
    定义数组时:
    • 如果列出全部元素的初值,可以不用说明元素个数
    • 当指定的初值个数小于数组大小时,剩下的数组元素会被赋予0值
    • 如果没有指定任何一个元素的初值
      • 对于静态生存期的数组,每个元素仍然会被赋予0值
      • 对于动态生存期的数组,每个元素的初值都是不确定的
    • 声明为常量的数组,必须给定初值
    // 数组的初始化
    // 列出全部元素的初值
    int a[3] = {1, 1, 1};
    // 列出全部元素的初值,可以不用说明元素个数
    int a[] = {1, 1, 1};
    // 指定的初值个数小于数组大小时,剩下的数组元素会被赋予0值
    float fa[5] = {1.0, 2.0, 3.0};
    // 声明为常量的数组
    const float fa[5] = {1.0, 2.0, 3.0};
    

1.3 数组作为函数参数

用数组元素作为调用函数的参数,则实参和形参都应该是数组名,且类型要相同。和普通变量作参数不同,使用数组名传递数据时,传递的是地址,也就是说如果在被调函数中对形参数组元素值进行改变,主调函数中实参数组的相应元素值也会改变。

1.4 对象数组

  • 对象数组声明语法格式:

      类名 数组名[常量表达式];
    
  • 访问对象数组元素语法格式:

      数组名[下标表达式].成员名;
    

对象数组的初始化过程,实际上就是调用构造函数对每一个元素对象进行初始化的过程。当一个数组中的元素对象被删除时,系统会调用析构函数来完成扫尾工作。


2. 指针

2.1 内存空间的访问方式

在C++程序中如何利用内存单元存取数据?
一是通过变量名,而是通过地址。具有静态生存期的变量在程序开始运行之前就被分配了内存空间。具有动态生存期的变量,是在程序运行时遇到变量声明语句时被分配内存空间的。

2.2 指针变量的声明

指针也是一种数据类型,具有指针类型的变量称为指针变量。指针变量是用于存放内存单元地址的。通过变量名访问一个变量是直接的,而通过指针访问一个变量是间接的。声明指针的语法格式为:

数据类型 *标识符;

这里 * 表示声明的是一个指针类型的变量

int * ptr;  // 定义了一个指向int型数据的指针变量

2.3 与地址相关的运算 “*” 和 “&”

  • “*” 称为指针运算符,表示获取指针所指向的变量的值
  • “&” 称为取地址运算符,用来得到一个对象的地址
// * 出现在声明语句中,在被声明的变量名之前,表示声明的是指针
int * p;           //声明p是一个int型指针
// * 出现在执行语句中或声明语句的初始化表达式中,表示访问指针所指向对象的内容
cout << *p;        // 输出指针p所指向的内容
// & 出现在变量声明语句中位于被声明的变量左边时,表示声明的是引用
int &rf;           // 声明一个int型的引用rf
// & 在给变量赋初值时出现在等号右边或在执行语句中作为一元运算符出现时,表示取对象的地址
int a, b;
int * pa, *pb = &b;
pa = &a;

2.4 指针的赋值

指针赋值有两种方法:

  • 在定义指针的同时进行初始化赋值。语法形式为:

      存储类型 数据类型 * 指针名 = 初始地址;
    
  • 在定义之后,单独使用赋值语句。语法格式为:

      指针名 = 地址;
    

一个数组,可以用它的名称来直接表示它的起始地址。数组名称实际上就是一个不能被赋值的指针,即指针常量

int a[10];      //定义int型数组
int *ptr = a;   //定义并初始化int型指针

关于指针的类型,还应该注意:

  • 可以声明指向常量的指针,此时不能通过指针来改变所指对象的值,但指针本身可以改变,可以指向另外的对象。
    int a;
    const int *p1 = &a;  //p1是指向常量的指针
    int b;
    p1 = &b;   //正确,p1本身的值可以改变
    *p1 = 1;   //错误,不能通过p1改变所指的对象
    
  • 可以声明指针类型的常量,这是指针本身的值不能被改变。
    int * const p2 = &a;  //声明p2是指针常量
    p2 = &b;              //错误,p2是指针常量,值不能改变
    
  • 一般情况下,指针的值只能赋给相同类型的指针。但是任何类型的指针都可以赋值给void类型指针。(void指针一般只在指针所指向的数据类型不确定时使用)。

2.5 指针运算

  • 指针是一种数据类型。与其他数据类型一样,指针变量也可以参与部分运算,包括算术运算、关系运算和逻辑运算。指针可以和整数进行加减运算,比如有指针p1和整数n1,则p1+n1表示指针p1当前所指位置后方第n1个数的地址,p1-n1表示指针p1当前所指位置前方第n1个数的地址。“指针++”或“指针- -”表示指针当前所指下一个或前一个数据的地址
  • *(p1+n1)表示p1当前所指后方第n1个数的内容,也可以写作p1[n1]
  • 一般来讲,指针的算术运算是和数组的使用相联系的,因为只有在使用数组时,才会得到连续分布的可操作内存空间
  • 0专用于表示空指针,也就是一个不指向任何有效地址的指针

2.6 用指针处理数组元素

int array[5];

声明了一个存放5个int类型数的一维数组,数组名array就是数组的首地址(第一个元素的地址),即array和&array[0]相同。数组中5个整数顺序存放,因此,通过数组名这个地址常量和简单的算术运算就可以访问数组元素。数组中下标为 i 的元素就是*(数组名+ i ),例如,*array就是array[0],*(array+3)就是array[3]

2.7 指针数组

如果一个数组的每个元素都是指针变量,这个数组就是指针数组。声明一维指针数组的语法形式为:

数据类型 * 数组名[下标表达式];
int *pa[3];

声明了一个int类型的指针数组pa,其中有3个元素,每个元素都是一个指向int型数据的指针。

2.8 用指针作为函数参数

子函数运行过程中,通过形参指针对数据值的改变也同样影响着实参指针所指向的数据值。C语言中,以指针作为函数的形参有三个作用:

  • 使实参与形参指针指向共同的内存空间,以达到参数双向传递的目的。C++中通过引用实现该功能。
  • 减少函数调用时数据传递的开销。C++中有时通过引用实现,有时使用指针
  • 指向函数的指针传递函数代码的首地址

2.9 指针型函数

当一个函数的返回值是指针类型时,这个函数就是指针型函数。使用指针型函数的最主要的目的就是要在函数结束时把大量的数据从被调函数返回到主调函数中。指针型函数的一般定义形式为:

数据类型 * 函数名(参数表){
	函数体
}

数据类型表明函数返回指针的类型;函数名和 “*” 标识了一个指针型的函数;参数表中是函数的形参列表。

2.10 指向函数的指针

函数指针就是专门用来存放函数代码首地址的变量。声明函数指针的一般语法为:

数据类型 (*函数指针名)(形参表)

数据类型说明函数指针所指函数的返回值类型;*函数指针名指明一个函数指针的名称;形参表列出来该指针所指函数的形参类型和个数。
由于对函数指针的定义在形式上比较复杂,所以一般使用typedef,例如:

typedef int(*Functions)(double);

声明了Functions “有一个double形参,返回类型为int的函数指针”类型的别名。所以需要声明这一类型的变量时,可以直接使用:

Functions funcPtr;

声明了一个具有该类型的名称为funcPtr的函数指针。
函数指针在使用之前要进行赋值,是指针指向一个已经存在的函数代码的起始地址,一般语法为:

函数指针名 = 函数名;

通过指针访问类的静态数据成员示例:

#include<iostream>
using namespace std;

//Point类定义
class Point {
public:
	Point(int x = 0, int y = 0) :x(x), y(y) {	//Point类构造函数
		count++;
	}
	Point(const Point &p) :x(p.x), y(p.y) {		//Point类复制构造函数
		count++;
	}
	~Point() {									//析构函数
		count--;
	}
	int getX() const{							//获取x的成员函数
		return x;
	}
	int getY() const {							//获取y的成员函数
		return y;
	}
	static int count;							//静态成员
	
private:
	int x, y;
};

int Point::count = 0;

int main() {
	//通过指针访问类的静态数据成员
	int* ptr = &Point::count;									//定义指向Point类静态成员count的指针ptr
	Point a(4, 5);												//定义Point对象a
	cout << "Point A: " << a.getX() << "," << a.getY() << " ";	//获取A点的坐标
	cout << "Object count=" << *ptr << endl;					//计算对象个数

	Point b(a);													//定义Point对象b
	cout << "Point B: " << b.getX() << "," << b.getY() << " ";	//获取B点的坐标
	cout << "Object count=" << *ptr << endl;					//计算对象个数
	return 0;
}

运行结果为:
在这里插入图片描述

2.11 对象指针

对象指针就是用于存放对象地址的变量。声明对象指针的一般语法为:

类名 *对象指针名;

使用对象指针访问对象成员的语法形式为:

对象指针名->成员名;

(*对象指针名).成员名

3. 动态分配内存

建立和删除堆对象使用两个运算符:new 和 delete

  • 运算符 new 的功能是动态分配内存,或者称为动态创建堆对象。如果内存申请成功,new 运算符便返回一个指向新分配内存首地址的类型的指针,可以通过这个指针对堆对象进行访问;如果申请失败,会抛出异常。语法格式为:

      new 数据类型(初始化参数列表);
    
  • 运算符 delete 用来删除由 new 建立的对象,释放指针所指向的内存空间。语法格式为:

      delete 指针名;
    
  • 运算符 new 也可以创建数组类型的对象。用 new 建立的数组,用 delete 删除时在指针名前面要加“[]",即“delete[] 指针名 ;” 创建数组类型对象语法格式为:

      new 类型名[数组长度];
    

4. 用vector创建数组对象

数组是继承于C语言的一种表示群体数据的方法,具有简单、高效的优点,但无论是静态数组,还是用new动态创建的数组,都难以检测下标越界的错误。为此,C++提供了被封装的动态数组–vector。用vector定义动态数组的形式为:

vector<元素类型>数组对象名(数组长度);
int x = 10;
vector<int>arr(x);  //定义了一个大小为10的int型动态数组对象arr

与普通数组不同的是,用vector定义的数组对象的所有元素都会被初始化。如果数组的元素类型为基本数据类型,则所有元素都会被以0初始化,如果数组元素为类类型,则会调用类的默认构造函数初始化。另外,初值也可以自己指定,但只能为所有元素指定相同初值,形式为:

vector<元素类型>数组对象名(数组长度,元素初值);

对vector数组对象元素的访问形式,与普通数组一致:

数组对象名[下标表达式]

但是vector数组对象的名字表示的就是一个数组对象,而非数组的首地址,因为数组对象不是数组,而是封装了数组的对象。vector定义的数组对象具有一个重要的函数成员 size(),它会返回数组的大小


课程链接:https://www.bilibili.com/video/BV1iF411Y74v

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值