The C++ Programming Language 第五章

The C++ Programming Language 第五章 学习笔记
james chen/050220


*********************
5.1指针
*********************

对于类型T,T*就是"到T的指针",也就是说,一个类型为T*的变量能保存一个类型为T的对象的地址。
char c='a';
char *p=&c;                                               //将c的地址送给char指针p
cout<<c<<","<<*p<<endl;                          //两个值一样
*p='h';                                                        //间接修改了c,因为p=&c,(*p='h')==(c='h')
cout<<c<<endl;                                           //h

很郁闷的是:到T*、函数的指针,指针数组,玩起来更复杂,哎,go:

一、到T*的指针
  [到(T类型的指针)的指针],这样理解可能要好一些。

int i=123;
int *p1=&i;
//int** p2=p1;                                           //错,p1只有一个*,故不能赋值
int** p2=&p1;                                       //正确,取p1指针的地址送给p2,相当于int**,刚好与p2匹配
cout<<&i<<endl;  
cout<<p1<<endl;                                       //p1和i地址相同
cout<<&p1<<endl;                               //&p1为p1指针的地址,与p1不同,p1是所指对象i的地址
cout<<p2<<endl;                                       //&p2为p2指针的地址,与p1相同
//cout<<*p2<<endl;                                      //错,*p2只是取得p2指针所指对象的地址
cout<<*(*p2)<<endl;                                  //正确,取得p2指针所对对象的值
                                                                    //也可以写成**p2
**p2=321;                                               //给p3指针所指的指针赋值
cout<<i<<endl;                                           //321
cout<<*p1<<endl;                                      //321

延伸学习:
指向T*的指针的指针的指针的......
int a=123;
int *b=&a;
int **c=&b;
int ***d=&c;
int ****e=&d;
int *****f=&e;
*****f=54321;                                       //54321
cout<<a<<endl;                                       //54321
cout<<*b<<endl;                                   //54321
cout<<**c<<endl;                                  //54321
cout<<***d<<endl;                                 //54321
cout<<****e<<endl;                              //54321
cout<<*****f<<endl;                              //54321
    //爽啊,原来是这样的,爽够了该继续了。。。

二,指针数组,即数组元素为指针。

int* a[10];                                               //声明一个数组,内含10个元素,为int型指针
for(int i=0;i<10;i++)
a[i]=new int(100+i);                                  //申请int型内存空间,并赋值100+i,再将地址送给a[i]
for(i=0;i<10;i++)
cout<<a[i]<<endl;                                      //显示数组每个元素(指针)的地址
for(i=0;i<10;i++)
cout<<*a[i]<<endl;                                  //显示值,为100,101,102,103....109

三,指针与函数

1,指针作为函数的参数
void hehe(int *x)
{*x+=100;}
int a=23;
hehe(&a);
cout<<a<<endl;                                        //123;
int *b=new int(a);                                      //申请一内存空间,赋值为a
hehe(b);
cout<<*b<<endl;                                       //223;

2,指针型函数,即返回一个指针
int *haha(int *x)
{*x+=100;return x;}
int a=1;
int *b=haha(&a);                                          //将返回的地址送给*b
cout<<*b<<endl;                                           //101
cout<<haha(b)<<endl;                              //0x00....显示返回指针地址,在这里与b地址相同
cout<<*b<<endl;                                       //201
cout<<*haha(b)<<endl;                              //301
int c=*haha(b);                                           //将返回地址取值*haha(b),再将值送给int变量
cout<<c<<endl;                                       //401

3.指向函数的指针
就是说将一个函数的首地址赋值给一个函数指针,然后即可用此函数指针操作函数。。

int (*add)(int a);
int add1(int a){return a*10;}
int add2(int a){return a*100;}
int add3(int a){return a*1000;}
add=add1;
int i=2;
int a=add(i);
add=add2;
int b=add(i);
add=add3;
int c=add(i);
cout<<a<<","<<b<<","<<c<<endl;                 //20,200,2000,

延伸学习:
函数指针数组,主要是觉得每次赋值太过累赘
int add1(int a){return a*10;}
int add2(int a){return a*100;}
int add3(int a){return a*1000;}
int (*add[3])(int a)={add1,add2,add3};         //在声明时就将add1,add2,add3压进去
for(int i=0;i<3;i++)
cout<<add[i](2)<<endl;                                   //20,200,2000

注意:函数指针和函数指针数组都只是指针,不是函数,所以千万不要搞个函数体画蛇添足。

//爽啊,这可以俺自己试出来的哦,至少现在俺看过的书上还没有这种玩法,当然,肯定是自己看书太少。


*********************
5.2数组
*********************

数组的初始化
int a[3]={1,2,3};                                       //正确
//int a[3]={1,2,3,4};                                   //错误,数组a只有三个元素
int a[5]={1,2};                                            //正确,相当于int a[5]={1,2,0,0,0};
int a[]={1,2,3,4,5,6};                               //正确
a[]={1,2,3,4};                                        //错误,给一个不存在的数组赋值


5.2.2字符串文字量

字符串文字量是用两个双引号括起来的字符序列,如:
"How are you!!"

一个字符文字量的实际长度要比它所包含的字符个数要多一个,因为它总是由'/0'结尾,如:
szieof("love")==5

字符串文字量的类型是“适当个数的const字符的数组”,所以"love"的类型其实就是const char[5]。
可以用字符串文字量给一个char*赋值,但不能用这个char*去修改字符串文字量,如:
char *a="how are you!!";
cout<<a<<endl;                                                //how are you!!
//a[0]='a';                                                            //错,给常量赋值,但编译器不会提示

记住:把字符串文字量当成常量。

如果想要修改字符串,就需要将它复制到一个char数组里:
char a[]="how are you!!";
cout<<a<<endl;                                                //how are you!!
a[3]='a';
cout<<a<<endl;                                                    //howaare you!!

字符串文字量是静态分配的,所以可以让函数返回:
const char* aa()
{return "haha";}
const char *b=aa;
cout<<b<<endl;                                                //haha

为了使程序整洁,可以用空白字符将长的字符串分成几个部分,如:
char *p="aaaaaa"
 " bbbbbb"   " ccccc";
cout<<p<<endl;                                                //aaaaaa bbbbbb ccccc

 

*********************
5.3指针与数组
*********************

在c++中,指针与数组密切相关。一个数组的名字即是该数组的首地址,也就可以赋值给同类型的指针。
char x[10]="aaabbbccc";
char* a=x;                                                       //将x的首地址赋值给*a
char* b=&x[3];                                               //将x的第4个元素的地址赋值*b
char* c=&x[6];                                               //将x的第7个元素的地址赋值*c
cout<<a<<endl;                                               //aaabbbccc
cout<<b<<endl;                                               //bbbccc
cout<<c<<endl;                                               //ccc

5.3.1:在数组里漫游

访问数组可通过下标与指针两种方式:
char a[5]="abcd";
for(int i=0;i<4;i++)                              //i<4亦可换成*b!=0,结束符为0
cout<<a[i]<<endl;                              //用下标遍历数组

char *b=a;
for(i=0;i<4;i++)                                  //i<4亦可换成*b!=0,结束符为0
cout<<*b++<<endl;                          //用指针遍历数组 

char *c=a;
while(cout<<*c++<<endl,*c!=0);     //用指针遍历数组

只有当两个指针指向同一数组元素时,指针之间相减才有意义。如果从一个指针减去另一指针,结果就是这两个指针之间的数组元素个数(一个整数)。指针加或减一个整数,得到的仍是一个指针,如果运算所得指针地址超出数组边界,那这个值是无定义的。

int a[10];
int b[10];
int i=&a[5]-&a[2];  
cout<<i<<endl;                                   //3,间隔3个元素
i=&a[5]-&b[2];
cout<<i<<endl;
int* p=a+2;                                           //p=a[2];
p=a-2;                                                //出界,*p没定义

郁闷的是,这个出界的指针居然可以使用,怪哉:
cout<<*p<<endl;                               //随机值
*p=4321;
cout<<*p<<endl;                               //4321


*********************
5.4常量
*********************

常量即不变化的值,一经声明,便可直接使用,但不能修改。
常量声明时必须初始化。

const int a=123;
const int b[]={4,3,2,1}; 
const int c;                                       //出错,未初始化
a=200;                                            //出错,常量不能修改
b[2]=34;                                           //出错,常量不能修改

常量主要用在一些初始化后便不用改变的值,如:
pi圆周率,固定值,不用改变。
一些对象的值。
函数形参,只读不写。
int add100(const int a)
{
a+=100;                                        //出错,不能改变a的值
return a+100;                                 //OK,未改变a的值
}


5.4.1:指针和常量

使用一个指针时涉及到两个对象:指针本身和所指针对象。

const * 将使对象成为常量。                //个人理解为:指针常量
* const 将使指针成为常量。            //个人理解为:常量指针

例:
int i=100;
int o=123;

//const *
int const *p=&i;                            //使对象成为常量
cout<<*p<<endl;                          //100
*p=123;                                       //错,对象为常量,不能赋值
p=&o;                                           //OK,指针不是常量,可以改变地址
cout<<*p<<endl;                          //123

//* const
int *const p1=&i;                        //常指针
cout<<*p1<<endl;                        //100
*p1=123;                                    //OK,对象非常量,可以改
cout<<*p1<<endl;                        //123
p1=&o;                                        //错,指针为常量,不能改

const int *const p2=&o;                //到const的const指针
*p2=33;                                        //错,p2指向的是常量
p2=&i;                                           //错,p2本身也是常量
cout<<*p2<<endl;                        //123


*********************
5.5引用
*********************

引用就是对象的另一个名字。引用的主要用途是为了描述函数的参数和返回值,还有运算符重载。

int a=123;
int& b=a;
int c=b;
cout<<a<<","<<b<<","<<c<<endl;         //123,123,123
b=234;
cout<<"a:"<<a;                                        //234,因为b引用了a,b=234就相当于a=234

一个引用的地址在初始化后就不能改变了,它总是指向初始化时所指的那个对象。
所以用作形参会很爽。

注意:不能对一个引用声明时赋值。
int &m=234;                                         //错,常规引用需要一内存单元地址,故不能直接赋值。

但可以用const T&来在声明引用时直接赋值:
const int &m=234;
const T&的实际过程:
1,右值到T类型的隐式转换。
2,将右值放到一个T类型的临时变量中。
3,将此临时变量地址作为初始式的值。
这个临时变量一直存在,直到这个引用的作用域结束。

Bjarne Stroustrup忠告:
.为了提高程序的可读性,通常应该尽可能避免让函数去修改它们的参数。
.如果将“普通”引用参数用于某些函数,那么这些函数的名字就应该给出其引用参数将被修改的强烈提示。


*********************
5.6指向void的指针
*********************

一个指向任何对象类型的指针都可以赋值给类型为void*变量,void*可以赋值给另一个void*,两个void*可以比较相等与否,而且可以显式地将void*转换为另一类型。

int *p=new int(123);
void *a=p;                                           //赋值给void指针
void *b=a;                                           //void指针赋值给另一void指针
cout<<a<<endl;
cout<<b<<endl;
cout<<p<<endl;                                   //三个地址都一样
cout<<*a<<endl;                                   //出错,不能间接引用
a++;                                                    //出错,不能增量,不知道a的大小
int *t=a;                                               //出错,不能隐式转换
int *t=static_cast<int*>(a);                     //OK,可以显式转换回int

void*的最重要的用途是需要向函数传递一个指针,而又不能对对象的类型做任何假设。还有就是从函数返回一个无类型的对象,当然,要使用该对象,必须显示类型转换。

/*
个人理解:将一个对象赋值给void指针后,可以看到两个地址都一样,我想应该可以理解为,void指针是该对象的一个引用,只是类型不明而已,对这个void指针不能使用*,++等取值操作,否则引起错误。
*/


*********************
5.7结构
*********************

数组是相同元素的聚集,而stuct则是任意类型元素的一个聚集。
struct address
{
char *name;
int id;
};

对struct的变量声明和其他类型一样:
address a;
a.name="i love bb!!";
a.id=4321;
对结构类型变量的初始化也可以与数组一样:
address b={"hehe",321};

结构型指针的使用:
address *c=&a;
cout<<c->name<<endl;                              //i love bb!!
cout<<c->id<<endl;                                      //4321
cout<<(*c).name<<endl;                              //i love bb!!
cout<<(*c).id<<endl;                                  //4321
address d=a;                                               //struct赋值给struct
d=*c;                                                        //struct*赋值给struct

在对struct类型指针的使用时,指针->成员  ==  *指针.成员

*********************
5.8 BS的忠告
*********************

1.避免非平凡的指针技术。
2.当心,不要超出数组的界线去写。
3.尽量使用0而不是NULL。
4.尽量使用vector和valarray而不是内部数组。
5.尽量使用string而不是以0结尾的char数组。
6.尽量少用普通的引用参数。
7.避免void*,除了在某些低级代码里。
8.避免在代码中使用非平凡的文字量("神秘的数"),应该定义和使用各种符号常量。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xchenbb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值