文章目录
第四章 数组字符串指针
基于范围的for 循环
int a[]={1, 14, 4 ,4 ,78};
for(auto i:a)
{
}
字符数组
字符串数组的特殊性
char str[45]="this is a string";
char* str2="this is a string too";
int a[45]={1,1,2,4,4,3};
cout<<hex<<str<<endl;//this is a string
cout<<hex<<&str[0]<<endl;//this is a string
cout<<hex<<str2<<endl;//this is a string too
cout<<hex<<&str[3]<<endl;//s is a string
cout<<hex<<str2+3<<endl;//s is a string too
cout<<hex<<a<<endl;//0041FD34
cout<<hex<<&a[0]<<endl;//0041FD34
cout<<hex<<&a[3]<<endl;//0041FD40
cout<<dec<<sizeof a<<endl;//180----a包含了数组大小
cout<<dec<<sizeof a[0]<<endl;//4
获取数组中元素个数
_countof( )
字符串输入
const int MAX=80;
char name[MAX];
cin.getline(name,MAX,'\n');//以最大量MAX(MAX-1)结束或者字符'\n'结束
//或者
cin.getline (name,MAX);//以最大量控制
cin>>name;//不控制,实际以'/n'控制
多维数组
数组在内存中的存储方式是使最右边的索引值快速的变化,二维数组实际上是一维数组的一维数组.例如夏利中的data[0],data[1]都是一维数组.
初始化:
double beans[12][10]={0.0};
int data[2][4]={
{1,2 },
{2,5,67,4}
};
指针
指针声明
long * pnumber;\\指向`long`类型的指针
long number=99;
pnumber=&number;
int * a,b;
int* a,b;
\\等同
int *a;
int b;
指针初始化
int number=0;
int * pnum=&number;
\\或者
int * pnum=nullptr;
\\增加检查
if(pnum==nullptr)
cout<<"pnumber does not point to anything"<<endl;
if(!pnum)
cout<<"pnumber does not point to anything"<<endl;
nullptr
可以隐式转换为任何指针类型和bool
类型
指向char
类型的指针与字符数组区别
char*
可以用字符串字面值初始化.将其视为字符串,以\0
终止,(实际上是const char类型
的数组)
char* proverb ="a miss is as good as a mile.";
const char p[]= "a miss is as good as a mile.";
cout<<proverb<<endl;
指向char
类型的指针:本身是指针
字符数组:本身是数组,可以用_countof
来确定长度,也可以用sizeof/sizeof
char *pstr="sdfOIUYjndasdfJU Idfksde.";
char astr[]="sdfOIUYjndasdfJU Idfksde.";
cout<<"*pstr=\"sdfOIUYjndasdfJU Idfksde.\""<<endl;
cout<<"astr[]=\"sdfOIUYjndasdfJU Idfksde.\""<<endl<<endl;
cout<<"sizeof(pstr):"<<sizeof(pstr)<<" "
<<"sizeof(astr):"<<sizeof(astr)<<endl<<endl;
cout<<"pstr hex: "<<hex<<pstr<<endl;
cout<<"astr hex: "<<hex<<astr<<endl<<endl;
cout<<"*pstr: "<<*pstr<<" "
<<"astr[0]: "<<astr[0]<<endl;
cout<<"sizeof(*pstr):"<<sizeof(*pstr)<<" "
<<"sizeof(astr[0]):"<<sizeof(astr[0])<<endl<<endl;
cout<<"*(pstr+0): "<<*(pstr+0)<<" "
<<"astr[0]: "<<astr[0]<<endl;
cout<<"sizeof(*(pstr+0)):"<<sizeof(*(pstr+0))<<" "
<<"sizeof(astr[0]):"<<sizeof(astr[0])<<endl<<endl;
cout<<"*(pstr+2): "<<*(pstr+2)<<" "
<<"astr[2]: "<<astr[2]<<endl;
cout<<"sizeof(*(pstr+2)):"<<sizeof(*(pstr+0))<<" "
<<"sizeof(astr[2]):"<<sizeof(astr[0])<<endl<<endl;
cout<<"(sizeof(pstr)/sizeof(*(pstr+3))): "<<dec<<(sizeof(pstr)/sizeof(*(pstr+3)))<<endl;
cout<<"(sizeof(astr)/sizeof(astr[3])): "<<dec<<(sizeof(astr)/sizeof(astr[3]))<<endl;
cout<<"strlen(pstr): "<<strlen(pstr)<<" "
<<"strlen(astr): "<<strlen(astr)<<endl;
输出:
*pstr="sdfOIUYjndasdfJU Idfksde."
astr[]="sdfOIUYjndasdfJU Idfksde."
sizeof(pstr):4 sizeof(astr):26
pstr hex: sdfOIUYjndasdfJU Idfksde.
astr hex: sdfOIUYjndasdfJU Idfksde.
*pstr: s astr[0]: s
sizeof(*pstr):1 sizeof(astr[0]):1
*(pstr+0): s astr[0]: s
sizeof(*(pstr+0)):1 sizeof(astr[0]):1
*(pstr+2): f astr[2]: f
sizeof(*(pstr+2)):1 sizeof(astr[2]):1
(sizeof(pstr)/sizeof(*(pstr+3))): 4
(sizeof(astr)/sizeof(astr[3])): 26
strlen(pstr): 25 strlen(astr): 25
请按任意键继续. . .
指针数组 常量指针 指向常量的指针
char * pstr[]= {"tan tanghua",
"guo dinghua",
"zhao lixiu",
"zhang jingjing",
"huo guorong",
"li yuanyuan" };
数组中各个指针初始化为li yuanyuan
等字符串字面值的地址,字符串字面值的类型是const char
数组
*pstr[0]="little finger";\\无法编译:无法从"const char [7]"转换为"char",不能将 "const char *" 类型的值分配到 "char" 类型的实体
*pstr[0]='x';\\可以编译,运行时崩溃
为了避免以上通过指针改内容,改进声明方法
const char * pstr[]= {"tan tanghua",
"guo dinghua",
"zhao lixiu",
"zhang jingjing",
"huo guorong",
"li yuanyuan" };
但是仍然可以合法的编写
pstr[0]=pstr[1];
上面两个指针指向同一个姓名,并没有改变指针数组元素指向对象的值,改变的是pstr[0]
中存储的指针的值
为了避免修改指针中的地址,改进声明,注意const
位置
const char * const pstr[]= {"tan tanghua",
"guo dinghua",
"zhao lixiu",
"zhang jingjing",
"huo guorong",
"li yuanyuan" };
1 指向常量对象的指针
不能修改被指向的对象,但是可以使指针指向其他对象
const char* pstring="some string";//看作一个整体,const ( ),也就是说 *psting无法修改
char a='A';
pstring=&a;//可以
*pstring="another";//不可以
*pstring='x';//不可以
2 指向某个对象的常量指针
不能修改指针中的地址,但是可以修改指针指向的对象
char* const pstring="some string";
char a='A';
pstring=&a;//不可以
*pstring="another";//不可以,因为char*+字符串自带 const 属性
*pstring='x';//不可以
3 指向常量对象的常量指针
指针和被指向对象都定义成常量,都不能修改
const char* const pstring="some string";
char a='A';
pstring=&a;//不可以
*pstring="another";//不可以
*pstring='x';//不可以
const char*
是一个指向字符的指针,且这些字符是const
char* const
是一个指向字符的const
指针
sizeof操作符
cout<<(sizeof pstr)/(sizeof pstr[0])
对于指针数组
cout<<(sizeof a)/(sizeof a[0])
对于普通数组a[]={1,2,32,3};
cout<<sizeof b
对于变量int b=4;
size_t long_size=sizeof(long);
作用于类型名称,该类型占用的字节数量,圆括号
注意一下现象,sizeof
用来统计元素个数时尽量连用(sizeof a)/(sizeof a[0])
int a[]={1,1,4,58,58,98,41,15};
cout<<"&a="<<hex<<a<<" "<<sizeof a<<endl;//&a=0025FA14 20
cout<<"&a[0]="<<hex<<&a[0]<<" "<<sizeof a[0]<<endl;//&a[0]=0025FA14 4
cout<<"sizeof a="<<sizeof a<<endl;//sizeof a=20
cout<<"sizeof a[0]="<<sizeof a[0]<<endl;//sizeof a[0]=4
cout<<"sizeof a/sizeof a[0]="<<(sizeof a)/(sizeof a[0])<<endl;//sizeof a/sizeof a[0]=8
cout<<typeid(sizeof a).name()<<endl;//unsigned int
指针和数组
double * pdata=nullptr;
double data[5];//*data=data[0],*(data+1)=data[1],*(data+2)=data[2]
pdata=data;//与pdata=&data[0]区别
pdata=&data[1];
pdata=pdata+1;//&data[2],*pdata=data[2]
统计字符串字符个数
char buffer[80]="sdfasdfasdfasdfasdfasdf";
char* pbuffer=buffer;
while(*pbuffer)//static_cast<int>('\0')=0
pbuffer++;
cout<<typeid(pbuffer).name()<<pbuffer<<endl;\\char *
cout<<typeid(pbuffer-buffer).name()<<pbuffer-buffer<<endl;\\int
cout<<pbuffer-buffer<<endl;
//或者
int i=0;
while(*(pbuffer+i))
i++;
cout<<i<<endl;
指针和多维数组
*指针数组是数组元素为指针的数组(例如 int p[3],定义了p[0],p[1],p[2]三个指针),其本质为数组
数组指针是指向数组地址的指针,其本质为指针
double * data[15];//定义一个指针数组,数组中有15个指针
double beans[6][5];
double *pbeans;
pbeans=&beans[0][0];//第一个元素的地址
pbeans=beans[0];//第一行的地址
pbeans=beans;//错误,类型不同
int a[4][5];
int (*p)[5]=a;
p
是一个指针变量,它指向包含5个int
元素的一维数组,此时p
的增量以它所指向的一维数组长度为单位;
*p+i
是二维数组a[0][i]
的地址;
*(p+2)+3
表示a[2][3]
地址
*(*(p+2)+3)
表示a[2][3]
的值
等价:
beans[i][j]
*(*(beans+i)+j)
*(beans[i]+j)
(*beans+i)[j]
动态内存分配
一个new
一定要对应一个delete
double * pvalue=nullptr;
pvalue=new double;
*pvalue=999.0;
//或者
pvalue=new double(999.0);
//或者
double * pvalue=new double(999.0);
delete pvalue;
pvalue=nullptr;
为数组动态分配内存
数组动态类型分配最好用size_t
类型来分配数组大小
pstr=new char[20];
delete [ ] pstr;
pstr=nullptr;
long *p=nullptr;
int max;//最好用size_t类型
cin>>max;
p=new long[max];
//而对于数组类型
long data[max];//出错,必须是常量
//动态的分配
double *value=new double[5];
double *temp=new double[another];
for(int i=0;i<another;i++)
{
*(temp+i)=*(value+i);
}
delete [] value;//删除分配
value=temp;//重新
temp=nullptr
多维数组动态分配
double (*pbeans)[4]=nullptr;
pbeans=new double [3][4];
//销毁
delete [] pbeans;
pbeans=nullptr;
使用引用
引用分为两种:l value引用
和rvalue引用
,lvalue引用
是别名而非指针,声明时必须指出对应的变量,与指针不同,我们不能修改引用使其表示另一个变量.
long num=0L;
long & refnum=num;
//对于常量
int & refData=5;//wrong
const int & refData=5;
基于范围for循环中使用引用
//1 t变量没用引用数组元素,只是引用了其值,所以不能用他修改元素
for(auto t:nums)
{
sum+=t;
++count;
}
//2 使用引用修改元素,避免时间一些成本很高的复制操作
const double F2C=5.0/9.0;
for(auto & t:temperature)
t=(t-32)*F2C;
//3数组的引用
int a[5]={0,2,3,45,6};
int (&b)[5] = a;
for(int i = 0; i < 5; i++)
cout<<b[i]<<endl;
cout<<sizeof(b); //20
字符串的库函数
标准库提供的cstring头文件中包含操作**以空字符结尾的字符串(null-terminated string)**的函数.
strlen(),wcslen()
确定以空字符结尾的字符串的长度,size_t类型的返回值,计算空格
strnlen(),wcsnlen()
更安全的字符段长度计算,对于外部来源
strcat(),wcscat()
拼接以空字符结尾的字符串,第一个参数必须有足够的空间容纳两个字符串
strncat(),wcsncat()
增加第三个参数指定附加字符个数,并在末尾加空字符
strcat_s()wcscat_s()
安全的字符串链接,不需要输入以空字符结尾
strcpy(),wcscpy()
复制以空字符结尾的字符串,将字符串从源位置复制到目标位置
strcpy_s(),wcscpy_s()
更安全的复制
strcmp(),wcscmp()
比较以空字符结尾的字符串,比较第一对不同的字符代码来确定
strspn(),wcsspn()
搜索以空字符结尾的字符串,搜索字符串中不包含在给定集合中的第一个字符的索引
strstr()
搜索以 空字符结尾的字符串,返回第一个字符串中第二个字符串的第一个位置