《C++语言程序设计基础》学习之数组指针与字符串
数组元素在内存中顺次存放,它们的地址是连续的。元素间物理地址上的相邻,对应着逻辑次序上的相邻。
数组名字是数组首元素的内存地址,数组名是常量,不能被赋值
数组名是数组的指针,指向首个元素的地址,多维数组的n-1维是指针
一维数组初始化:
在定义数组时给出数组元素的初始值:列出全部元素的初始值 例如:static int a[10]={0,1,2,3,4,5,6,7,8,9};
可以只给一部分元素赋初值 例如:static int a[10]={0,1,2,3,4};
在对全部数组元素赋初值时,可以不指定数组长度 例如:static int a[]={0,1,2,3,4,5,6,7,8,9}
二维数组初始化:
将所有初值写在一个{}内,按顺序初始化 例如:static int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12
分行列出二维数组元素的初值 例如:static int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
可以只对部分元素初始化 例如:static int a[3][4]={{1},{0,6},{0,0,11}};
列出全部初始值时,第1维下标个数可以省略
例如:static int a[][4]={1,2,3,4,5,6,7,8,9,10,11,12}; 或:static int a[][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
如果不作任何初始化,内部auto型数组中会存在垃圾数据,static数组中的数据默认初始化为0;
如果只对部分元素初始化,剩下的未显式初始化的元素,将自动被初始化为零
int main(){
int f[20] = { 1,1 };
for (int i = 2; i < 20; i++)
f[i] = f[i - 2] + f[i - 1];
for (int i = 0; i < 20; i++) {
if (i % 5 == 0) cout << endl;
cout.width(10); //设置cout的宽度从右侧开始填充
cout << f[i];
}
return 0;
}
数组的指针作为函数的参数时,函数中改变数组,原数组也会被改变,因为传递的是指针,如果不希望函数中改变数组,可以使用const修饰指针,传递常数组给函数。
void rowSum(int a[][4], int nRow) { //const int a[][4]
for (int i = 0; i < nRow; i++) {
for (int j = 1; j < 4; j++)
a[i][0] += a[i][j];
}
}
int main(){
int table[3][4] = { {1,2,3,4},{2,3,4,5},{4,5,6,9}};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++)
cout << table[i][j] << " ";
cout << endl;
}
rowSum(table, 3);//调用子函数,计算各行和
//输出计算结果
for (int i = 0; i < 3; i++)
cout << "Sum of row " << i << "is " << table[i][0] << endl;
return 0;
}
对象数组:数组是由同类型的数据的集合,同样的,对象是自定义类的实例,可以由对象来构成对象数组,数组成员是类的对象
定义对象数组:类名 数组名[元素个数];
访问对象数组元素,通过下标访问:数组名[下标].成员名
对象数组的初始化:
数组中每一个元素对象被创建时,系统都会调用类构造函数初始化该对象。
通过初始化列表赋值。例:Point a[2]={Point(1,2),Point(3,4)};
如果没有为数组元素指定显式初始值,数组元素便使用默认值初始化(调用默认构造函数)。
数组函数所属类的构造函数:
元素所属的类不声明构造函数,则采用默认构造函数。
各元素对象的初值要求为相同的值时,可以声明具有默认形参值的构造函数。
各元素对象的初值要求为不同的值时,需要声明带形参的构造函数。
当数组中每一个对象被删除时,系统都要调用一次析构函数。
Point.h
#ifndef _POINT_H
#define _POINT_H
class Point {//类的定义
public:
Point();
Point(int x, int y);
~Point();
void move(int newX, int newY);
int getX() const { return x; } //常对象调用常函数成员
int getY() const { return y; }
static void showCount();//静态函数成员处理静态数据成员
private:
int x, y;
};
#endif // ! _POINT_H
Point.cpp
#include "pch.h"
#include <iostream>
#include"Point.h"
using namespace std;
Point::Point() :x(0), y(0) {
cout << "Default Constructor called." << endl;
}
Point::Point(int x, int y) : x(x), y(y) {
cout << "Constructor called." << endl;
}
Point::~Point() {
cout << "Destructor called." << endl;
}
void Point::move(int newX, int newY) {
cout << "Moving the point to(" << newX << ", " << newY << ")" << endl;
x = newX;
y = newY;
}
main.cpp
#include"pch.h"
#include"Point.h"
#include<iostream>
using namespace std;
int main() {
cout << "Entering main..." << endl;
Point a[2]; //对象数组调用默认构造函数
for (int i = 0; i < 2; i++)
a[i].move(i + 10, i + 20); //数组名下标使用
cout << "Exiting main..." << endl;
return 0;
}
运行的结果://先调用默认构造函数然后调用move函数最后调用析构函数
Entering main...
Default Constructor called.
Default Constructor called.
Moving the point to(10, 20)
Moving the point to(11, 21)
Exiting main...
Destructor called.
Destructor called.
数组的遍历形式,可以通过sizeof(array) / sizeof(数据类型)求得数组长度,也可以使用sizeof(array) / sizeof(array[0]),是相同的方法。
int main(){
int array[3] = { 1,2,3 };
int *p;
//sizeof(array) / sizeof(int)=3
//++p每次的步长是sizeof(int)
for (p = array; p < array + sizeof(array) / sizeof(int); ++p){
*p += 2; //对元素的值加2
std::cout << *p << std::endl;
}
return 0;
}// 3,4,5
c++11 有基于范围的for循环,自动遍历整个容器,数组也是容器
int main(){
int array[5] = { 1,2,3,4,5 };
for (int & e : array){
e += 10; //e就是数组元素的值 对每个数组值加10
std::cout << e << std::endl;
}
return 0;
} //11 12 13 14 15
内存空间的访问方式:通过变量名访问;或者通过地址访问
指针的概念:指针:内存地址,用于间接访问内存单元;指针变量:用于存放地址的变量
指针变量的定义:
例:static int i;//静态变量 static int* ptr = &i; //ptr是指针变量,ptr的值是指针即i的内存地址,定义指针并且赋值 &i是地址
-
例:*ptr = 3;
//ptr是指针变量,ptr的值是指针即i的内存地址,ptr的值是变量i的内存空间起始地址,*ptr是变量i存储的值
* 是指针运算符: int a; int* ptr; ptr=&a; //*ptr==a True
& 是地址运算符:int a; int* ptr; ptr=&a;
指针的初始化和赋值:
不要用一个内部非静态变量去初始化 static 指针
向指针变量赋的值必须是地址常量或变量,不能是普通整数,例如:
通过地址运算“&”求得已定义的变量和对象的起始地址 //int a; int* ptr =&a;
动态内存分配成功时返回的地址 //new
例外:整数0可以赋给指针,表示空指针。 int* ptr=0;
int main() {
int i; //定义int型数i
int *ptr = &i; //取i的地址赋给ptr
i = 10; //int型数赋初值
cout << "i = " << i << endl; //输出int型数的值
cout << "*ptr = " << *ptr << endl; //输出int型指针所指地址的内容
return 0;
}
允许定义或声明指向 void 类型的指针。该指针可以被赋予任何类型对象的地址。但不能通过void指针访问变量
例: void *general;
int main() {
//!void voidObject; 错,不能声明void类型的变量
void *pv; //对,可以声明void类型的指针
int i = 5;
pv = &i; //void类型指针指向整型变量
int *pint = static_cast<int *>(pv); //void指针转换为int指针
cout << "*pint = " << *pint << endl;
//cout << *pv;会报错,不能通过void类型指针访问变量
return 0;
}
指向常量的指针:
不能通过指向常量的指针改变所指对象的值,但指针本身可以改变,可以指向另外的对象。
int a;
const int *p1 = &a; //p1是指向常量的指针
int b;
p1 = &b; //正确,p1本身的值可以改变
*p1 = 1; //编译时出错,不能通过p1改变所指的对象
指针类型的常量:
若声明指针常量,则指针本身的值不能被改变。
int a;
int * const p2 = &a;
p2 = &b; //错误,p2是指针常量,值不能改变
C++11使用nullptr关键字,是表达更准确,类型安全的空指针
指针类型的算术运算:
指针p加上或减去n,其意义是指针当前指向位置的前方或后方第n个数据的起始位置。
指针的++、--运算 意义是指向下一个或前一个完整数据的起始,其跨度是数据类型的sizeof
运算的结果值取决于指针指向的数据类型,总是指向一个完整数据的起始位置。
当指针指向连续存储的同类型数据时,指针与整数的加减运和自增自减算才有意义。
a是数组的首地址,*(pa+1)的跨度是sizeof(short)。
指针可以和零之间进行等于或不等于的关系运算。 例如:p==0或p!=0 用于判断指针p是否是空指针