数组与指针.docx
《数组与指针.docx》由会员分享,可在线阅读,更多相关《数组与指针.docx(19页珍藏版)》请在冰豆网上搜索。
![数组与指针.docx](https://file1.bdocx.com/fileroot1/2023-1/25/12fa0f93-36cb-4bc1-9199-dd2a29c2a288/12fa0f93-36cb-4bc1-9199-dd2a29c2a2881.gif)
数组与指针
数组与指针
目录:
1、指针简介(p2-p5)
1.指针的声明
2.指针的初始化
3.指针间的相互赋值操作
4.void型指针
二、数组简介(p5-p9)
1.数组声明和初始化
2.数组的下标引用
3.常量数组
三、指针与数组(p9-p17)
1.指针与数组之间的关联
2.行指针和列指针
3.指针数组和矩阵数组的一些差异
4.动态内存申请
四、C-字符串(p17-p31)
1.字符串的输入输出
2.C串的相关操作函数
5、引用、C++串(p31)
(注:
本内容主要由笔记积累和个人经验及一些书目借鉴辅助而成,如有错误请及时提醒,由于编辑原因,程序一些字符自动转为大写,在电脑上编辑时请手动改正;另,因为时间精力原因,一些内容在文中已略去,如未提及请参考其他资料或请教同学)
1、指针简介
一般来说,指针是一个其值为地址的变量或是一个数据对象。
它提供了一种使用地址的符号方法,主要应用于数组的遍历。
1、指针的声明
(指向数据类型)*(指针变量名1);
需要注意的是:
如果声明多个指针变量,均需在变量名前加‘*’,否则视为一般变量,如:
int*v1,v2,*v3;
其中,v1、v3为指针变量,v2为int型变量。
2、指针的初始化
a、一般在声明的同时就可以进行初始化,如:
inta,*p=&a;(使用取地址符获取a变量的地址并赋给p)
b、如果暂时不用或不明确指向对象,可以先赋0或NULL(这是仅有的两个可以赋值给指针的常量,都是空的意思),当在需使用时再重新赋值。
c、还可以用指针给指针进行初始化:
inta,*p=a;
int*p1=p;
此时p1接受p传递过来的地址,和p同时指向a。
3、指针间的相互赋值操作
指针间的赋值涉及到指针间的兼容性问题,它不像普通变量有默认的类型转换(如char->int->double),它对类型要求更加严格。
必须同类型才能才可赋值,而且地址赋值操作必须是两者同时为非常量类型。
关于指针常量和常量指针:
它们都是通过const关键字来声明的。
常量指针,即:
指向常量的指针
常量指针的声明,如:
intarr[size]={0,1,2,3,4};
Constint*p=arr;
常量指针不允许通过该指针对其指向地址所存储的值进行修改,如:
*p=12;
这是系统不允许的。
指针常量的声明:
本身是一个常量的指针。
Int*constr=arr;
指针常量不允许对指针变量值进行修改,如:
Inta;r=&a;
是错误的。
(注意:
常量指针和指针常量在声明时必须初始化。
否则编译错误!
)
常量指针可以用常量指针初始化,如:
Constint*p1=p;//合法
指针常量同样可以用指针常量初始化,如:
Int*constr1=r;//合法
可以用指针常量给常量指针初始化,但反之不可以,系统会提示:
cannotconvertfrom'constint*'to'int*'。
(注:
用指针常量给常量指针初始后,可人为地通过指针常量来修改常量指针同样指向的相同区域的值)
4、void型指针
ANSI新标准增加了一种"void"指针类型,即可定义一个指针变量,
但是不指定它是指向哪一种数据类型。
char*p1;
void*p2;
p1=(char*)p2;//p2指针本来是void类型,可用这条语句对其强制转换,将其转换成char类型
(大家可以尝试更为标准的纯地址转换方式:
Reinterpret_cast(expression))
同样,可以用(void*)p1将p1的值转换成void*类型:
p2=(void*)p1;//将p1转换成void类型指针
也可以将一个函数定义成void*类型,如:
void*fun(charch1,charch2);表示该函数返回值是一个地址,它指向“空类型”,如果需要引用这个地址,根据需要对其进行转换.
比如需要将这个函数的返回值(地址)转换成字符型,可以这样:
p1=(char*)fun(charch1,charch2)
(注:
假定有两个已初始化的指针p1,p2
指针可参与的运算:
P1-p2......表示各自所指向地址间隔,在实际应用中无多大意义。
但:
p1+p2.....是不可以的,两个地址相加无任何意义。
P1=p2.......p2地址值赋给p1
还有自增自减等操作,一般用于数组遍历,以及引用成员操作等。
另:
注意取地址符&和解引用符(或间访寻址符)*的区别。
&是取地址,可理解为升级操作;*是从地址中取值,相当于降级操作,两者除声明变量时作用相反。
)
2、数组简介
是在C语言中允许定义的由同种数据类型项组合而成的数据
构造类型。
(而struct是C语言中允许定义的由多种类型数据项组合而成的数据构造类型)经声明定义后,其大小便不能改变。
1、数组声明和初始化
数据类型数组名[数组大小];
A、int型数组声明和初始化
一维数组的声明和初始化:
Inta[size]={0,1,2,3};
(long,float,double...类型的数组声明同int)
一般声明时size必须为常量,否则可能导致编译错误。
Size大于或等于元素个数。
如果初始化时未对所有元素初始化,其他几位默认为0;但若数组未初始化,元素值是随机的。
多维数组(以二维数组为例)的声明和初始化,如:
Intarr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
大括号中再用大括号来声明所属维数。
如果这样改:
Intarr[3][4]={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};
系统会报错,由于大括号括起的元素有4组,定义为3组;
如果定义为:
Intarr[3][4]={{1,2,3},{4,5,6},7,8,9,10,11,12};
系统会自动把{1,2,3}和{4,5,6}归为arr[0]和arr[1]的元素,这时会发现剩下的有6个元素,仍会报错。
如果不加大括号将数据分开,系统会自动按行读取:
Intarr[3][4]={1,2,3,4,5,6,7,8,9,10};
系统会将{1,2,3,4}、{5,6,7,8}、{9,10,0,0}分别归为arr[0]、arr[1]、arr[2]的元素。
(注:
intarr[][4]={1,2,3};其中arr数组等同于arr[1][4],但不同于arr[4]={1,2,3};)
二维数组还可以这样初始:
Intarr[][4]={1,2,3,4,5};
系统会依照初始元素个数来分配arr的第二维空间(此中的arr[][4]等同于arr[2][4]。
但仅用于初始情况下)
依照数组按行存取的原则,以下定义便是非法的:
Intarr[][]={1,2,3,4,5};
(无法确定内存空间的分配!
)
B、char型数组的声明和初始化
与int型数组相似,不同在于一维数组的大小要比元素个数多一,如:
Charc[6]={'h','e','l','l','o'};(末尾自动初始为
'\0');
注:
若将数组大小改为5,编译不一定会报错,但这会导致这些元素无法构成完整的字符串,在一些串操作中会导致错误结果。
有如:
charc[5]="Hello";
会发生编译错误
再如以下定义:
Charc[]={'h','e','l','l','o'};
和int型数组不同,在数组大小没有显示确认时,系统并不会在末尾自动添加一个'\0',在一些串操作中同样会导致错误结果!
或定义为:
Charc[6]={'h','e','l','l','o','\0'};
除此,字符数组有其他特殊的声明方式,如:
Charc1[]="hello";
Charc2[][6]={"hello","world"};
还可以通过ASCII码读取:
Charc3[6]={104,101,108,108,111,0};
或8进制(形如'\000')或十六进制(形如'\x00')表示。
结果与c1相同。
(注:
数组间不可以用数值表达式相互赋值,数组不可以用逻辑表达式整体做比较或做值运算)
2、数组的下标引用
数组的下标可以为整型非负常量、整型表达式、或整型变量。
借助下标引用可以访问数组元素。
(略)。
3、常量数组
常量数组的声明,如:
Constinta[4]={1,2,3,4};
Constcharc[6]={104,101,108,108,111,0};
值得注意的是,对于常量数组,必须在声明时对其初始化,而且每个元素都必须被初始,不能省略。
如:
Constcharc[6]={104,101,108,0};
将不能通过编译。
3、指针与数组
1、指针与数组之间的关联
一般来说,出现指针的同时一定会出现数组,因为指针的主要作用是实现数组的遍历,也是这个原因,在出现数组的同时常常能看到指针的身影。
用数组为指针初始化:
(略)
数组的遍历:
(略)
需要注意的是,不带下表的数组名相当于一个指针常量,是不能对它的地址值进行修改的。
2、行指针和列指针(相对于二维数组而言的)
A、先介绍一下数组指针和指针数组:
数组元素全为指针的数组称为指针数组:
Int*p[4];//*p[4]为指针数组,含四个指针元素。
//本质是数组
数组指针,即指向数组首元素的指针,如:
Int(*p)[4];//p为数组指针,指向数组首地址;
//本质是指针
B、行指针
即指向行坐标的的指针,如:
Inti,j;
inta[2][3];
Int(*p)[3]=a;//这里为数组指针。
For(i=0;i<2;i++)
For(j=0;j<3;j++)
Printf("%d",*(*(p+i)+j));
这段代码中,p为二维数组a的行指针,可见p为一个二级指针。
而这段代码实现的是数组的遍历输出。
C、列指针
Inti,j;
inta[2][3];
Int*p1=*a;(或int*p1=a[0];)
For(i=0;i<2;i++)
For(j=0;j<3;j++)
Printf("%d",*(p1+i*2+j));
这段代码功能与上相同,不同的是,p1为数组a的列指针,与行指针不同,它指向数组的列坐标,为一级指针。
由上,将代码合并:
Inti,j;
inta[2][3];
Int(*p)[3]=a;
Int*p1=*p;
For(i=0;i<2;i++)
For(j=0;j<3;j++)
Printf("%d",*(p1+i*2+j));
同样可实现数组遍历。
(注意解引用符*的作用,p将二级指针化为一级指针的值传给p1)
D、指向指针的指针
有如下代码:
Main()
{
Inti;
Char*ptr[]={"one","two","three","four"};
Char**p=ptr;//p为二级指针
For(i=0;i<4;i++)
{
Cout<<*p<P++;
}
}
以上代码实现对指针数组内的字符串的遍历输出。
P即为指向ptr[0]的指针,ptr[0]实际上是指向字符串"one"的指针,存储"one"的地址,p即为指向指针的指针。
上述代码可以修改为:
Main()
{
Inti;
Char*ptr[]={"one","two","three","four"};
For(i=0;i<4;i++)
{
Cout<}
}
同样可实现相同功能,至于指向指针的指针的功用这里不加叙述。
3、指针数组和矩阵数组的一些差异
这里用一个实例来简述。
有一下定义:
Charfruit[3][7]={"APPLE","PEAR","ORANGE"};
Char*fruit1[3]={"APPLE","PEAR","ORANGE"};
关于内存存储:
矩阵数组fruit存储情况如下:
A
P
P
L
E
'\0'
'\0'
P
E
A
R
'\0'
'\0'
'\0'
O
R
A
N
G
E
'\0'
共需21字节。
指针数组fruit1存储情况如下:
A
P
P
L
E
'\0'
P
E
A
R
'\0'
O
R
A
N
G
E
'\0'
共需18字节。
由此指针数组应用于字符串,可以更多的节省内存。
数组遍历:
对于指针数组,它不仅可以用像矩阵数组那样用下标引用的方式读取每个字符串,由于ptr指向"APPLE"第一个字符'A'的地址,通过间接访问的形式,也可以挨个读取字符。
4、动态内存申请
在实际程序应用中,我们会碰到这样一类情况,有时候我们
依照设计在一段时间内要一定的储存空间来储存一定数据,而这段时间以外不需要这些额外的空间,这会在程序运行中造成不必要的内存浪费,为了减少这种内存浪费,在程序中就有了动态内存申请的用武之地。
A、malloc与free
malloc函数原型:
void*malloc(unsignedsize)
(声明void*类型是为了适应其它标准类型)
malloc()函数的返回值要赋给一个相同类型的指针。
free函数原型:
voidfree(void*p)
malloc和free是C中内存分配和内存申请函数,函数原型都包含在malloc.h的头文件中,具体用法用下述代码说明:
#include
#include
intmain()
{
char*p[100];
inti=0;
for(;i<100;i++)
{p[i]=(char*)malloc(sizeof(char)*100);
//连续申请100个100字节的内存
scanf("%s",p[i]);//向p[i]地址输入字符串值
printf("%s\n",p[i]);//输出p[i]指向的字符串
}
for(i=0;i<100;i++)
free(p[i]);
//连续释放p[i]内存
return0;
}
注:
其中申请动态内存的语句可以等价替换为:
p[i]=(char*)calloc(100,sizeof(char));
B、new和delete
new和delete是C++中很重要的两个内置操作符,有比较复杂
的语法结构与应用,其语言本身已固定,无法重新定制。
在此仅对其中一种形式(newoperator)作介绍。
有如下一段代码:
#include
usingnamespacestd;
intmain()
{
char*p[100];
inti=0;
for(;i<100;i++)
{p[i]=newchar[sizeof(char)*100];//注意是方括号
cin>>p[i];
cout<
}
for(i=0;i<1024;i++)
delete[]p[i];//注意释放方式的不同
return0;
}
注:
这段代码与前一段代码功能相同
使用时要注意由于释放格式的不同而造成的一些差异,如:
Deletep[i];
语句本身没有语法错误,但它仅释放p[i]指向的//第一个单位的1字节的内存(如果是int,则是4字节),而上述代码中的释放内存语句可以释放p[i]所分配到的整块内存。
另:
使用上述new来申请动态内存有两方面含义:
第一,它分配足够的内存,用来放置某类型的对象。
第二,它调用一个构造函数,为刚才分配的内存中的那个对象设定初始值。
(应用于class时)
(注:
应用于class,delete可以自动调用析构函数,而malloc和free不会有与new和delete相同的操作)
四、C-字符串
C风格字符串通常以字符数组的形式出现(也有用指针指向字符串常量的),故也涉及到很多与数组相关的操作和内容。
所以学习C风格字符串应与字符数组联系起来,由于数组和指针使用比较灵活,也给C串的使用造成一定的麻烦!
1、字符串的输入输出
以数组为存储方式的C串具有能整体输入输出的特性。
在C语言中,提供了一些用于输入输出的函数:
gets(),
puts(),scanf(),printf(),getchar(),putchar()等函数。
以上6个函数的函数原型都包含在头文件stdio.h中。
(注:
字符指针这样初始化:
Char*p=”hello”;
系统会这样处理:
先定义一个数组:
constcharstr[]=”hello”;
然后有:
char*p=str;
只是这个数组名无从得知)
A、gets()函数,puts()函数
关于gets的函数原型描述如下:
Char*gets(char*s)
它以两种方式获得输入:
1、使用一个地址把字符串赋予name(name为函数体中定义的一个数组名)
2、返回地址给一个char型的指针s,若读取失败,返回一个空地址0或NULL.(这使得s在主函数中即使未初始也不至于成为野指针!
)
Gets()在读取字符串时,以'\0'为结束标志,能读取包括空格在内的其他字符。
而puts()函数则是输出一个字符串,执行的正是与gets()相反的操作。
Puts()会读取被操作的字符串中的每个字符并输出,知道遇到空字符结束,期间包括空格在内的以及换行符(执行换行操作)等其他字符并输出。
B、scanf()和printf()函数
字符串输入,例:
#include
Intmain()
{
Charc[100];
Scanf("%s",c);
Charc1[20]="OK,\nitsuptoyou!
";
Printf("%s\n%s",c,c1);
Return0;
}
输入:
Itsagoodidea!
输出:
Its
OK,
Itsuptoyou!
分析:
对于scanf()函数,在读取字符串时遇到空格或空字符或换行符都会停止读入。
而printf()函数则会读取并输出所有字符,直到遇到空字符或字符串结束时结束。
注:
作为个是输入输出函数,格式命令符%作用很重要,输出字符串的格式命令为"%s"。
(对于其他格式输入输出的格式要求及相关格式命令这里不多做介绍)
C、getchar()和putchar()函数
Getchar()和putchar()函数分别在读入和输出时都是
以单个字符为操作单位的。
并且能对除空字符外的所有字符进行操作。
正是gets()和puts()函数的细化版。
D、预定义函数getline()及cin读入、cout输出
C++中对字符串操作的cin的成员函数,用于读取字符
串:
#include
Usingnamespacestd;
Intmain()
{
Charc[100];
Cin.getline(c,100,'!
');
Charc1[20]="OK,\nitsuptoyou!
";
Cout<Return0;
}
该段程序改自本部分B讲中的示例程序,当进行相同的输入后输出:
Itsagoodidea!
OK,
Itsuptoyou!
Getline()函数有三个参数,后两个在原型中都具有默认值。
第三个参数为结束标志符,默认为'\n',本题中设置为’!
'即遇'!
'结束读入。
(虽然貌似本题这样设没多大作用)
cout对于字符串的输出:
与printf()相似,可以输出
包括换行符及空格在内的字符,直到遇到空字符结束;而cin读入时遇到空格或换行符或空时都会停止读入。
(注:
getline()函数也用在string类中,但使用方法略有不同:
Strings;
Getline(cin,s);
2、C串的相关操作函数
主要对几个较常用的函数做以下简述:
strlen(),
Strcmp()等。
注:
这类函数大多以'\0'为终止标示符。
函数名:
strlen
功能:
在串中查找指定字符串的第一次出现
用法:
intstrlen(char*str1);
程序例:
#include
#include
intmain(void)
{
char*str="BorlandInternational";
intptr=strlen(str);
printf("Thesubstringis:
%d\n",ptr);
return0;
}
注:
注意与sizeof区别,如:
Chara[]="abcdefg",b[10]="abcdefg";
Strlen(a)值为7,strlen(b)为7;而sizeof(a)为8,sizeof(b)为10;
又如:
Char*a="abcdefg",b[10]="abcdefg";
Strlen(a)值为7,strlen(b)为7;而sizeof(a)为4,sizeof(b)为10;
函数名:
strcpy
功能:
拷贝一个字符串到另一个
用法:
char*stpcpy(char*destin,char*source);
程序例:
#include
#include
intmain(void)
{
charstring[10];
char*str1="abcdefghi";
stpcpy(string,str1);
printf("%s\n",string);
return0;
}
函数名:
strcat
功能:
字符串拼接函数
用法:
char*strcat(char*destin,char*source);
程序例:
#include
#include
intmain(void)
{
chardestination[25];
char*blank="",*c="C++",*Borland="Borland";
strcpy(destination,Borland);
strcat(destination,blank);
strcat(destination,c);
printf("%s\n",destination);
return0;
}
函数名:
strcmp
功能:
串比较
用法:
intstrcmp(char*str1,char*str2);
逐个判断,直到遇到'\0';看Asicii码,str1>str2,返回值>0;两串相等,返回0
str1程序例:
#include
#i