数组、指针和字符串
刚刚结束了第六章的学习,这里结合一下老师发的ppt和大纲外加教材来小小的自我总结一下
第六章 数组、指针和字符串
6.1数组
- 数组的初始化时,第一维的下标个数可以不用显式说明:
int a[2][3]={1,2,3,4,5,6};
int a[][3]={1,2,3,4,5,6};
上面两种等价。
- 数组作为函数参数
1 void test(int a[][4],int r){
2 a[0][0]=10;
3 }
4 int main(){
5 int b[3][4]={1};
6 test(b,3);
7 }
与普通的函数参数不一样,这个时候是传递了数组的地址,所以函数中进行的改变,实际数组的元素也会发生改变
- 对象数组
声明和访问形式
类名 数组名[下标表达式];
数组名[下标表达式].成员名
例如:
location a[2]={location(1,2),location(3,4)};
cout<<a[1].x<<","<<a[1].y;
6.2指针
指针这里越过基本概念,大概知道指针是通过地址指向目标就行,通过实敲代码来理解最好.
我把几乎所有知识点都敲到了一个代码里面,下面的各段代码有时候需要结合起来看。
6.2.3 * &
* 是指针运算符,也是解析
&是取地址运算符
6.2.4.1 指针的赋值
1 int a;
2 int b[]={99};
3 /* int *ptr;
4 ptr=&a;
5 */
6 int *ptr=&a;//这两种定义的效果相同,
7 //ptr是储存的地址,只有定义的时候可以这样定义,改变的时候要把地址给ptr,值给*ptr
8
9 int *p=b;//数组名称实际上是一个指针常量,所以可以直接给
10 //这种方式也可以
11 /* int *p; p=b; */
12
13 a=10;
14 cout<<"a="<<a<<endl;
15 //*ptr++;//这样得出来的指针所指的值不对,a的值也没有改变
16 *ptr+=1;//这样以后指针对应的a的值改变了
17 cout<<"a="<<a<<endl;
18 cout<<"*ptr="<<*ptr<<endl;//输出的是地址上的那个值
19 cout<<"ptr="<<ptr<<endl;//输出的只是地址
20 cout<<"*p="<<*p<<endl;
21 cout<<"p="<<p<<endl;
6.2.4.2 const相关的指针
/*
-----------------------------------------------------------------------------------
*/
cout<<endl<<"----------常指针知识点:------------"<<""<<endl;
/*注意区分指向常量的指针 和 指针类型的常量*/
const int *p1=&a;//指向常量的指针,代表地址的p1本身可以改变,而指向的值不能改变 ;弱常指针
int *const p2=&a;//指针类型的常量,代表地址的p2本身是常量不能改变,而指向的值依然是变量; 强常指针
int c=108;
cout<<"*p1="<<*p1<<" a="<<a<<endl;
p1=&c;
cout<<"*p1="<<*p1<<" a="<<a<<" c="<<c<<endl;
/* *p1=110; 此句话会出错,因为这是指向常量的指针*/
/* p2=&c;此句话也会出错,这是指针类型的常量,指针本身所指向的地址不能改变,此时地址还是a的地址*/
cout<<"*p2="<<*p2<<" a="<<a<<endl;
*p2=19;
cout<<"*p2="<<*p2<<" a="<<a<<endl;
6.2.4.3 void类型的指针
1 /*
2 -----------------------------------------------------------------------------------
3 */
4 cout<<endl<<"----------void类型指针------------"<<""<<endl;
5 void *pv;//但是可以声明void类型的指针
6 //void voidobject 不能声明void类型的变量
7 int i=5;
8 pv=&i;
9 int *pi=static_cast<int *>(pv);
10 //这是强制转换类型,把void指针转换为int指针赋值给pi
11 cout<<"*pi="<<*pi<<endl;
12 cout<<" pi="<<pi<<endl;
13 cout<<" pv="<<pv<<endl;//pi pv地址一样,只是指针类型不一样
14 //cout<<"*pv="<<*pv<<endl;
15 //任何类型的指针都可以赋值给void类型的指针变量
16 //void类型指针一般只在指针指向的数据类型不确定时候使用
6.2.5指针运算
1 /*
2 -----------------------------------------------------------------------------------
3 */
4 cout<<endl<<"----------指针的运算------------"<<""<<endl;
5 int *pp;
6 /* pp+n; 指针pp后方第n个数的地址
7 pp++;指针pp后方第1个数的地址
8 pp--; 指针pp前方第1个数的地址
9 */
10 //*(pp+1)这是pp指向位置后第一个数的内容
11 //等同于 pp[1] *(pp-1) = pp[-1]
12 //空指针
13 pp=0;
14 pp=NULL; //空指针不指向任何地址
15 /*
16 同类型的指针可以做关系运算 > < >= <= == !=
17 可以与空指针(0 NULL )做关系运算
18 多个关系运算可以构成逻辑运算
19 */
20 int ap[5]={0,1,2,3,4};
21 pp=ap;int *qq=(ap+3);
22 cout<<"qq-pp="<<qq-pp<<endl;//指针做减法运算,qq到pp相隔了三个整型数据
23 //cout<<"qq+pp="<<qq+pp<<endl;没法做加法运算
24 cout<<"ap[0]=*pp="<<*pp<<endl;
25 (*pp)++;
26 cout<<"ap[0]=*pp="<<*pp<<endl;
27
28 ap[1]==*pp++;
29 ap[2]==*(pp++);//*pp++ *(pp++)等效,
30 //因为++后置优先级更高,都是先pp++ ,地址变化,再取指针
31 ap[2]==*qq--;
32 cout<<"pp="<<pp<<" qq="<<qq<<" (ap+2)=&ap[2]="<<&ap[2]<<endl;
33 cout<<"*pp="<<*pp<<" *qq="<<*qq<<" ap[2]="<<ap[2]<<endl;
34
- 举例子例如定义了*p=&a;
p只是储存了a的地址,*p才是a所代表的值,p++或者对p进行运算都不能改变a,只是改变*p所指向的值。
这个地址的运算在数组里面有大用处,因为数组元素的地址是连续的。
- 空指针的地址是0或者NULL
6.2.6 用指针处理数组元素
1 /*
2 -----------------------------------------------------------------------------------
3 */
4 cout<<endl<<"----------指针处理数组元素------------"<<""<<endl;
5 int array[5]={0,1,2,3,4};
6 //array == &array[0] == &array 数组名其实就是一个指针变量
7 //array+3 == &array[3]
8 *array==array[0];
9 *(array+1)==array[1];
10 for(int *pa=array;pa<(array+5);pa++){
11 // for(int *pa=&array[0];pa<&array[5];pa++)与上一行等价
12 /* pa指向了数组每个元素的地址,数组的地址是连续的,
13 所以是小于array【5】的地址,pa++是依次后移一个位置 */
14 cout<<*pa<<' ';
15 }cout<<endl;
- 教材在6.2.4就讲过,数组的名称实际上就是一个不能被赋值的指针,即指针常量,为上文提到的强常指针(不能改变地址,但是可以通过指针改变所指向的值)
array=&array=&array[0] 数组名这个指针指向了第一个元素(挺重要的,之后运用挺多)
array+i=&array[i] 数组的运算
6.2.7指针数组
1 /*
2 -----------------------------------------------------------------------------------
3 */
4 cout<<endl<<"----------指针数组------------"<<""<<endl;
5 int l1[]={1,0,0};
6 int l2[]={0,1,0};
7 int l3[]={0,0,1};
8 int *ps[3]={l1,l2};
9 ps[2]=l3;
10 cout<<"ps[0]="<<ps[0]<<" *ps[0]="<<*ps[0]<<endl;
11 cout<<"l1="<<l1<<" &l1="<<&l1<<endl;
12 cout<<"ps[0][0]="<<ps[0][0]<<" l1[0]="<<l1[0]<<endl;
13 cout<<"&ps[0][0]="<<&ps[0][0]<<" &l1[0]="<<&l1[0]<<endl;
14 //ps[0]=l1=&l1 ps[1]=l2=&l2;
15 cout<<"*(ps[0]+1)="<<*(ps[0]+1)<<" l1[0+1]="<<l1[1]<<endl;
16 //ps[i][j] == *(ps[i]+j)
17 int array2[3][3]={{11,12,13},{21,22,23},{31,32,33}};
18 //*(*(array2+i)+j) == array2[i][j]
数组指针并不是很难,
实际就是一个数组,只不过每一个元素都是指针。
语法形式为:
数据类型 *数组名[下标表达式]
只不过要注意区分数组指针和指针数组的概念(此知识点之后补上)
6.2.8 用指针作为函数参数
1 /*
2 void fun1(int a,int b);
3 void fun2(int *c,int *d);
4 int p,q,e[],f[];
5 fun1(p,q);
6 fun2(&p,&q);
7 fun2(e,f);
8 //fun1不会改变变量本来的值,变的只是形参,fun2的形参是指针,会改变变量。
9 */
6.2.9 指针型函数
语法形式
数据类型 *函数名(参数表){
函数体;
}
基本没有深入讲相关知识点,只是提了一下,所以这里也一笔带过
6.2.10 指向函数的指针
1 void print(double ){
2 cout<<"A test"<<endl;
3 }
4 void write(double data){
5 cout<<"this test is "<<data<<endl;
6 }
7 double message(double data){
8 return data;
9 }
10 class node{
11 public:
12 int n,m;
13 node(int n1){
14 n=n1;
15 };
16 void print();
17 private:
18 int b;
19 };
20 void node:: print(){
21 cout<<"6666"<<endl;
22 }
23 int main(){
24 cout<<endl<<"----------指向函数的指针-----------"<<""<<endl;
25 //数据类型 (*函数指针名)(形参表)
26 //函数指针名=函数名; 函数指针名=&函数名 两种用法相同
27 void(*pf1)(double);
28 double(*pf2)(double);
29 print(3.14);
30 pf1=&print;
31 pf1(3.14);
32 pf1=&write;
33 pf1(3.14);
34 //pf1=&message; 有错误,这是void类型的函数指针
35 pf2=message;
36 cout<<pf2(3.14)<<endl;
语法形式:
数据类型 (*函数指针名)(形参表)
这里还涉及到了typedef 这个好像在之前章节讲过,还没看,先放这,一会复习前面章节再补上
6.2.11.1对象指针
感觉和数据指针函数指针这些都大同小异
用途:多态的实现
Ps:定义对象的时候会调用类的构造函数,而指针不会。
可以通过指针来调用对象的成员,不过有特殊形式
对象指针名->成员名
Or
(*对象指针名).成员名
6.2.11.2this 指针
这个指针是直接隐藏于类的每一个非静态成员函数里面的
主要是知识点,教材上也没有实际运用
据说函数形参名称于类的数据成员名称一样,可以用这个区分
大概是
Class node{
Public:
Int n;
Void print(int n){
This->n=n;}
}
6.2.11.3指向类的非静态成员指针(指向成员的指针)
不考这个知识点,暂时空出来以后再说。
6.3动态内存
1 class node{
2 public:
3 node(){
4 cout<<"调用了默认构造函数"<<endl;
5 }
6 node(int x,int y){
7 this->x=x;
8 this->y=y;
9 cout<<"调用了构造函数"<<endl;
10 }
11 ~node(){
12 cout<<"调用了析构函数"<<endl;
13 }
14 void move(int newx,int newy);
15 private:
16 int x,y;
17 };
18
19 inline void node::move(int newx,int newy){
20 x=newx;y=newy;
21 }
22
23 int main(){
24 int *p1=new int(6);
25 int *p2=new int();//int *p2=new int;
26 cout<<"p1="<<p1<<" *p1="<<*p1<<endl;
27 delete p1;
28 delete p2;
29 *p1=1;//delete删除了指向的内存空间,指针本身还在
30 node *pn;
31 pn=new node;
32 delete pn;
33 pn=new node(1,1);
34 delete pn;
35 cout<<"----------申请多个--------"<<endl;
36 //申请多个动态对象数据
37 node *p=new node[2];
38 p[0].move(1,2);
39 p[1].move(3,4);
40 delete []p;
41 return 0;
42 }
建立和删除堆对象使用运算符 new 和 delete
(在程序里面用了一下this指针,没有出错,真好)
6.5深复制和浅复制
啊这部分文字好多(巨懒)
咕咕咕
6.6 字符串
- string是c++库黎明预定义的类,可以直接使用
- 字符串是一个字符型的数组,以 \0 结尾
- 字符串之间的大小比较:字典序
- 读入:cin getline
cin读入字符串会以空格 tab制表符 回车作为分隔符
getline读入一整行,以回车作为分隔符
getline(cin,字符串名);
getline(cin,字符串名,分隔符); getline(cin,s1,',');
还有gets get cin.get cin.getline getchar scanf等很多读入方式,之后来做个整合。
#6.4 vector(老师未讲)
定义形式:
vector<元素类型>数组对象名(数组长度);
调用方式和数组一样的 a[i],比较有用的是里面的size
a.size()就是数组a的大小
课本没有讲太多的用法
诸如此类还有 string pair vector map 等,也等以后一同梳理(咕咕咕