《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是地址

《C++语言程序设计基础》学习之数组指针与字符串

  • 例:*ptr = 3; 
    //ptr是指针变量,ptr的值是指针即i的内存地址,ptr的值是变量i的内存空间起始地址,*ptr是变量i存储的值

《C++语言程序设计基础》学习之数组指针与字符串

* 是指针运算符: 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)。
《C++语言程序设计基础》学习之数组指针与字符串

指针可以和零之间进行等于或不等于的关系运算。   例如:p==0p!=0    用于判断指针p是否是空指针