AutoLeaders控制组—C语言指针学习笔记
一、指针
1.指针和指针变量
数据在内存中的存储地址,即一个变量的地址称为该变量的“指针”。
指针相当于旅馆中的房间号,而其所标志的内存单元中存放的数据相当于房间中居住的旅客,指针指向该变量单元。
一个专门用来存放另一变量的地址的变量称为“指针变量”。指针变量的值是地址(即指针)。
补充:&为取地址符号,是升维度的;*为取值符号,是降维度的。
2.直接访问与间接访问
直接访问:直接按变量名进行访问。
int i,k,j;
scanf("%d %d",&i,&j);
k=i+j;
printf("%d %d %d",i,j,k);
程序经过编译以后已经将变量名转换为变量的地址,对变量值的存取都是通过地址进行的。
间接访问:将变量i的地址存放在另一变量中,然后通过该变量来找到变量i的地址,从而访问变量i。
int a=10;
int*p=&a;
*p=100;
printf("%d\n",a);
printf("%d\n",*p);
p中的值是变量a的地址,通过p能知道a的地址,从而找到a的内存单元,即通过地址来体现这种指向关系。
3.指针变量的定义和使用
定义指针变量的一般形式为 类型名*指针变量名;
(1)先定义指针变量,再赋值。
int a=10,b=20;
int*p1,*p2;
p1=&a;
p2=&b;
(2)可以在定义指针变量的同时对它初始化。
int a=10,b=20;
int*p1=&a,*p2=&b;
注意:
(1)*表示该变量的类型为指针型变量,指针变量名称为p1、p2,a、b的地址是赋值给指针变量p1、p2,而不是赋给*p1(即a)、*p2(即b)。
(2)指针变量只能存放地址(指针),不要将一个整数赋给一个指针变量 。
基类型:定义指针变量时必须指定的类型名,用来指定此指针变量可以指向的变量的类型。
指针变量是基本数据类型派生出来的类型,不能离开基本类型而独立存在。(基本类型:整型、浮点型、字符型等)不同类型的数据在内存中所占的字节数和存放方式是不同的,只有知道数据的类型,才能按存储单元的长度以及数据的存储形式正确地取出该数据。
注意:
(1)一个指针变量只能指向同一个类型的变量;
(2)一个变量的指针的含义包括两个方面,一是以存储单元编号表示的地址,二是它指向的存储单元的数据类型,说明变量类型时应完整地说“p1是指向整型数据的指针变量”;
(3)指针类型的表示方式:指向整型数据的指针类型表示为“int*”,读作“指向int的指针”或简称“int指针”。其他指针类型同理。
指针变量的引用
(1)给指针变量赋值。
p=&a; //把a的地址赋值给指针变量p
(2)引用指针变量指向的变量。
int a=10;
int*p=&a;
*p=1; //表示将整数1赋给p当前所指向的变量a
printf("%d",*p); //以整数形式输出指针变量p所指向的变量a的值
(3)引用指针变量的值。
int a=10;
int*p=&a;
printf("%o",p); //以八进制数形式输出指针变量的值,即a的地址
指针变量的使用
eg1.输入a和b两个整数,按先大后小的顺序输出a和b。
(思路与两个变量值的互换类似,区别在于该代码不交换整型变量的值,而是交换两个指针变量的值)
#include <stdio.h>
int main()
{
int *p1,*p2,*temp,a,b;
scanf("%d %d",&a,&b);
p1=&a;
p2=&b;
if(a<b)
{
temp=p1; //使temp指向变量a
p1=p2; //使p1指向变量b
p2=temp; //使p2指向变量a
//也可写作{p1=&b;p2=&a;},程序更加简练
}
printf("a=%d,b=%d\n",a,b);
printf("max=%d,min=%d",*p1,*p2);
return 0;
}
指针变量作为函数参数
eg1.对输入的两个整数按大小顺序输出。
(与上个代码不同,该代码会交换整型变量的值,形参p1、p2在函数调用结束后释放)
#include <stdio.h>
void swap(int*p1,int*p2)
{
int temp=*p1;
//不可写为int*temp,指针变量temp指向的单元不可预见,对未知存储单元赋值可能破坏系统正常工作状态
*p1=*p2;
*p2=temp;
}
int main()
{
int a,b;
int*p1,*p2;
scanf("%d %d",&a,&b);
p1=&a;
p2=&b;
if(a<b)
{
swap(p1,p2);
}
printf("max=%d,min=%d\n",a,b);
return 0;
}
注意:形参变量和实参变量之间的数据传递是**单向的“值传递”**方式,所以不可通过执行调用函数来改变实参指针变量的值 ,但是可以改变实参指针变量所指变量的值。
eg2.对输入的三个整数按大小顺序输出。
#include <stdio.h>
void swap(int*p1,int*p2)
{
int temp=*p1;
*p1=*p2;
*p2=temp;
}
void exchange(int*p1,int*p2,int*p3)
{
if(*p1<*p2)
{
swap(p1,p2);
}
if(*p1<*p3)
{
swap(p1,p3);
}
if(*p2<*p3)
{
swap(p2,p3);
}
}
int main()
{
int a,b,c;
int*p1,*p2,*p3;
scanf("%d %d %d",&a,&b,&c);
p1=&a;
p2=&b;
p3=&c;
exchange(p1,p2,p3);
printf("%d>%d>%d\n",a,b,c);
return 0;
}
4.通过指针引用数组
数组元素的指针
数组元素的指针就是数组元素的地址。
==引用数组元素可以用下标法(如arr[0]),也可以用指针法(如*(a+i)或*(p+i)),指针法能使目标程序质量高(占内存少,运行速度快)。
注意:数组名代表数组首元素的地址,不代表整个数组 。
定义指针变量:
int*p=&a[0];
int*p;
p=&a[0];
int*p=a;
在引用数组元素时指针的运算
当指针指向数组元素的时候,允许对指针进行如下加减运算:
(1)加一个整数(用+或+=);
(2)减一个整数(用-或-=);
(3)自加运算(如a++、++a);
(4)自减运算(如a- -、- -a);
(5)两个指针相减(两个指针都指向同一数组中的元素)
注意:
(1)执行a++时并不是将a的值(地址)简单地加1 ,而是加上上一个数组元素所占的字节数。如果指针变量p的初值为&a[0],则p+i和a+i就是数组a[i]的地址。设d为基类型所占字节数,那么a+1的实际地址为a+1*d,也就是说*(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。
[]是变址运算符,即将a[i]按a+i计算地址,然后找出此地址单元中的值。
(2)如果指针变量p1和p2都指向同一数组,如执行p2-p1,结果是p2-p1的值(两个地址之差)除以数组元素的长度。由此可知它们所指元素的相对距离。
(3)两个地址不能相加,如p1+p2是无实际意义的。
通过指针引用数组元素
eg1.有一个整型数组a,有10个元素,要求输出数组中的全部元素。
法1.下标法。
#include<stdio.h>
int main()
{
int a[10];
for(int i=0;i<10;i++)
{
scanf("%d",&a[i]);
}
for(int i=0;i<10;i++)
{
printf("%d\n",a[i]);
}
return 0;
}
法2.通过数组名计算数组元素地址,找出元素的值。
#include<stdio.h>
int main()
{
int a[10];
for(int i=0;i<10;i++)
{
scanf("%d",&a[i]);
}
for(int i=0;i<10;i++)
{
printf("%d\n",*(a+i));
}
return 0;
}
法3.用指针变量指向数组元素。
#include<stdio.h>
int main()
{
int a[10];
int*p;
for(p=a;p<(a+10);p++)
{
scanf("%d",p);
}
for(p=a;p<(a+10);p++)
{
printf("%d\n",*p);
}
return 0;
}
或
#include<stdio.h>
int main()
{
int a[10];
int*p=a;
for(int i=0;i<10;i++)
{
scanf("%d",p++);
}
p=a;
for(int i=0;i<10;i++,p++)
{
printf("%d\n",*p);
}
/*可改写为
for(int i=0;i<10;i++)
{
printf("%d\n",*p++);
}*/
return 0;
}
三种方法的比较:
(1)执行效率:法1=法2<法3。
(2)法1直观,不易出错;法2、法3不直观;法3简洁高效。
注意:
(1)不用p变化的方法而用数组名a变化的方法是不行的。因为数组名a代表数组首元素的地址,它是一个指针型常量,它的值在程序运行期间是固定不变的。
(2)指针变量可以指向数组以后的存储单元,如最后一个元素为a[9],而指针变量指向a[10],编译时不出错,但运行结果不是预期的,这是程序逻辑上的错误,比较隐蔽、难以发现。
(3)指向数组的指针变量可以带下标。在程序编译时,对下标的处理方法是转换为地址的,对p[i]处理成*(p+i)。若p当前指向a[3],则p[2]代表a[3+2],即a[5],易出错,建议少用。
(4)多种技巧:
a.p++使p指向下一元素a[1],然后执行*p,得到下一个元素a[1]。
p++;
*p;
b.++和*同优先级,结合方向为自右向左,先引用p的值,实现*p的运算,然后再使p自增1.
*p++;
c.++(*p)表示p所指向的元素加1,如果p=a,则++(*p)相当于++a[0],若a[0]=3,则执行后a[0]=4。
d.(初学者不建议多用,但应知道)如果p指向a数组中第i个元素a[i],则:
*(p- -)相当于a[i- -],先对p进行取值,再使p自减;
*(++p)相当于a[- -i],先使p自加,再取值;
*(- -p)相当于a[- -i],先使p自减,再取值。
用数组名作函数参数
以变量名和数组名作为函数参数的比较
实参类型 | 变量名 | 数组名 |
---|---|---|
要求形参的类型 | 变量名 | 数组名或指针变量 |
传递的信息 | 变量的值 | 实参数组首元素的地址 |
通过函数调用能否改变实参的数 | 不能 | 能 |
注意:
(1)实参数组名代表一个固定的地址,或者说是指针常量,但形参数组名并不是一个固定的地址,而是按指针变量处理。
(2)在函数调用进行虚实结合后,它的值就是实参数组首元素的地址。在函数执行期间,它可以再被赋值。
void fun(arr[],int n)
{
printf("%d\n",*arr);
arr=arr+3;
printf("%d\n",*arr);
}
eg1.将数组a中n个整数按相反顺序存放。
#include <stdio.h>
void inv(int x[],int n)
{
int temp,m=(n-1)/2;
for(int i=0;i<=m;i++)
{
int j=n-1-i;
temp=x[i];
x[i]=x[j];
x[j]=temp;
}
return;
}
int main()
{
int a[10]={3,7,9,11,0,6,7,5,4,2};
for(int i=0;i<10;i++)
{
printf("%d ",a[i]);
}
printf("\n");
inv(a,10);
for(int i=0;i<10;i++)
{
printf("%d ",a[i]);
}
printf("\n");
return 0;
}
或
#include <stdio.h>
void inv(int*x,int n)
{
int temp,*p,*i,*j,m=(n-1)/2;
i=x;
j=x+n-1;
p=x+m;
for(;i<=p;i++,j--)
{
temp=*i;
*i=*j;
*j=temp;
}
return;
}
int main()
{
int a[10]={3,7,9,11,0,6,7,5,4,2};
for(int i=0;i<10;i++)
{
printf("%d ",a[i]);
}
printf("\n");
inv(a,10);
for(int i=0;i<10;i++)
{
printf("%d ",a[i]);
}
printf("\n");
return 0;
}
或
#include <stdio.h>
void inv(int*x,int n)
{
int temp,*p,*i,*j,m=(n-1)/2;
i=x;
j=x+n-1;
p=x+m;
for(;i<=p;i++,j--)
{
temp=*i;
*i=*j;
*j=temp;
}
return;
}
int main()
{
int a[10]={3,7,9,11,0,6,7,5,4,2};
int*p=a;
for(int i=0;i<10;i++,p++)
{
printf("%d ",*p);
}
printf("\n");
p=a;
inv(p,10);
for(p=a;p<a+10;p++)
{
printf("%d ",*p);
}
printf("\n");
return 0;
}
归纳分析:如果有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况:
(1)形参和实参都用数组名。
(2)实参用数组名,形参用指针变量。
(3)实参形参都用指针变量。
(4)实参为指针变量,形参为数组名。
注意:如果用指针变量作实参,必须先使指针变量有确定值,指向一个已定义的对象。
eg2.用指针方法对10个整数按由大到小顺序排序。
#include <stdio.h>
void sort(int x[],int n)
{
for(int i=0;i<n-1;i++)
{
int k=i;
for(int j=i+1;j<n;j++)
{
if(x[j]>x[k])
{
k=j;
}
if(k!=i)
{
int t=x[i];
x[i]=x[k];
x[k]=t;
}
}
}
}
int main()
{
int i,a[10];
int*p=a;
for(i=0;i<10;i++)
{
scanf("%d",p++);
}
p=a;
sort(p,10);
for(p=a,i=0;i<10;i++)
{
printf("%d ",*p);
p++;
}
printf("\n");
return 0;
}
或
void sort(int*x,int n)
{
for(int i=0;i<n-1;i++)
{
int k=i;
for(int j=i+1;j<n;j++)
{
if(*(x+j)>*(x+k))
{
k=j;
}
if(k!=i)
{
int t=*(x+i);
*(x+i)=*(x+k);
*(x+k)=t;
}
}
}
}
通过指针引用多维数组
多维数组元素的地址
a[i]等价于*(a+i);
a[i][j]等价于*(a[i]+j)或*(*(a+i)+j);
注意:
(1)a[i]在一维数组中代表a数组序号为i的元素的存储单元,在二维数组中只是一个地址。如:a[1]=&a[1][0]。a数组1行0列的元素地址表示方法:a[1]+0。
(2)在不同计算机、不同的编译环境、不同的时间输出二维数组的地址,由于分配内存情况不同,所显示的地址可能是不同的。但是显示的地址有共同规律。
二维数组a有关指针
表示形式 | 含义 |
---|---|
a | 二维数组名,指向一维数组a[0],即0行首地址 |
a[0],*(a+0),*a | 0行0列元素地址 |
a+1,&a[1] | 1行首地址 |
a[1],*(a+1) | 1行0列元素a[1][0]的地址 |
a[1]+2,*(a+1)+2,&a[1][2] | 1行2列元素a[1][2]的地址 |
*(a[1]+2,*(*(a+1)+2),a[1][2] | 1行2列元素a[1][2]的值 |
指向多维数组元素的指针变量
指向数组元素的指针变量
eg.有一个3x4的二维数组,要求用指向元素的指针变量输出二维数组各元素的值。
#include <stdio.h>
int main()
{
int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}};
int*p;
for(p=a[0];p<a[0]+12;p++)
{
if((p-a[0])%4==0&&p!=a[0])
{
printf("\n");
}
printf("%4d",*p);
}
return 0;
}
指向由m个元素组成的一维数组的指针变量
eg.输出二维数组任一行任一列元素的值。
#include <stdio.h>
int main()
{
int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}};
int(*p)[4],i,j;//p指向一个包含4个元素的一维数组
p=a;
scanf("%d %d",&i,&j);
printf("a[%d,%d]=%d\n",i,j,*(*(p+i)+j));
return 0;
}
注意:
(1)p两侧的括号不可省。若省略,[]运算级别高,因此p先与[4]结合,再与*结合,属于指针数组。
(2)p=a表示p的值是&a[0],p=&a表示p指向一维数组(行)。
(3)p的类型是int()[4]型,基类型是一维数组,其长度是16字节。
用指向数组的指针作函数参数
eg1.有一个班,3个学生,各学4门课,计算总平均分数以及第n个学生的成绩。
#include <stdio.h>
void average(float*p,int n)
{
float*p_end;
float sum=0,aver;
p_end=p+n-1;
for(;p<=p_end;p++)
{
sum=sum+(*p);
aver=sum/n;
}
printf("average=%5.2f\n",aver);
}
void search(float(*p)[4],int n)
{
for(int i=0;i<4;i++)
{
printf("%5.2f ",*(*(p+n)+i));
}
}
int main()
{
float score[3][4]={{65,67,70,60},{80,87,90,81},{90,99,100,98}};
average(*score,12);
search(score,2);
return 0;
}
5.通过指针引用字符串
字符串引用方式
字符串的引用
(1)用字符数组存放一个字符串,可以通过数组名和下标引用字符串中的一个字符,也可以通过数组名和格式声明"%s"输出该字符串。
#include <stdio.h>
int main()
{
char string[]="I love China!";
printf("%s\n",string);
printf("%c\n",string[7]);
return 0;
}
(2)用字符指针变量指向一个字符串常量,通过字符指针变量引用字符串常量。
#include <stdio.h>
int main()
{
char*string="I love China!";
//等价于 char*string;
// string="I love China!";
printf("%s\n",string);
return 0;
}
注意:
(1)C语言中只有字符变量,没有字符串变量。string只能指向一个字符类型数据。上面只是把“I love China!”的第一个字符的地址赋给指针变量string。
(2)可以对指针变量进行再赋值。
(3)通过字符数组名或字符指针变量可以输出一个字符串,而对一个数值型数组,不能用数组名输出它的全部元素。
字符串的输出
(1)逐个字符输出。
eg.将字符串a复制为字符串b,然后输出字符串b。
#include <stdio.h>
int main()
{
char a[]="I am a student.",b[20];
int i;
for(i=0;*(a+i)!='\0';i++)
{
*(b+i)=*(a+i);
}
*(b+i)='\0';
printf("string a is:%s\n",a);
for(i=0;b[i]!='\0';i++)
{
printf("%c",b[i]);
}
return 0;
}
(2)用指针变量访问字符串。
eg.同上。
#include <stdio.h>
int main()
{
char a[]="I am a student.",b[20],*p1,*p2;
p1=a;
p2=b;
for(;*p1!='\0';p1++,p2++)
{
*p2=*p1;
}
*p2='\0';
printf("string a is:%s\n",a);
printf("string b is:%s\n",b);
return 0;
}
字符指针作函数参数
eg.用函数调用实现字符串的复制。
(1)用字符数组名作为函数参数。
#include <stdio.h>
void copy_string(char from[],char to[])
{
int i=0;
while(from[i]!='\0')
{
to[i]=from[i];
i++;
}
to[i]='\0';
}
int main()
{
char a[]=" I am a teacher.";
char b[]="You are a student.";
printf("string a=%s\nstring b=%s\n",a,b);
copy_string(a,b);
printf("string a=%s\nstring b=%s\n",a,b);
return 0;
}
(2)用字符型指针变量作实参。
#include <stdio.h>
void copy_string(char from[],char to[])
{
int i=0;
while(from[i]!='\0')
{
to[i]=from[i];
i++;
}
to[i]='\0';
}
int main()
{
char a[]=" I am a teacher.";
char b[]="You are a student.";
char*from=a,*to=b;
printf("string a=%s\nstring b=%s\n",a,b);
copy_string(from,to);
printf("string a=%s\nstring b=%s\n",a,b);
return 0;
}
调用函数时实参与形参的对应关系
实参 | 形参 |
---|---|
字符数组名 | 字符数组名 |
字符数组名 | 字符指针变量 |
字符指针变量 | 字符指针变量 |
字符指针变量 | 字符数组名 |
使用字符指针变量和字符数组的比较
(1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第一个字符的地址)。
(2)可以对字符指针变量赋值,但不能对数组名赋值。
(3)数组可以在定义时对各元素赋初值,但不能用赋值语句对字符数组中全部元素整体赋值。
(4)编译时为字符数组分配若干存储单元,以存放各元素的值,而对字符指针变量,只分配一个存储空间。
(5)指针变量的值是可以改变的,而数组名代表一个固定的值(数组首元素的地址),不能改变。
(6)字符数组中各元素的值是可以改变的,但字符指针变量指向的字符串常量中的内容是不可以再赋值的。
(7)引用一个字符数组元素建议用下标法和地址法。指针变量指向数组。指针变量带下标的形式引用所指的字符串中的字符。
(8)用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。
eg.可变格式输出函数:
#include<stdio.h>
int main()
{
char*format;
int a=10;
float b=3.14;
format="a=%d,b=%f\n";
printf(format,a,b);
return 0;
}
6.指向函数的指针
函数指针
如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,这段**存储空间的起始地址(入口地址)**称为这个函数的指针。
用函数指针变量调用函数
(1)通过函数名调用函数。
#include <stdio.h>
int max(int x,int y)
{
int z;
if(x>y)
{
z=x;
}
else
{
z=y;
}
return z;
}
int main()
{
int a,b,c;
scanf("%d %d",&a,&b);
c=max(a,b);
printf("a=%d\nb=%d\nc=%d\n",a,b,c);
return 0;
}
(2)通过指针变量访问它所指向的函数。
#include <stdio.h>
int max(int x,int y)
{
int z;
if(x>y)
{
z=x;
}
else
{
z=y;
}
return z;
}
int main()
{
int(*p)(int,int); //定义指向函数的指针变量p
int a,b,c;
p=max;
scanf("%d %d",&a,&b);
c=(*p)(a,b);
printf("a=%d\nb=%d\nc=%d\n",a,b,c);
return 0;
}
定义和使用指向函数的指针变量
定义指向函数的指针变量的一般形式: 类型名(*指针变量名)(函数参数表列);
注意:
(1)在一个程序中,一个指针变量可以先后指向同类型的不同函数。
(2)如果要用指针调用函数,必须先使指针变量指向该函数。p=max;
(3)在给函数指针变量赋值时,只须给出函数名而不必给出参数。p=max;
(4)用函数指针变量调用函数时,只须将(*p)代替函数名即可,在(*p)之后的括号中根据需要写上实参。c=(*p)(a,b);
(5)对指向函数的指针变量不能进行算术运算。
(6)用函数名调用函数,只能调用所指定的一个函数,而通过指针调用函数可以根据不同情况先后调用不同函数。
用指向函数的指针作函数参数
指向函数的指针变量的一个重要用途是把函数的地址作为参数传递到其他函数。
如果在每次调用函数时,要调用的函数不是固定的,这时用指针变量就比较方便,只要给出不同的函数名作为实参即可,这种方法符合结构化程序设计方法原则,在程序设计中常使用。
eg.有两个整数a和b,由用户输入1,2或3.如输入1,程序就给出a和b中大者,输入2就给出a和b中小者,输入3则求a+b。
#include <stdio.h>
int fun(int x,int y,int(*p)(int,int))
{
int result;
result=(*p)(x,y);
printf("%d\n",result);
}
int max(int x,int y)
{
int z;
if(x>y)
{
z=x;
}
else
{
z=y;
}
printf("max=");
return z;
}
int min(int x,int y)
{
int z;
if(x<y)
{
z=x;
}
else
{
z=y;
}
printf("min=");
return z;
}
int add(int x,int y)
{
int z;
z=x+y;
printf("sum=");
return z;
}
int main()
{
int a=34,b=-21,n;
scanf("%d",&n);
if(n==1)
{
fun(a,b,max);
}
else if(n==2)
{
fun(a,b,min);
}
else if(n==3)
{
fun(a,b,add);
}
return 0;
}
7.返回指针值的函数
定义返回指针值的函数的一般形式为: 类型名 *函数名(参数表列)
eg.有a个学生,每个学生有b门课程的成绩。要求用指针函数实现在用户输入学生序号以后,能输出该学生的全部成绩。
#include<stdio.h>
float*search(float(*pointer)[4],int n)
{
float *pt;
pt=*(pointer+n);
return pt;
}
int main()
{
float score[3][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66}};
float*p;
int i,k;
scanf("%d",&k);
p=search(score,k);
printf("The scores of No.%d are :\n",k);
p=search(score,k);
for(i=0;i<4;i++)
{
printf("%5.2f\t",*(p+i));
}
return 0;
}
8.指针数组和多重指针
指针数组
一个数组,若其元素均为指针类型数据,称为指针数组。
定义一维指针数组的一般形式为 类型名*数组名[数组长度];
指针数组适合用来指向若干个字符串,使字符串处理更加灵活方便。
eg.将若干个字符串按字母顺序由小到大输出。
#include<stdio.h>
#include<string.h>
void sort(char*name[],int n)
{
char*temp;
int i,j,k;
for(i=0;i<n-1;i++)
{
k=i;
for(j=i+1;j<n;j++)
{
if(strcmp(name[k],name[j])>0)
{
k=j;
}
}
if(k!=i)
{
temp=name[i];
name[i]=name[k];
name[k]=temp;
}
}
}
void print(char*name[],int n)
{
int i;
for(i=0;i<n;i++)
{
printf("%s\n",name[i]);
}
}
int main()
{
char*name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer design"};
int n=5;
sort(name,n);
print(name,n);
return 0;
}
指向指针数据的指针
指向指针数据的指针变量简称为指向指针的指针。它的每一个元素是一个指针型的变量,其值为地址。
eg1.使用指向指针数据的指针变量。
#include<stdio.h>
int main()
{
char*name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer design"};
char**p;
int i;
for(i=0;i<5;i++)
{
p=name+i;
printf("%s\n",*p);
}
return 0;
}
eg2.有一个指针数组,其元素分别指向一个整型数组的元素,用指向指针数据的指针变量,输出整型数组各元素的值。
#include<stdio.h>
int main()
{
int a[5]={1,3,5,7,9};
int *num[5]={&a[0],&a[1],&a[2],&a[3],&a[4]};
int**p,i;
p=num;
for(i=0;i<5;i++)
{
printf("%d ",**p);
p++;
}
return 0;
}
指针数组作main函数的形参
在某些情况下,main函数可以有参数,如int main(int argc,char*argv[])
argc和argv就是main函数的形参,是程序的“命令行参数”。
命令行的一般形式为 命令名 参数1 参数2······参数n
命令名是可执行文件名(此文件包含main函数),命令行参数应当都是字符串。
二、结构体
1.定义和使用结构体变量
建立结构体类型
由不同类型数据组成的组合型的数据结构,称为结构体。
声明一个结构体类型的一般形式为
struct 结构体名
{成员表列};
花括号内是该结构体所包含的子项,称为结构体的成员。
对各成员都应进行类型声明,即 类型名 成员名;
成员表列也称为域表,每一个成员是结构体中的一个域。成员名命名规则与变量名相同。
注意:
(1)结构体类型可以设计出多种,各自包含不同的成员。
(2)成员可以属于另一个结构体类型。
补充:“.”成员运算符,优先级最高。
定义结构体类型变量
先声明结构体类型,再定义该类型的变量。
这种方式是声明类型和定义变量分离,在声明类型后可以随时定义变量,比较灵活。
struct Student(结构体类型名) student1,student2(结构体变量名);
在声明类型的同时定义变量。
这种方法能直接看到结构体的结构,比较直观,在写小程序时用此方式比较方便,但写大程序时,往往要求对类型的声明和对变量的定义分别放在不同的地方使程序结构清晰,便于维护。
struct Student(结构体名)
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30](成员表列);
}student1,student2(变量名表列);
不指定类型名而直接定义结构体类型变量。
这种方法不出现结构体名,显然不能再以此结构体类型去定义其他变量,用得不多。
其一般形式为
struct
{
成员表列
}变量名表列;
注意:
(1)结构体类型与结构体变量是不同的概念,只能对变量赋值,存取,运算,分配空间。
(2)结构体类型中的成员名可以与程序中的变量名相同,但二者不代表同一对象。
(3)对结构体变量中的成员(即“域”)可以单独使用,它的作用与地位相当于普通变量。
结构体变量的初始化和引用
#include<stdio.h>
int main()
{
struct Student
{
long int num;
char name[20];
char sex;
char addr[20];
}a={10101,"Li Hua",'M',"123 Beijing Road"};
printf("NO.%ld\nname:%s\nsex:%c\naddress:%s\n",a.num,a.name,a.sex,a.addr);
return 0;
}
注意:
(1)在定义结构体变量时可以对它的成员初始化。初始化列表是用花括号括起来的一些常量,这些常量依次赋给结构体变量中的各成员。
(2)引用结构体变量中成员的值的引用方式为 结构体变量名.成员名
(3)只能对结构体变量中的各个成员分别进行输入输出,不能输出结构体变量名来达到输出结构体变量全部成员的值。
(4)如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级地找到最低一级的成员。只能对最低级的成员进行赋值,存取,运算。
(5)对结构体变量的成员可以进行各种运算,如赋值,加法,自加。
(6)同类的结构体变量可以互相赋值。
(7)可以引用结构体变量成员的地址,也可以引用结构体变量的地址。
2.使用结构体数组
定义结构体数组
定义结构体数组一般形式是
(1)struct 结构体名
{成员表列} 数组名[数组长度];
(2)先声明一个结构体类型,然后再用此类型定义结构体数组:
结构体类型 数组名[数组长度];
对结构体数组初始化的形式是在定义数组的后面加上:
={初值表列};
结构体数组的应用举例
#include <stdio.h>
struct Student
{
int num;
char name[20];
float score;
};
int main()
{
struct Student stu[5]={{10101,"Zhang",78},{10103,"Wang",98.5},{10106,"Li",86},{10108,"Ling",73.5},{10110,"Sun",100}};
struct Student temp;
const int n=5;
int i,j,k;
for(i=0;i<n-1;i++)
{
k=i;
for(j=i+1;j<n;j++)
{
if(stu[j].score>stu[k].score)
{
k=j;
}
temp=stu[k];
stu[k]=stu[i];
stu[i]=temp;
}
}
for(i=0;i<n;i++)
{
printf("%6d%8s%6.2f\n",stu[i].num,stu[i].name,stu[i].score);
}
return 0;
}
3.结构体指针
指向结构体变量的指针
指向结构体对象的指针变量既可指向结构体变量,也可指向结构体数组中的元素,指针变量的基类型必须与结构体变量的类型相同。
#include<stdio.h>
#include<string.h>
int main()
{
struct Student
{
long num;
char name[20];
char sex;
float score;
};
struct Student stu_1;
struct Student*p;
p=&stu_1;
stu_1.num=10101;
strcpy(stu_1.name,"Li Lin");
stu_1.sex='M';
stu_1.score=89.5;
printf("No.%ld\nname:%s\nsex:%c\nscore:%5.1f\n",stu_1.num,stu_1.name,stu_1.sex,stu_1.score);
printf("No.%ld\nname:%s\nsex:%c\nscore:%5.1f\n",(*p).num,(*p).name,(*p).sex,(*p).score);
return 0;
}
注意:
如果p指向一个结构体变量stu,以下3种用法等价:
(1)stu.成员名
(2)(*p).成员名
(3)p->成员名
指向结构体数组的指针
#include <stdio.h>
struct Student
{
int num;
char name[20];
char sex;
int age;
};
struct Student stu[3]={{10101,"Li Lin",'M',18},{10102,"Zhang Fang",'M',19},{10104,"Wang Min",'F',20}};
int main()
{
struct Student*p;
printf("No. Name sex age\n");
for(p=stu;p<stu+3;p++)
{
printf("%5d %-20s %2c %4d\n",p->num,p->name,p->sex,p->age);
}
return 0;
}
注意:
(1)如果p的初值为stu,即指向stu的第一个元素,p+1后,p就指向下一个元素。
(2)p是一个指向struct Student类型对象的指针变量,它用来指向一个struct Student类型的对象,不应用来指向stu数组元素中的某一成员。
用结构体变量和结构体变量的指针作函数参数
将一个结构体变量的值传递给另一个函数的三种方法:
(1)用结构体变量的成员作参数。
(2)用结构体变量作实参。
(3)用指向结构体变量(或数组元素)的指针作实参,将它们的地址传给形参。
#include<stdio.h>
#define N 3
struct Student
{
int num;
char name[20];
float score[3];
float aver;
};
void input(struct Student stu[])
{
int i=0;
for(i=0;i<N;i++)
{
scanf("%d%s%f%f%f",&stu[i].num,&stu[i].name,&stu[i].score[0],&stu[i].score[1],&stu[i].score[2]);
stu[i].aver=(stu[i].score[0]+stu[i].score[1]+stu[i].score[2])/3;
}
}
struct Student max(struct Student stu[])
{
int i,m=0;
for(i=0;i<N;i++)
{
if(stu[i].aver>stu[m].aver)
{
m=i;
}
}
return stu[m];
}
void print(struct Student stud)
{
printf("\n成绩最高的学生是:\n");
printf("学号:%d\n姓名:%s\n三门课成绩:%5.1f,%5.1f,%5.1f\n平均成绩:%6.2f\n",stud.num,stud.name,stud.score[0],stud.score[1],stud.score[2],stud.aver);
}
int main()
{
struct Student stu[N],*p=stu;
input(p);
print(max(p));
return 0;
}