-
C基础第四天
今天讲的是指针和字符串数组,不知道是之前留下的印象还是确实难理解,讲到这里的时候思维就自然而然的跟不上了。只能多接触试试了。
指针
**1、地址指针的概念
**
** 变量的指针和指向变量的指针
**
** 定义一个指针变量
**
** 指针变量的引用
**
**2、指针变量做函数参数
**
3、数组指针和指向数组的指针变量
**
** 指向数组元素的指针
**
** 通过指针引用数组元素
**
** 指向多维数组的指针和指针变量
**
**
**
** 在计算机中所有的数据都是存放在存储器中的,存储器被划分为一个一个的内存单元,为了正确的找到这些信息,必须为每一个内存单元进行编号,内存单元的编号就是地址。地址通常也称为指针。
** 举个具体的例子: 我们去银行存取款,银行的工作人员根据我们的账号找到我们的存款单。找到以后进行存取款操作,并做相关记录。这里,账号就是存单指针,存款数就是存单的内容.
**
**
**
**如何定义一个指针变量?
**
**对指针变量的定义包括三个内容:
**
**1、指针类型的说明,定义变量为一个指针变量;
**
**2、指针变量名;
**
**3、变量值(指针)所指向的数据类型;
**
**
**
**指针变量定义的一般形式为:
**
** 类型说明符 *变量名;
**
*注: 表示这个变量为指针变量,变量名即为定义的指针变量名,类型说明符表示本指针所指向的变量的数据类型。
**
**例:int *p;
**
**表示p是一个指针变量,它指向某个整型变量,他的值是某个整型变量的地址;p具体指向那一个整型变量,由向p赋值的地址来决定;
**
例:
int *p2; /*p2是指向整型变量的的指针变量*/
float *p3; //p3是指向浮点型变量的指针变量
char *p4; //p4是指向字符型变量的指针变量
**注:一个指针只能指向同类型的变量,p2只能指向浮点型变量 。
**
**
**
**指针变量的使用:
**
**指针变量和普通变量一样,使用之前必须要定义说明和赋值。未经赋值的指针变量不能使用,否则会造成系统混乱,甚至是死机。
**
**指针变量的赋值只能是地址,决对不能赋其他任何数据。
**
**
**
**两个相关的运算符: &:取地址运算符; *:指针运算符(或间接访址运算符)。
**
**C语言中提供了地址运算符&,表示变量的地址;
**
**使用的一般形式为:
**
** &变量名;
**
**例:&a 表示变量a的地址,&b表示b的地址,变量本身必须事先说明。
**
**(1)、指针变量的初始化方法
**
int a;
int *p=&a;
**(2)、指针变量的赋值
**
int a;
int *p;
p=&a;
**不允许把一个赋值给指针变量,int *p; p=1000; 这样的做法是错误的。
**
被赋值的的指针变量前面不能再加'*'说明符,如:*p=&a;
**
**
**我们定义两个整形变量和一个指针变量:
**
int i=200,x; int *ip; //i,x中存放的是整型变量,ip中只能存放整型变量的地址。
**
**
**此时指针变量ip指向整型变量i,假设i的地址是1800,这个赋值语句可以理解为
其中*ip和i是等价的,*p可以完全代替i,
x=*ip; //ip通过指针间接访问变量i地址,等价于 x=i;
指针变量和一般的变量一样,存放的值是可以改变的
int i,j,*p1,*p2;
i='a';
j='b';
p1=&i;
p2=&j;
建立如下的对应关系
这时赋值表达式p2=p1;//p2与p1指向同一对象i,此时p2就等价于i;*
如果执行p2=p1;//表示把p1指向的内容赋值给p2;**
#include <stdio.h>
int main()
{
int i=3,j=7;
int *p1,*p2;
p1=&i;
p2=&j;
p2=p1;//分别执行p2=p1 和 *p2=*p1
//*p2=*p1;
printf("i=%d,j=%d,*p1=%d,*p2=%d,p1=%p,p2=%p\n",i,j,*p1,*p2,p1,p1);
return 0;
}
*''和'&'使用注意事项
**
*''是指针运算符,间接访问运算符,'&'是取地址运算符。
**
**初始化 int *p=&i; 两个符号必须同时在
**
**赋值时 p=&i; 只能有一个取地址符号在
**
***p和一个整型变量等价,&i和一个指针变量等价。
**
**指针变量作为函数的参数
**
**函数的参数除了时整型、字符型、浮点型等,还可以是指针类型的。
**
指针类型的参数的作用是把一个变量的地址传入函数中。
#include <stdio.h>
void swap(int *pa,*pb)
{
int temp;
temp=*pa;
*pa=*pb;
*pb=temp;
}
int main()
{
int a,b;
int *pa,*pb;
printf("请输入a和b的值\n");
printf("a=");
scanf("%d",&a);
printf("b=");
scanf("%d",&b);
pa=&a;
pb=&b;
swap(pa,pb);
printf("a=%d,b=%d",a,b);
return 0;
}
**指针变量的运算
**
**1、赋值运算。
**
** (1)指针变量的初始化和赋值前面个已经介绍过了
**
** (2)把一个变量的地址赋值给指向相同数据类型的指针变量
**
int a ,*pa;
pa=&a;//把整型变量a的地址复制给整型指针变量pa
** (3)把一个指针变量的值赋给另一个指针变量(二者类型相同)
**
int a,*pa,*pb;
pa=&a;
pb=pa; //把a的地址赋给pb,pa,pb类型相同可以相互赋值;
** (4)把数组的首地址赋给指向数组的指针变量
**
int a[5],*pa;
pa=a;//注意:这里不需要取地址符,a表示数组的首地址
//等价于 pa=&a[0];数组第一个元素的地址也是整个数组的首地址
** (5)把字符串的首地址赋给指向字符类型的指针变量
**
char *p;
p="C program";//这里不是吧整个字符串装入指针变量,而是把存放该字符串的字符数组的首地址装入指针变量。
** (6)把函数的入口地址赋给指向函数的指针变量
**
int (*pf)();
pf=f; //f为函数名
**2、指针变量的加减法
**
** 指针的加减法主要的运算对象是数组。
**
** 指针变量加上或减去一个整数n的意义是把指针指向的当前位置(某数组元素)向前向后移动n个位置。
**
**数组指针变量移动一个位子,和地址加一减一的概念是不同的。数组是不同类型的,所以数组元素所占字节数也是不同的,指针变量加一,即向后移动一个位置,表示指针指向下一个数组元素的首地址。而不是在原地址上加一。
**
int a[5],*pal
pa=a; //pa指向数组的第一个元素a[0]
pa=pa+2;//pa向后移动两个位置,指向数组的第三个元素,a[2];
**指针变量的加减法只能对数组指针变量进行,对指向其他数据类型的指针变量加减操作是毫无意义的.
**
**3、两个指针变量间的运算:只有指向同一数组的两个指针变量之间才能够进行运算,否则是毫无意义的。
**
** (1)两指针变量相减:两指针变量相减所得之差是两个指针所指元素之间相差的元素个数。实际上就是两个指针值(地址)相减的差值再除以该数组元素的长度。
**
** 注:两指针之间不能进行加操作,加操作毫无意义
**
** (2)两指针之间的关系运算: 指向同一数组的两个指针变量进行关系运算可表示它们指向的数组元素之间的关系。
**
pf1==pf2;//表示pf1和pf2指向同以数组元素。
pf1>pf2;//表示pf1处于高地址位
pf1<pf2;//表示pf1处于低地址位
** 指针变量还可以与0进行比较,
**
** 当p为指针变量时,p==0;表示p时空指针,它不指向任何变量。
**
** P!=0; 表时指针 不为空
**
** 空指针是对指针变量赋0得到的。
**
** 对指针变量赋0和不赋值是不同的。指针变量未赋值,可以是任意值,是不能使用的,否则将造成意外错误。指针变量赋0值以后,则可以使用,只是它不指向具体的变量而已。
**
例:
int main()
{
int a=10,b=20,s,t,*pa,*pb;
pa=&a;
pb=&b;
s=*pa+*pb;//求和
t=*pa * *pb;//求积
printf("a=%d\nb=%d\na+b=\na*b=%d\n",a,b,a+b,a*b);
printf("s=%d\ns=%d\n",s,t);
}
**数组指针和指向数组的指针变量
**
**一个变量有一个地址,一个数组包含若干个元素,每个数组元素都在内存中占用存储单元,他们都有相应的地址。所谓数组的指针是指数组的起始地址,数组元素的地址是数组元素的地址。
**
**
**
*指向数组元素的指针
**
一个数组是由连续的一块内存单元组成的。数组名就是这块连续内存单元的首地址。一个数组也是由各个数组元素(下标变量)组成的。每个数组元素按其类型不同占有几个连续的内存单元。一个数组元素的首地址也是指它所占有的几个内存单元的首地址定义一个指向数组元素的指针变量的方法,与以前介绍的指针变量相同。例如:int a[10]; /不定义a为包含10个整型数据的数组/intp; /定义p为指向整型变量的指针/应当注意,因为数组为int型,所以指针变量也应为指向int型的指针变量。下面是对指针变量赋值:p=&a[0];把a[10]元素的地地赋给指针变量p。也就是说,p指向a数组的第0号元素.
**
**C语言规定数组名代表的就是数组的首地址,也就是第0号元素的地址,
**
**p=&a[0]; 等价于 p=a;
**
**在定义指针变量时可以赋给初值
**
int *p=&a[0]; 等价于 int *p; p=&a[0]; 同时等价于 int *p=a;
**从图中的关系可以卡出 p,a,&a[0]均指向同一单元,他们是数组a的首地址,也就是0号元素a[0]的首地址。需要注意的是p是变量,a,&a[0]是常量。
**
**
**
**通过指针引用数组元素。
**
**C语言规定:如果指针变量p已经指向数组中的一个元素,则P+1指向同数组的下一个元素。
**
**引入指针变量以后我们就可以用两种方法来访问数组元素。
**
如果P的初值为&a[0],则
(1)P+i和a+i就是a[i]的地址,或者说他们指向a数组的第i个元素;
(2)*(p+i)或*(a+i)就是p+i或a+i所指向的数组元素,即a[i].例如*(p+5)或*(a+5)就是a[5];
(3)指向数组的指针变量也可以带下标,p[i]与*(p+i)等价;
所以说,引用数组可以使用下标法和指针法
1、下标法:即用a[i]的形式访问数组元素,
2、指针法:即采用*(a+i),*(p+i)形式,用间接访问的方法来访问数组元素,其中a是数组名,p是指向数组的指针变量,p=a;
int main()
{
int a[10],i;
for(i=0;i<10;i++)
*(a+i)=i
for(i=0;i<10;i++)
printf("a[%d]=%d\n",i,*(a+i));
}
指针数组的概念:
一个数组的所有元素都是指针则是数组指针,指针数组是一组有序的指针的集合。
指针数组的所有元素必须是具有相同存储类型和指向相同数据类型的指针变量。
**指针数组的一般形式:
类型说明符 * 数组名[数组长度];
**
其中类型说明符为指针指向的变量的类型:
*例如:int pa[3];:pa是一个指针数组,他有三个数组元素,每个元素都是一个指针,指向整型变量。
指向指针的指针:如果一个人指针变量中存放的另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。
int i;
int *p;
int a[n];
int *p[n];
int (*p)[n];
int f();
int *p();
int (*p)();
int **p;
**
**
**二、函数指针
**
**指向函数的指针包含了函数的地址,可以通过它来调用函数。声明格式如下:
类型说明符 (*函数名)(参数)
**
**其实这里不能称为函数名,应该叫做指针的变量名。
**
**这个特殊的指针指向一个返回整型值的函数。
**
**指针的声明笔削和它指向函数的声明保持一致。
**
**指针名和指针运算符外面的括号改变了默认的运算符优先级。
**
**如果没有圆括号,就变成了一个返回整型指针的函数的原型声明。
**
**
**
*void (fptr)();
**
**
**
**把函数的地址赋值给函数指针,可以采用下面两种形式:
**
**
**
**fptr=&Function; fptr=Function;
**
**
**
**取地址运算符&不是必需的,因为单单一个函数标识符就标号表示了它的地址,
**
**如果是函数调用,还必须包含一个圆括号括起来的参数表。
**
**可以采用如下两种方式来通过指针调用函数:
**
*x=(fptr)(); x=fptr();
**
**第二种格式看上去和函数调用无异。
**
**但是有些程序员倾向于使用第一种格式,因为它明确指出是通过指针而非函数名来调用函数的。
**
void (*funcp)(); //一个函数指针
void FileFunc(),EditFunc(); //声明两个函数
main()
{
funcp=FileFunc; //把函数的地址赋值给函数指针
(*funcp)(); //利用函数指针调用函数
funcp=EditFunc; //把函数的地址赋值给函数指针
(*funcp)(); //利用函数指针调用函数
}
void FileFunc() //FileFunc函数的定义
{
printf("FileFunc\n");
}
void EditFunc() //EditFunc函数的定义
{
printf("EditFunc\n");
}
程序输出为:
FileFunc EditFunc
FileFunc EditFunc
**一、指针函数
**
** 当一个函数声明其返回值为一个指针时,实际上就是返回一个地址给调用函数,
**
** 以用于需要指针或地址的表达式中。
**
**
格式:
类型说明符 * 函数名(参数)
**
**当然了,由于返回的是一个地址,所以类型说明符一般都是int。
**
**例如:
**
int *GetDate(); int * aaa(int,int);
**函数返回的是一个地址值,经常使用在返回数组的某一元素地址上。
**
int * GetDate(int wk,int dy);
void main()
{
int wk,dy;
do {
printf("Enter week(1-5)day(1-7)\n");
scanf("%d%d",&wk,&dy);
}while(wk<1||wk>5||dy<1||dy>7);
printf("%d\n",*GetDate(wk,dy));
}
int * GetDate(int wk,int dy)
{
static int calendar[5][7]= {
{1,2,3,4,5,6,7},
{8,9,10,11,12,13,14},
{15,16,17,18,19,20,21},
{22,23,24,25,26,27,28},
{29,30,31,-1} };
return &calendar[wk-1][dy-1];
}
程序应该是很好理解的,子函数返回的是数组某元素的地址。输出的是这个地址里的值。
字符数组
1、字符数组的定义:与数组的定义相同
例如:char c[10].
2、字符数组的初始化
字符数组也允许在定义的时候初始化赋值:
例:char c[10]={'C',' ','p','r','o','g','r','a','m'};
赋值后各元素的值为:
c[0]为‘C’,
c[1]为‘ ’,
c[2]为'p'
........
c[8]='m';
c[9]默认赋值为0;
3、字符数组的引用
#include <stdio.h>
int main()
{
int i,j;
chara[][5]=
{
{'B','A','S','I','C'},
{'d','B','A','S','E'}
};
for(i=0;i<=1;i++)
{
for(j=0;j<5;j++)
{
printf("%c ",a[i][j]);
}
printf("\n");
}
}
字符串和字符串结尾标志
在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。当把一个字符串存入数组时,也把结束符‘\0’存入数组,并以此作为该字符串的结束标志。有了‘\0’以后,就不必再用字符数组的长度来判断字符串的长度了。
C语言允许使用字符串的方式对数组作初始化赋值。
例:char c[]={'C',' ','p','r','o','g','r','a','m'};
可写为:char c[]={"C program"};或去掉{} 写为:char c[]="C program";
用字符串赋值要比用字符逐个赋值多占一个字节,用于存放字符串结束标志'\0'.
'\0'是由C编译系统自动加上的,由于采用'\0'结束标志,所以在用字符串赋初值时一般无须指定数组的长度,而由系统自行处理。
字符数组的输入输出:
除了上述赋值方法以外还可以使用printf,scanf函数直接输入输出;
int main()
{
char string[]="TIAN YONG";
printf("%s\n",string);
return 0;
}
int main()
{
char str[20];
printf("intput str:");
scanf("%s",str);
printf("%s\n",str);
return 0;
}
空格以后的字符都不能输出。
字符串处理函数:
1:puts()字符串输出函数
一般形式:puts(字符数组名)
功能:把字符数组中的字符串输出到显示器:
int main()
{
char c[]="string666";
puts(c);
return 0;
}
2:gets()字符串输入函数
一般格式:gets(字符数组名)
功能;从标准输入设备上输入一个字符串
int main()
{
cahr st[15];
printf("input st:");
gets(st);
puts(st);
return 0;
}
3:strcat:字符串连接函数
一般形式:strcat(字符数组名1,字符数组名2)
功能:把字符串2中的字符串连接到字符数组1中字符串后面,并删除数组1中的‘\0’.
例题连接“my nane is XXX”
4:strcpy:字符串拷贝函数
一般形式:strcpy(字符数组名1,字符数组名2)
功能:把字符数组2中的字符串拷贝到字符数组1中。结束标志‘\0’也一同拷贝。注意:要求字符数组1要足够长,否则不能全部装入所拷贝的字符串。
5:strcmp:字符串比较函数
一般形式:strcmp(字符数组名1,字符数组名2)
按照ASCII吗顺序比较两个数组中的字符串,并由函数返回值返回比较,相等返回0,1>2返回值大于,否则返回值小于0;
6:strlen:测试字符串长度
一般形式:strlen(字符数组名)
功能:测试字符串的实际长度(不含字符串结束标志‘\0’)并作为函数返回值。