一、一维数组
1 声明
语法:
<数据类型> <数组名> [<整型常量表达式>];
注意:
数据类型是数组元素的数据类型;整形常量表达式必须是正整数;
变量应遵循“先声明后使用”的原则。
2 使用
语法:
<数组名> [<整型表达式>];
注意:
整型表达式是下标;数组元素下标从0开始;使用数组元素与使用一般变量没有区别;
数组声明中[]的长度与使用元素时[]的下标是不同概念,前者是常量,后者是常量或变量;
数组名就是该数组的起始地址,是一个地址常量。
3 初始化
在声明数组的同时对其元素进行初始化,使用花括号{}将初始化值括起来;
初始化时,也可不指明数组长度,编译器将用初始化列表的长度作为数组长度。
注意:
若数组长度很长,且每个元素都初始化为0,可简化成:
int num[100]={0};
4 整体操作
数组不能进行整体赋值,因为数组名是常量,不能被赋值;
除char类型的数组外,其余类型数组不能整体输入和输出。
5 作函数参数
数组作为参数时,需要指定数组,还需要指定数组长度;
选取数组名作为函数实参,选取类似数组定义方式作为形参。
举例:
#include <iostream> using namespace std; void input(float [], int); // 函数声明,或写成 void input(float array[], int len); void getHighest(float [], int, float&); const int NUM=20; int main() { float score[NUM]; float highest; input(score,NUM); // 通过score传递数组首地址,通过NUM传递数组长度给input函数的响应形参 getHighest(score,NUM,highest); cout<<highest<<endl; return 0; } void input(float array[], int len) // 函数定义 { int i; for(i=0;i<len;i++) { cin>>array[i]; } } void getHighest(float array[], int len, float& max) { int i; for(i=0;i<len;i++) { if(array[i]>max) max=array[i]; } }
6 有字面意义的下标——枚举类型作下标
枚举类型作下标,可增强代码可读性。
枚举类型变量可以与整型变量进行预算,结果为整型;
但整型变量不能直接赋值给枚举类型变量,需要强制类型转换。
7 利用typedef定义一维数组
举例:
typedef int Array[5]; Array arr; // 等价于 int arr[5];
注意:
Array是数据类型名,不是数组名,表示长度为5、元素类型为int的数据类型。
用Array定义的数据arr是长度为5、元素类型为int的一维数组。
8 结构数组
const int NUM=100; struct Date{ int year; int month; int day; }; struct StudentRec{ string name; int number; char gender; Date Birth; }; StudentRec EngClass[100];
cout<<EngClass[0].Birth.year<<" "<<EngClass[0].Birth.month<<" "<<EngClass[0].Birth.day<<endl;
9 对象数组
创建和使用对象数组与结构数组类似。
二、二维数组
1 声明
语法:
<数据类型> <数组名> [<整型常量表达式>][<整型常量表达式>];
注意:二维数组声明中的长度必须是常量。
2 使用
语法:
<数组名> [<整型表达式>][<整型表达式>]
使用二维数组元素和使用一般变量没有区别;
3 初始化
声明二维数组的同时,对其元素初始化;
每一行元素用一对花括号括住;
若初始化列表中给出所有元素的初始值,则声明时可省略对行数的说明;
若初始化列表中给出的初始化数目少于元素数目,则其余元素均初始化为0。
举例:
int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; // 等价于 int arr[][4]={...};
4 枚举类型作二维数组元素下标
可增强代码可读性;
5 二维数组的一般处理
处理通常有:遍历整个数组、处理行元素、处理列元素等;
举例:
#include <iostream> using namespace std; const int CITYS = 9; const int MONTHS = 12; int main() { float avgTemp[CITYS][MONTHS]; float sum = 0; int i, j; // 求二维数组中所有元素之和 for (i = 0; i < CITYS; i++) { for (j = 0; j < MONTHS; j++) { cin >> avgTemp[i][j]; sum += avgTemp[i][j]; } } cout << "The sum is:" << sum << endl; // 求二维数组每行之和 float sum1[CITYS] = { 0 }; for (i = 0; i < CITYS; i++) { for (j = 0; j < MONTHS; j++) { sum1[i] += avgTemp[i][j]; } } for (i = 0; i < CITYS; i++) { cout << "The sum of row " << i << " is: " << sum1[i] << endl; } // 求二维数组每列之和 float sum2[MONTHS] = {0}; for (j = 0; j < MONTHS; j++) { for (i = 0; i < CITYS; i++) { sum2[j] += avgTemp[i][j]; } } for (j = 0; j < MONTHS; j++) { cout << "The sum of col " << j << " is: " << sum2[j] << endl; } return 0; }
6 定义二维数组的其他方法
方法1:
typedef float MonthType[12]; MonthType avgTemp[9];
二维数组avgTemp可看成一维数组:该一维数组的元素分别是avgTemp[0...8],avgTemp[0...8]这9个元素都是长度为12的、元素类型为float的一维数组。
方法2:
把行数相等、列数相等、元素类型相同的二维数组堪称属于某种数据类型。
这样只需创建出这种数据类型,就可用其定义多个二维数组。
typedef float ArrayType[9][12]; ArrayType avgTemp;
其中,ArrayType是数据类型名,表示“行列数目分别为9、12的、数据类型为float的”形式的二维数组的数据类型;
用ArrayType定义的avgTemp等价于:
float avgTemp[9][12];
7 数组作函数参数
二维数组名可作实参;
被调用函数相应的形参必须给出第二维的长度(列数),且该长度必须与实参的列长度相等;
无需给出第一维的长度(行数),添加一个形参接收传递进来的行数。
const int CITYS=9; const int MONTHS=12; ... ... input(avgTemp,CITYS); // 调用input函数 ... ... void input(float array[][MONTHS], int row) // 定义input函数 {... ...}
const int CITYS=9; const int MONTHS=12; typedef float Array2D[CITYS][MONTHS]; typedef float Array1D[CITYS]; ... ... Array2D avgTemp; Array1D highest; getHighest(avgTemp,highest); ... ... void getHighest(Array2D array, Array1D largest) {... ...}注意两个例子之间的区别。
一、基本概念
指针,即地址,包含两方面信息:1 地址值;2 所指向数据的数据类型。
二、指针常量与指针变量
1 指针常量与取地址操作符&
C++提供取地址符&,可置于某变量名前,用以获取存放该变量的内存块地址。
举例:
int i;
注意:定义语句后,&i是i的地址,是指向int型变量i的指针,是一个指针常量。
2 指针变量的定义、运用及指针操作符*
2.1 指针变量的定义、运用
定义指针变量时不仅要说明该变量是指针变量,还要说明该指针变量所指向数据的数据类型。
语法:
<数据类型>* <指针变量名>; <数据类型> *<指针变量名>;
注意:在同一语句中定义多个指针变量时,应采用第二种形式。
2.2 指针操作符*
C++提供指针操作符*,置于指针变量前,获取该指针变量所指向的变量。
注意:
不要混淆定义指针变量的语句中的*与指针操作符*,前者说明是指针变量,后者用以获取指针所指向的变量;
一定先为指针变量赋值,使其指向某一确切的内存块后,才可使用指针操作符*访问该内存块。
3 直接访问、间接访问
从编译器的角度,间接访问是使用变量名访问变量的方式;直接访问是使用地址访问变量的方式。
4 (void*)类型的指针
一般情况下,指针包含两个信息:地址值和所指向数据的数据类型;
特殊地,指针只包含地址值,不包含所指向数据的数据类型,该指针就是指向void型的指针。
语法:
void* <指针变量名>;
一开始,把某段内存的起始地址赋值给指针变量,但所指向数据的数据类型尚未确定,需要在之后进行强制类型转换,得到指向某种确定类型的指针后才可使用。
三、指针的运用
1 修改指针变量的值,使之指向另一变量
指针变量是变量,其值可修改;若修改,则其指向另一变量。
#include <iostream> using namespace std; int main() { int a = 5, b = 9; int *p1, *p2, *p; p1 = &a; // p1指向变量a p2 = &b; // p2指向变量b p = p1; // p1与p2的值互换 p1 = p2; p2 = p; cout << a << "\t" << b << endl; cout << *p1 << "\t" << *p2 << endl; system("pause"); return 0; }2 指针变量作函数参数
2.1
#include <iostream> using namespace std; void swap(int* p1, int* p2) { int temp; temp = *p1; *p1 = *p2; *p2 = temp; } int main() { int a = 4, b = 5; int *p1, *p2; p1 = &a; p2 = &b; swap(p1, p2); cout << a << "\t" << b << endl; // 两个值交换 system("pause"); return 0; }
传递方式是值传递,即实参p1的值单向传递给形参p1,实参p2的值单向传递给形参p2,从而形参p1和p2的值分别是a和b的地址;
在swap函数中,p1和p2也指向a和b,*p1是a,*p2是b;而swap函数中的三条赋值语句实质是a和b的值对调。
注意:
main函数的实参p1与swap函数的形参p1是不同的两个变量,两者关系只存在于main调用swap时,实参p1的值传递给形参p1,之后就没有任何关系。
相当于向main函数返回多个结果值,这是指针做参数的优点。
2.2
#include <iostream> using namespace std; void swap(int* p1, int* p2) { int *temp; *temp = *p1; *p1 = *p2; *p2 =*temp; } int main() { int a = 4, b = 5; int *p1, *p2; p1 = &a; p2 = &b; swap(p1, p2); cout << *p1 << "\t" << *p2 << endl; system("pause"); return 0; }
运行该程序时,出现”内存访问非法“的错误,因为定义temp为指针变量后,没有赋值给它确切的地址值;
因此,temp随机指向内存空间中某个位置,即*temp为内存空间中某一不确定的内存块;
强行把a的值赋值给*temp,导致内存访问非法。
2.3
#include <iostream> using namespace std; void swap(int x, int y) { int temp; temp = x; x = y; y = temp; } int main() { int a = 4, b = 5; swap(a, b); cout << a << "\t" << b << < endl; // 两个值未交换 return 0; }
运行后,两个值未调换,因为实参a和形参x是两个不同的变量,两者唯一关系是在调用swap函数时,a的值单向传递给x,之后没有任何关系;
因此无论x在swap函数中怎么修改,都不会影响main函数中的a。
2.4
#include <iostream> using namespace std; void swap(int* p1, int* p2) { int *temp; temp = p1; p1 = p2; p2 = temp; } int main() { int a = 4, b = 5; int *p1, *p2; p1 = &a; p2 = &b; swap(p1, p2); cout << *p1 << "\t" << *p2 << endl; // 两个值未交换 system("pause"); return 0; }
形参p1和实参p2是两个不同的变量,因此在swap中形参p1与p2的值对调,对于实参p1和p2以及a和b都没有任何影响。
3 指针与引用
3.1 相同点
使一个函数向调用者传递多个结果值;
3.2 不同点
原理不同:对于引用传递参数,实参和形参是同一个变量;对于指针传递参数,被调用函数获取某变量的地址,从而使用该地址访问该变量;
使用引用形参比使用指针参数方便,因为使用指针时,要明确指针指到哪里;对于资深程序员,更喜欢指针。
#include <iostream> using namespace std; void swap(int& p1, int& p2) { int temp; temp = p1; p1 = p2; p2 = temp; } int main() { int a = 4, b = 5; int *p1, *p2; p1 = &a; p2 = &b; swap(p1, p2); cout << *p1 << "\t" << *p2 << endl; system("pause"); return 0; }
一、通过指针引用数组元素
1 数组名
数组名是地址常量(指针常量),是该数组的起始地址,或该数组第0个元素的地址。
如:
float score[5]; float* p = score; // 定义指向float型的指针变量,并把score赋值给该变量,则p指向score[0] float* p = &score[0]; // 也可把score[0]的地址&score[0]赋值给p
2 通过指针使用元素
2.1 下标法: a[i]
2.2 指针法: *(a+i) 或 *(++a)
二、数组作函数参数的进一步讨论
#include <iostream> using namespace std; const int NUM = 5; void sort(int* arr, int n) // 选择排序 { int i, j, k, t; for (i = 0; i < n - 1; i++) { k = i; // 找到[i...n-1]之间的最大元素,用k记录其下标 for (j = i + 1; j < n; j++) { if (*(arr + k) < *(arr + j)) k = j; } if (k != i) { t = arr[i]; arr[i] = arr[k]; arr[k] = t; } } } int main() { int *p, n[NUM]; for (p = n; p < n + NUM; p++) cin >> *p; sort(n, NUM); for (p = n; p < n + NUM; p++) cout << *p << " "; system("pause"); return 0; }
三、动态分配内存
定义数组时必须指定数组长度;
但可对内存进行动态分配,即在程序运行时根据实际需要分配内存空间。
1 malloc和free
int* p=(int*)malloc(len*sizeof(int)); free(p);
注意:
malloc函数返回值是void*型,无法用下标或指针法使用元素,也不能用指针作自增运算,因为这些运算需要知道指针所指向的数组的类型,因此需要把返回值转换为指向某种数据类型的指针。
malloc函数只负责分配len个字节的内存块,并返回该内存块的起始地址;对于该内存块内部如何划分为一个个元素,取决于把malloc的返回值转换为指向何种类型数据的指针。
2 new和delete
int* p=new int[len]; delete[] p;
注意:
使用new操作也可为存储单个数据分配内存块,则在数据类型后无需[len]部分;delete也不需要使用[]。
int* p=new int; delete p;
malloc和free一般用于简单数据类型的动态内存分配;对于对象的动态内存分配,则需要使用new和delete。
四、二维数组与指针
1 二维数组名与指向指针的指针
int a[3][4]={{1,2,3,4},{2,3,4,5},{3,4,5,6}};
a[i]和a都是指针,但指向的数据类型不同。
a[i]指向一个元素;a指向一行元素(准确地说,a指向一维数组)。
int (*p)[4]; // 括号必不可少 p=a;
定义p为指向“由4个int型元素组成的一维数组”的指针。
cout<<p[1][2]<<endl; cout<<*(*(p+1)+2)<<endl;
2 动态分配内存
#include <iostream> using namespace std; void input(float** score, int StuNum, int CouNum) { int i, j; for (i = 0; i < StuNum; i++) { for (j = 0; j < CouNum; j++) { cin >> score[i][j]; } } } void average(float** score, int StuNum, int CouNum, float* avg) { int i, j; float sum; for (i = 0; i < StuNum; i++) { sum = 0; for (j = 0; j < CouNum; j++) { sum += score[i][j]; } avg[i] = sum / CouNum; } } void print(float* avg, int StuNum) { int i; for (i = 0; i < StuNum; i++) { cout << avg[i] << endl; } } int main() { float** score; float* avg; int StuNum, CouNum, i; cin >> StuNum >> CouNum; score = new float*[StuNum]; // 分配行指针内存 for (i = 0; i < StuNum; i++) // 分配每行元素内存 { score[i] = new float[CouNum]; } avg = new float[StuNum]; input(score, StuNum, CouNum); average(score, StuNum, CouNum, avg); print(avg, StuNum); for (i = 0; i < StuNum; i++) // 释放每行元素内存 delete[] score[i]; delete[] score; // 释放行指针内存 delete[] avg; system("pause"); return 0; }
注意:
动态分配二维数组内存空间需要两步:分配StuNum个元素,每个元素是指向float型数据的指针;循环里动态分配StuNum个一维数组,每个数组长度都是CouNum。
score是float**型变量,即score是一个指向指针的指针变量,指向一个一维的指针数组。
其中每个元素是一个float*型的指针,指向一个元素为float型的一维数组。