|

楼主 |
发表于 2020-8-28 23:47:50
|
显示全部楼层
本帖最后由 why 于 2020-8-31 23:05 编辑
这一章网上说是整本书最难的地方,但指针就相当于汇编中的寻址,所以对于有点汇编基础来说这一章应该问题不是很大
前天把一段c程序汇编成8086程序,发现大部分都是计算都是内存里直接操作进行的,上一节中最后也提到了这点
指针在内存中的情况:
有一个变量a的地址在内存2000:100处,内容为12h
有一个变量b的地址在内存2000:102处,内容为10h
有一个指针变量p1的地址在内存2000:0处,内容为a的地址(即2000:100)
有一个指针变量p2的地址在内存2000:2处,内容为b的地址(即2000:102)
指针变量始终只能存地址!
*p //指针变量p指向a地址中的内容。*p的值为a地址的内容12h,参与运算的是12h
p1=&a; //p1地址为2000:0,内容为a的地址2000:100,参与运算的是2000:100
p1=a; //p1地址为2000:0,内容为a的地址2000:100,参与运算的是2000:100 ;如果a为数组,那么p1内容为a[0],参与运算的是数组下标0
这是指针和数据在内存中的关系,理解了这个关系指针就相对简单多了
指针实际上就是内存中新定义了一个数据段,这个数据段在内存中有实际地址。指针数据段存储的是内存中其他数据的地址
第8章 善于利用指针
8.1指针是什么
将地址形象化地称为“指针”。以上就是通过它能找到以它为地址的内存单元
除了位置信息外,还需要数据的类型。确定去几个字节的数据
如果有一个变量专门用来存放另一变量的地址(即指针),则它称为“指针变量”
8.2指针变量
8.2.1
【例8.1】通过指针变量访问整型变量
解题思路:先定义2个整型变量,再定义两个指针变量,分别指向这两个整型变量,通过访问指针变量,可以找到它们所指向的变量,从而得到这些变量的值
编写程序:
- #include <stdio.h>
- int main()
- {
- int a=100,b=10;
- int *pointer_1,*pointer_2;
- pointer_1=&a;
- pointer_2=&b;
- printf("a=%d,b=%d\n",a,b);
- printf("*pointer_1=%d,*pointer_2=%d\n",*pointer_1,*pointer_2);
- system("pause");
- return 0;
- }
复制代码
8.2.怎样定义指针变量
定义指针变量的一般形式为:
类型名 *指针变量
如:
int *pointer_1,*pointer_2;
左端的int是在定义指针变量时必须指定的“基类型”。指针变量的类型用来指定此指针变量可以指向的变量的类型。如上面定义的基类型为int的指针变量,可以用来指向整型变量,但不能指向浮点型变量
指针变量时基本数据类型派生出来的类型,它不能离开基本类型而独立存在。下面都是合法的定义“
float *pointer_3 //pointer_3是指向float型变量的指针变量,简称float指针
char *pointer_4 //pointer_4是指向char型变量的指针变量,简称char指针
可以在定义指针变量的同时对它进行初始化,如:
int *pointer_1=&a,*pointer_2=&b; //定义指针变量pointer_1.pointer_2,并分别指向a,b
指针变量前面的”*“表示该变量为指针类型变量
在定义指针变量时必须指定基类型
一个变量的指针的含义包括两个方面,一是以存储单元编号表示的纯地址,一是它指向的存储单元的数据类型
8.2.3怎样引用指针变量
在引用指针变量时,可能有3种情况:
(1)给指针变量赋值。如:
p=&a; //把a的地址赋值给变量p
(2)引用指针变量指向的变量
如果已执行p=&a; 即指针变量p指向了整型变量a,则
printf(”%d“,*p);
其作用是以整数形式输出指针变量p所指向的变量的值,即变量a的值
如果有以下赋值语句:
*p=1;
表示将整数1赋值给p当前所指向的变量,如果p指向变量a,则相当于把1赋值给a
(3)引用指针变量的值。如:
printf("%o",p);
作用时以八进制数形式输出指针变量p的值,如果p指向了a,就是输出了a的地址,即&a
【例8.2】输入a和b两个整数,按先后大小的顺序输出
解题思路:用指针的方法来处理这个问题,不交换整型变量的值,而是交换两个指针变量的值
编写程序:
- #include <stdio.h>
- int main()
- {
- int *p1,*p2,*p,a,b;
- printf("pleass enter two integer numbers:");
- scanf("%d,%d",&a,&b);
- p1=&a; //p1的内容是&a
- p2=&b; //p2的内容是&b
- if(a<b) //如果&a的内容小于&b的内容
- {
- p=p1;
- p1=p2;
- p2=p; //把指针p1、p2中存储的a、b的地址进行交换
- }
- printf("a=%d,b=%d\n",a,b); //输出a和b的值
- printf("max=%d,min=%d\n",*p1,*p2); //输出较大、较小值
- system("pause");
- return 0;
- }
复制代码
8.2.4指针变量作为函数参数
函数的参数不仅可以是整型、浮点型、字符型等数据,还可以是指针类型。它的作用时将一个变量的地址传送到另一个函数中
【例8.3】对输入的两个整数按大小顺序输出。现用函数处理,而且用指针类型的数据做函数参数
编写程序:
- #include <stdio.h>
- int main()
- {
- void swap(int *p1,int *p2); //声明swap函数
- int a,b; //整型变量a,b
- int *pointer_1,*pointer_2; //整型指针变量*pointer_1,*pointer_2
- printf("please enter a and b:"); //输入两个值
- scanf("%d,%d",&a,&b);
- pointer_1=&a; //&a的地址赋值给*pointer_1
- pointer_2=&b; //&b的地址赋值给*pointer_2
- if(a<b)
- swap(pointer_1,pointer_2); //如果a的值小于b,调用函数swap
- printf("max=%d,min=%d\n",a,b); //输出最大最小值
- system("pause");
- return 0;
- }
- void swap(int *p1,int *p2)
- {
- int temp; //定义整型数据暂存变量temp
- temp=*p1; //整型指针p1地址赋值给temp
- *p1=*p2; //整型指针p2地址赋值给p1
- *p2=temp; //整型指针temp地址赋值给p2
- }
复制代码
这个例子和上个例子有点区别,上个例子是对调指针中数据的地址,最终完成的数据对调
这个例子是对调了指针地址,完成的数据对调
指针实际上就是内存中新定义了一个数据段,这个数据段在内存中有实际地址。指针数据段存储的是内存中其他数据的地址
指针在内存中的情况:
有一个变量a的地址在内存2000:100处,内容为12h
有一个变量b的地址在内存2000:102处,内容为10h
有一个指针变量p1的地址在内存2000:0处,内容为a的地址(即2000:100)
有一个指针变量p2的地址在内存2000:2处,内容为b的地址(即2000:102)
p1=&a; //a的地址送入p1中,p1地址为2000:0,内容为a的地址2000:100
*p1=*p2; //p2的地址等于p1,p1地址为2000:2,内容为b的地址2000:102
这是指针和数据在内存中的关系,理解了这个关系指针就相对简单多了
【例8.4】对输入的两个整数按大小顺序输出
解题思路:常识调用swap函数来实现题目要求,在函数中改变形参(指针变量)的值,希望由此改变实参(指针变量)的值
编写程序:
- #include <stdio.h>
- int main()
- {
- void swap(int *p1,int *p2);
- int a,b;
- int *pointer_1,*pointer_2;
- printf("please enter two integer numbers:");
- scanf("%d,%d",&a,&b);
- pointer_1=&a;
- pointer_2=&b;
- if(a<b)
- swap(pointer_1,pointer_2);
- printf("max=%d,min=%d\n",*pointer_1,*pointer_2);
- system("pause");
- return 0;
- }
- void swap(int *p1,int *p2)
- {
- int *p;
- p=p1;
- p1=p2;
- p2=p; //指针值的交换
- }
复制代码
上题中的错误在于,想用形参的值返回到main函数中去。在swap函数中确实完成了指针值交换,但c语言中实参和形参之间的数据时单向的”值传递“。只能从主函数传递实际参数给swap,swap不能再把函数返回给main函数
【例8.5】输入3个正数a,b,c,要求按由大到小的顺序将它们输出。用函数实现
解题思路:用swap函数交换两个变量的值,用exchange函数改变3个变量的值
编写程序:
- #include <stdio.h>
- int main()
- {
- void exchange(int *q1,int *q2,int *q3);
- int a,b,c,*p1,*p2,*p3;
- printf("please enter therr numbers:");
- scanf("%d,%d,%d",&a,&b,&c);
- p1=&a;
- p2=&b;
- p3=&c;
- exchange(p1,p2,p3);
- printf("The order is:%d,%d,%d\n",a,b,c);
- system("pause");
- return 0;
- }
- void exchange(int *q1,int *q2,int *q3)
- {
- void swap(int *pt1,int *pt2);
- if(*q1<*q2)
- swap(q1,q2);
- if(*q1<*q3)
- swap(q1,q3);
- if(*q2<*q3)
- swap(q2,q3);
- }
- void swap(int *pt1,int *pt2)
- {
- int temp; //定义整型数据暂存变量temp
- temp=*pt1; //整型指针p1地址赋值给temp
- *pt1=*pt2; //整型指针p2地址赋值给p1
- *pt2=temp; //整型指针temp地址赋值给p2
- }
复制代码
8.3通过指针引用数组
8.3.1数组元素的指针
所谓数组元素的指针就是数组元素的地址
可以用一个指针变量指向一个数组元素。如:
int a[10]={1,3,5,7,9,11,13,15,17,19};
int *p;
p=&a[0]; //数组a[0]的地址送入指针变量p中
引用数组元素可以用下标法(如a[3]),也可以用指针法,即通过指向数组元素的指针找到所需的元素。使用指针法能是目标程序质量高(占内存少,运行效率高)
下面两个语句是等价的
p=&a[0]; //数组a[0]的地址送入指针变量p中
p=a; //数组a的首元素地址送入指针变量p中。实际上就是a[0],并不是送入整个数组的元素
8.3.2在引用数组元素时指针的运算
当数组指向数组元素时可以指向加减运算
8.3.3通过指针引用数组元素
引用一个数组元素,可以用下面两种方法:
(1)下标法:如a[ i ]
(2)指针法:如*(a+i)或*(p+1)其中a是数组名,p的内容是a。【例8.6】有一个整型数组a,有10个元素,要求输出数组中的全部元素
解题思路:引用数组中各元素的值有3种方法:
(1)下标法
(2)通过数组名计算数组元素地址
(3)用指针变量指向数组元素
分别写出程序
编写程序:
//下标法
- #include <stdio.h>
- int main()
- {
- int a[10];
- int i;
- printf("please enter 10 integer numbers:");
- for(i=0;i<10;i++)
- scanf("%d",&a[i]);
- for(i=0;i<10;i++)
- printf("%d",a[i]); //数组元素用数组名加下标循环输出
- printf("%\n");
- system("pause");
- return 0;
- }
复制代码
//通过数组名计算数组元素地址,找出元素的值
- #include <stdio.h>
- int main()
- {
- int a[10];
- int i;
- printf("please enter 10 integer numbers:");
- for(i=0;i<10;i++)
- scanf("%d",&a[i]);
- for(i=0;i<10;i++)
- printf("%d",*(a+i));
- printf("\n");
- system("pause");
- return 0;
- }
复制代码 //用指针变量指向数组元素
- #include <stdio.h>
- int main()
- {
- int a[10];
- int *p,i;
- printf("please enter 10 integer numbers:");
- for(i=0;i<10;i++)
- scanf("%d",&a[i]);
- for(p=a;p<(a+10);p++)
- printf("%d",*P); //循环输出*p的内容,*p的内容是个数据的地址,每循环一次指向下一个数据地址
- printf("\n");
- system("pause");
- return 0;
- }
复制代码
以上3种方式输出的结果是一致的
【例8.7】通过指针变量输出整型数组a的10个元素
解题思路:用指针变量p指向数组元素,通过改变指针变量的值,使p先后指向a[0]~a[9]各元素
编写程序:
- #include <stdio.h>
- int main()
- {
- int *p,i,a[10];
- p=a; //*p的内容等于a[0]
- printf("please enter 10 integer numbers:");
- for(i=0;i<10;i++)
- scanf("%d",p++); //循环把十进制字符送入到p对应的数组,每次循环p指向下一数组元素
- for(i=0;i<10;i++,p++) //注意这是变量p已经经过10次循环了,p=(a+10)
- printf("%d ",*p); //循环输出*p的内容,*p的内容是变量a的地址,实际是输出a的内容,每次输出*p加1指向下一数值元素
- printf("\n");
- system("pause");
- return 0;
- }
复制代码
课本上用这个例子来执行错误,就是为了让我们察觉到错误存在在哪里,修改程序让指针重新指向a
- #include <stdio.h>
- int main()
- {
- int *p,i,a[10];
- p=a; //*p的内容等于a[0]
- printf("please enter 10 integer numbers:");
- for(i=0;i<10;i++)
- scanf("%d",p++); //循环把十进制字符送入到p对应的数组,每次循环p指向下一数组元素
- for(i=0;i<10;i++,p++) //这时*p指向(&a+10)位置
- p=a; //重新让*p的内容变为a
- printf("%d ",*p); //循环输出*p的内容,*p的内容是变量a的地址,实际是输出a的内容,每次输出*p加1指向下一数值元素
- printf("\n");
- system("pause");
- return 0;
- }
复制代码
程序正确执行
8.3.4用数组名作函数参数
【例8.8】将数组a中n个整数按相反顺序存放
解题思路:将a[0]与a[n-1]对换,再将a[1]与a[n-2]对换……直到将a[int(n-1)/2]与a[n-int(n-1)/2-1]对换,今用循环处理此问题,设两个”位置指示变量“i和j,i的初值为0,j的初值为n-1.将a[ i ]与a[j]交换,然后使i的值加1,j的值减1,再将a[ i ]与a[j]对换,直到i=(n-1)/2为止。
用一个函数inv来实现交换,实参用数组名a,形参可用数组名,也可用指针变量名
编写程序:
- #include <stdio.h>
- int main()
- {
- void inv(int x[ ],int n); //声明inv函数
- int i,a[10]={3,7,9,11,0,6,7,5,4,2}; //定义一位数组
- printf("The priginal array:\n"); //输出”The priginal array:“后换行
- for(i=0;i<10;i++)
- printf("%d ",a[i]); //循环输出数组a中的字符
- printf("\n"); //换行
- inv(a,10); //传送数据到inv函数
- printf("The array has been inverted:\n"); //输出”The array has been inverted::“后换行
- for(i=0;i<10;i++)
- printf("%d ",a[i]); //十进制循环输出a数组中的元素
- printf("\n");
- system("pause");
- return 0;
- }
- void inv(int x[ ],int n)
- {
- int temp,i,j,m=(n-1)/2; //定义整型变量
- for(i=0;i<=m;i++) //定义循环次数为i≤(10-1)/2=5(四舍五入)
- {
- j=n-1-i; //j为字符串最后一个字符
- temp=x[i];
- x[i]=x[j];
- x[j]=temp; //执行首尾字符调换,每次执行i+1,
- }
- return;
- }
复制代码
【例8.9】改写上一例子,用指针变量做参数
编写程序:
- #include <stdio.h>
- int main()
- {
- void inv(int *x,int n);
- int i,arr[10],*p=arr; //定义一维数组,指针变量p指向arr首元素
- printf("The orginal array:\n"); //输出“"The orginal array:”
- for(i=0;i<10;i++,p++)
- scanf("%d",p); //循环输入10个十进制整型数值到*p指向的变量arr中
- printf("\n");
- p=arr; //*p的内容为arr[0]的地址
- inv(p,10); //给inv函数传递参数
- printf("The array has been inverted:\n");
- for(p=arr;p<arr+10;p++)
- printf("%d ",*p); //循环输出*p指向地址中的内容
- printf("\n");
- system("pause");
- return 0;
- }
- void inv(int *x,int n)
- {
- int *p,m,temp,*i,*j; //定义变量
- m=(n-1)/2; //m为5
- i=x; //i指向arr[0]
- j=x+n-1;
- p=x+m; //p指向arr[0+5]
- for(;i<=p;i++,j--) //设置循环次数(地址相减得出5),每次循环a[i]指向下一数组元素,a[j]指向上一数组元素
- {
- temp=*i;
- *i=*j;
- *j=temp; //实现数组首尾元素对调
- }
- return;
- }
复制代码
如果用指针变量作实参,必须先使指针变量有确定值,直线给一个已定义的对象【例8.10】用指针方法对10个整数按由大到小顺序排序
解题思路:在主函数中定义数组a存放10个整数,定义int *型指针变量p并指向a[0]。定义函数sort使数组a中的元素按由大到小的顺序排列。在主函数中调用sort函数,用指针变量p作实参。sort函数的形参用数组名。
编写程序:
- #include <stdio.h>
- int main()
- {
- void sort(int x[ ],int n);
- int i,*p,a[10];
- p=a;
- printf("please enter 10 integer numbers:");
- 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");
- system("pause");
- return 0;
- }
- void sort(int x[ ],int n)
- {
- int i,j,k,t;
- for(i=0;i<n-1;i++)
- {
- k=i;
- for(j=i+1;j<n;j++)
- if(x[j]>x[k])
- k=j; //较大值的下标往后兑换
- if(k!=i)
- {
- t=x[i];
- x[i]=x[k];
- x[k]=t;
- }
- }
- }
复制代码
有10个数需要按从小到大的顺序排序
冒泡排序法:两两比较,发现较大的值立刻调换往后边,执行一轮最后一个数为最大者,循环执行直到第一个数为最小值,每次循环不用再对比已经排好序的
选择排序法:两两比较,发现较小值把下标往后兑换,同时把较小值调换到最前边的位置,让这个数再次和后边的数比较,不停在最前边位置更新出最小值,一轮比较得出最前边的值为最小值,每次循环不用再对比已经排好序的
8.3.5通过指针引用多维数组
这一节本来没有什么难度,但是书上说的看一遍就出现各种难度了。实际就是偏移地址,但课本上从头到尾都没有说到问题的根本,越看越难
【例8.12】有一个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++) //p的内容为a[0]元素的地址,循序次数12(地址相减),每次循环指向下一元素地址
- {
- if((p-a[0])%4==0)
- printf("\n"); //如果p的内容减去a[0]元素的地址除4余0,执行输出换行。实际就是每输出4次换行一次
- printf("%4d",*p); //否则输出指针p指向的地址中的内容
- }
- printf("\n");
- system("pause");
- return 0;
- }
复制代码
【例8.13】输出二维数组任意行任意列元素的值
编写程序:
- #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; //指针变量指向包含4个一维数组的一维元素
- p=a; //p指向a[0]
- printf("please enter row and colum:"); //输出“输入两个数:”
- scanf("%d,%d",&i,&j); //把输入的数送入&i,&j
- printf("a[%d,%d]=%d\n",i,j,*(*(p+i)+j)); //输出a[i][j]的内容,p的值加1即指向下一个一维数组
- system("pause");
- return 0;
- }
复制代码
【例8.14】有一个班,3个学生,各学4门课,计算总平均分数以及第n个学生的成绩
编写程序:
- #include <stdio.h>
- int main()
- {
- void average(float *p,int n); //声明函数
- void search(float (*p)[4],int n); //声明函数
- float score[3][4]={{65,67,70,60},{80,87,90,81},{90,99,100,98}};//定义三维数组
- average(*score,12); //调用求平均分函数
- search(score,2); //调用查找第n个学生的成绩
- system("pause");
- return 0;
- }
- void average(float *p,int n)
- {
- float *p_end;
- float sum=0,aver;
- p_end=p+n-1; //p指向a[0]元素的地址,p_end指向a元素最后一个地址
- for(;p<=p_end;p++) //循环12次,每次循环p指向下一个元素地址
- sum=sum+(*p); //sum+*p指向的元素的值,赋值给sum
- aver=sum/n; //求平均分
- printf("average=%5.2f\n",aver); //以2为小数输出平均分
- }
- void search(float (*p)[4],int n) //p是指向具有4个元素的一维数组的指针
- {
- int i;
- printf("The score of No.%d are:\n",n); //输出main函数传递过来的n
- for(i=0;i<4;i++) //循序次数为4,每次循环i+1
- printf("%5.2f ",*(*(p+n)+i)); //以2位小数输出a[n][i]
- printf("\n");
- }
复制代码
【例8.15】在上例题的基础上,查找有一门以上课程不及格的学生,输出他们的全部课程的成绩
编写程序:
- #include <stdio.h>
- int main()
- {
- void search(float (*p)[4],int n);
- float score[3][4]={{65,57,70,60},{58,87,90,81},{90,99,100,98}};
- search(score,3);
- system("pause");
- return 0;
- }
- void search(float (*p)[4],int n)
- {
- int i,j,flag;
- for(j=0;j<n;j++)
- {
- flag=0;
- for(i=0;i<4;i++)
- if(*(*(p+j)+i)<60)
- flag=1;
- if(flag==1)
- {
- printf("No.%d fails,his scores are:\n",j+1);
- for(i=0;i<4;i++)
- printf("%5.2f ",*(*(p+j)+i));
- printf("\n");
- }
- }
- }
复制代码
8.4通过指针引用字符串
用字符数组存放一个字符串,可以通过数组名和下标引用字符串中一个字符,也可以通过数组名和格式声明”%s“输出该字符串
【例8.16】定义一个字符数组,在其中存放字符串”I love china!“,输出该字符串和第8个字符
编写程序:
- #include <stdio.h>
- int main()
- {
- char string[ ]="I love China!";
- printf("%s\n",string);
- printf("%c\n",string[7]);
- system("pause");
- return 0;
- }
复制代码
【例8.17】通过字符指针变量输出一个字符串
编写程序:
- #include <stdio.h>
- int main()
- {
- char *string="I love China!"; //*string指向"I love China!"的首字节
- printf("%s\n",string);
- system("pause");
- return 0;
- }
复制代码
【例8.18】将字符串a复制为字符串b然后输出字符串b
编写程序:
- #include <stdio.h>
- int main()
- {
- char a[ ]="I am a student.",b[20]; //定义a字符串并赋值,定义b为一维字符数组
- int i;
- for(i=0;*(a+i)!='\0';i++) //循序执行到a数组中出现ASCII码为0的字符为止
- *(b+i)=*(a+i); //a数组的内容顺序移动到b数组中
- *(b+i)='\0'; //b数组末尾加上\0
- printf("string a is:%s\n",a); //输出a数组中的字符串
- printf("string b is:");
- for(i=0;b[i]!='\0';i++) //循环执行到b[i]指向为\0的字符
- printf("%c",b[i]); //字符形式输出数组b
- printf("\n");
- system("pause");
- return 0;
- }
复制代码
【例8.19】用指针变量来处理8.18的问题
编写程序:
- #include <stdio.h>
- int main()
- {
- char a[ ]="I am a boy.",b[20],*p1,*p2;
- int i;
- 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);
- system("pause");
- return 0;
- }
复制代码
8.4.2字符指针做函数参数
【例8.20】用函数调用实现字符串的复制
编写程序:
//用字符数组名作为函数参数
- #include <stdio.h>
- int main()
- {
- void copy_string(char from[],char to[]); //声明函数
- char a[]="I am a tcacher."; //字符数组a赋值
- char b[]="You are a student."; //字符数组a赋值
- printf("string a=%s\nstring b=%s\n",a,b); //输出字符数组a.b
- printf("copy string a to string b:\n");
- copy_string(a,b); //调用函数
- printf("\nstring a=%s\nstring b=%s\n",a,b);
- system("pause");
- return 0;
- }
- void copy_string(char from[],char to[])
- {
- int i=0;
- while(from[i]!='\0') //循环到a数组出现\0为止
- {
- to[i]=from[i];i++; //将a数组的值赋值给b数组,每次循环a,b数组同时指向下以元素
- }
- to[i]='\0'; //复制完毕给b数组元素末尾加上\0
- }
复制代码
//用字符型指针变量作实参
- #include <stdio.h>
- int main()
- {
- void copy_string(char from[],char to[]); //声明函数
- char a[]="I am a tcacher."; //字符数组a赋值
- char b[]="You are a student."; //字符数组a赋值
- char *from=a,*from=b; //定义指针变量分别指向a,b
- printf("string a=%s\nstring b=%s\n",a,b); //输出字符数组a.b
- printf("copy string a to string b:\n");
- copy_string(from,to); //调用函数
- printf("\nstring a=%s\nstring b=%s\n",a,b);
- system("pause");
- return 0;
- }
- void copy_string(char from[],char to[])
- {
- int i=0;
- while(from[i]!='\0') //循环到a数组出现\0为止
- {
- to[i]=from[i];i++; //将a数组的值赋值给b数组,每次循环a,b数组同时指向下以元素
- }
- to[i]='\0'; //复制完毕给b数组元素末尾加上\0
- }
复制代码
//用字符指针变量作形参和实参
- #include <stdio.h>
- int main()
- {
- void copy_string(char *from,char *to);
- char *a="I am a tcacher."; //字符指针a指向"I am a tcacher."
- char b[]="You are a student."; //字符数组b赋值
- char *p=b; //字符指针p指向b
- printf("string a=%s\nstring b=%s\n",a,b);
- printf("copy string a to string b:\n");
- copy_string(a,p);
- printf("\nstring a=%s\nstring b=%s\n",a,b);
- system("pause");
- return 0;
- }
- void copy_string(char from[],char to[])
- {
- for(;*from!='\0';from++,to++) //循序执行到a数组元素为\0为止,每次循环a、b数组同时指向下一元素
- {
- *to=*from; //a数组元素的值赋值给b数组元素
- }
- *to='\0'; //\0赋值给b数组有效元素末尾
- }
复制代码
以上几种编写方法输出的结果都一样
8.4.3使用字符指针变量和字符数组的比较
用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是由区别的
字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址
赋值方式,可以对字符指针变量赋值,但不能对数组变量名赋值
存储单元的内容,编译时为字符数组分配若干存储单元,以存放各元素的值,而字符指针变量,只分配一个存储单元
指针变量的值是可以改变的,而字符数组代表一个固定的值(数组元素的地址),不能改变
【例8.21】改变指针变量的值
- #include <stdio.h>
- int main()
- {
- char *a="I love China!"; //字符指针变量指向字符串首字节,
- a=a+7; //
- printf("%s\n",a);
- system("pause");
- return 0;
- }
复制代码
字符数组中各元素的值是可以改变的(可以对它们再赋值),但字符指针变量指向的字符串常量中的内容是不可以被取代的(不能对它们再赋值)
引用数组元素。对字符数组可以用下标法(用数组名和下标)引用一个数组元素(如a[5]),也可以使用地址法(如*[a+5])
用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。如:
char *format;
format=”a=%d,b=%f\n“; //指向格式字符
printf(format,a,b); //相当于printf("a=%d,b=%f\n",a,b);
这种printf函数称为可变格式输出函数
也可以用字符数组实现。如:
char format[]=”a=%d,b=%f\n“;
printf(format,a,b)
但使用字符数组时,只能采用在定义数组时初始化或逐个对元素赋值的方法,而不能用复制语句对数组整体赋值。如:
char format[];
format=”a=%d,b=%f\n“; //非法
8.5指向函数的指针
8.5.1什么是函数指针
函数名就是函数的指针,它代表函数的起始地址
可以定义一个指向函数的指针变量,用来存放某一函数的起始地址,这就意味着此指针变量指向该函数。如:
int (*p)(int,int);
定义p是一个指向函数的指针变量,它可以指向函数类型为整型且有两个整型参数的函数。此时,指针变量p的类型用int(*)(int,int)表示
8.5.2用函数指针变量调用函数
【例8.22】用函数求整数a和b中的大者
编写程序:
//通过函数名调用
- #include <stdio.h>
- int main()
- {
- int max(int,int);
- int a,b,c;
- printf("please enter a and b:");
- scanf("%d,%d",&a,&b);
- c=max(a,b);
- printf("a=%d\nb=%d\nmax=%d\n",a,b,c);
- system("pause");
- return 0;
- }
- int max(int x,int y)
- {
- int z;
- if(x>y)
- z=x;
- else
- z=y;
- return (z);
- }
复制代码
//通过指针变量调用它所指向的函数
#include <stdio.h>
int main()
{
int max(int,int); //声明函数
int (*p)(int,int); //定义函数指针p
int a,b,c;
p=max; //函数指针p指向max函数,p的值为max的地址
printf("please enter a and b:");
scanf("%d,%d",&a,&b);
c=(*p)(a,b); //调用函数指针变量p。p的值为max,从max函数处执行程序
printf("a=%d\nb=%d\nmax=%d\n",a,b,c);
system("pause");
return 0;
}
int max(int x,int y)
{
int z;
if(x>y)
z=x;
else
z=y;
return (z);
}
运行结果与上边一致
8.5.3怎样定义和使用指向函数的指针变量
定义指向函数的指针变量的一般形式为:
类型名 (*指针变量名)(函数参数列表)
如:int (*p)(int,int); 这里得“类型名”是指函数返回值的类型
由于优先级的关系,“*指针变量名”用圆括号括起来就是函数变量。
说明:
定义指向函数的指针变量,它只能指向定义时指定的类型的函数,并且要和形参的函数列表对应
如果要用指针调用函数,必须先使指针变量指向该函数
在给函数指针变量赋值时,只须给出函数名而不必给出参数
用函数指针变量调用函数时,值需要将(*p)代替函数名即可,在(*p)之后的括号中根据需要写上实参
对执行函数的指针变量不能进行算术运算
【例8.23】输入两个数,然后让用户选择1或2,选1时调用max函数,输出二者中的大者,选2时调用min函数,输出二者中的小数
编写程序:
- #include <stdio.h>
- int main()
- {
- int max(int,int);
- int min(int x,int y);
- int (*p)(int,int);
- int a,b,c,n;
- p=max;
- printf("please enter a and b:");
- scanf("%d,%d",&a,&b);
- printf("please choose 1 and 2:");
- scanf("%d",&n);
- if(n==1)
- p=max;
- else if(n!=1)
- p=min;
- c=(*p)(a,b);
- printf("a=%d\nb=%d\n",a,b);
- if(n==1)
- printf("max=%d\n",c);
- else
- printf("min=%d\n",c);
- system("pause");
- return 0;
- }
- int max(int x,int y)
- {
- int z;
- if(x>y)
- z=x;
- else
- z=y;
- return (z);
- }
- int min(int x,int y)
- {
- int z;
- if(x<y)
- z=x;
- else
- z=y;
- return (z);
- }
复制代码
8.5.4用指向函数的指针作函数参数
指向函数的指针变量的一个重要用途是把函数的入口地址作为参数传递到其他函数
【例8.2.4】有两个整数a和b,由用户输入1,2,3。如果输入1,程序就给出a和b中的大者,输入2就给出a和b中的小者,输入3就给出a和b的和
编写程序:
- #include <stdio.h>
- int main()
- {
- int fun(int x,int y,int(*p)(int,int)); //声明函数fun
- int max(int,int); //声明函数max
- int min(int,int); //声明函数min
- int add(int,int); //声明函数add
- int a=34,b=-21,n;
- printf("please choose 1,2 or 3:");
- 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);
- system("pause");
- return 0;
- }
- int fun(int x,int y,int (*p)(int,int))
- {int result;
- result=(*p)(x,y); //根据输入选项设置*p指向的函数,返回的结果赋值给result
- 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("add=");
- return (z);
- }
复制代码
8.6返回指针值的函数
一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址,其概念与以前类似,只是返回的值的类型是指针类型而已
定义返回指针值的函数的原型一般形式为:
类型名 *函数名(参数列表)
【例8.25】有a个学生,每个学生有b门课程的成绩。要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现
编写程序:
- #include <stdio.h>
- int main()
- {
- float score[][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66}}; //定义3个学生4门功课的数组
- float *search(float(*pointer)[4],int n); //声明函数指针search
- float *p; //定义浮点型指针
- int i,k;
- printf("enter the number of student:");
- scanf("%d",&k);
- printf("The scores of No.%d are:\n",k);
- p=search(score,k); //调用search函数,返回值存放在p,指针p值为地址
- for(i=0;i<4;i++)
- printf("%5.2f\t",*(p+i)); //输出p+i指向的值,p为地址,每次加1指向下一个数组元素
- printf("\n");
- system("pause");
- return 0;
- }
- float *search(float(*pointer)[4],int n)
- {
- float *pt;
- pt=*(pointer+n); //数组下标加上k的值赋值给pt,得到一个地址
- return (pt);
- }
复制代码
【例8.26】对例8.25中的学生,找出其中有不及格的课程的学生及其学生号
编写程序:
- #include <stdio.h>
- int main()
- {
- float score[][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66}};
- float *search(float(*pointer)[4]); //声明函数
- float *p;
- int i,j;
- for(i=0;i<3;i++) //循环执行3次,每次给i加1,每次循序执行二维数组的下一个一维数组
- {
- p=search(score+i); //调用search函数,二位数组每加1执行的是下一个一位数组,得到float型数值
- if(p==*(score+i)) //p的值与数组数组中的元素比较如果相等
- {
- printf("No.%d score:",i+1); //输出i的值,i代表学号
- for(j=0;j<4;j++)
- printf("%5.2f ",*(p+j)); //循环4次输出*p指向的值,每次输出*p指向下一个数据
- printf("\n");
- }
- }
- system("pause");
- return 0;
- }
- float *search(float(*pointer)[4])
- {
- int i=0;
- float *pt;
- pt=NULL; //空字符
- for(;i<4;i++) //循环4次,每次循环指向下一个数组元素
- if(*(*pointer+i)<60) //如果数组中的值小于60,(*pointer+i)得到的是元素地址;*(*pointer+i)得到的是元素地址中的值
- pt=*pointer; //元素中的值赋值给pt
- return (pt);
- }
复制代码
8.7指针数组和多重指针
8.7.1什么是指针数组
一个数组,若其元素均为指针类型数据,称为指针数组,也就是数组中的每一个元素都存放一个地址,相当于一个指针变量。下面定义一个指针数组:
int *p[4];
由于[]比*优先级高,因为p先与[4]结合,形成一个p[4]的数组,然后在于p前面的*结合,形成一个指针,相当于p数组有4个元素,每个元素都是指针
注意: int (*p)[4]; 这是指向一维数组的指针变量
定义一维指针数组的一般形式:
类型名 *数组名[数组长度]
【例8.27】将若干字符串按字母顺序(由小到大)输出
编写程序:
- #include <stdio.h>
- #include <string.h>
- int main()
- {
- void sort(char *name[],int n); //声明sort函数
- void print(char *name[],int n); //声明print函数
- char *name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer design"}; //字符型指针数组name指向5个字符串
- int n=5;
- sort(name,n);
- print(name,n);
- system("pause");
- return 0;
- }
- void sort(char *name[],int n)
- {
- char *temp;
- int i,j,k;
- for(i=0;i<n-1;i++) //循环5次
- {
- k=i;
- for(j=i+1;j<n;j++) //循环4次
- if(strcmp(name[k],name[j])>0) //字符串比较。数组k大于数组j
- k=j; //把j的值赋值给k
- if(k!=i) //如果k被重新赋值
- {
- temp=name[i];
- name[i]=name[k];
- name[k]=temp; //调换指针数组元素中的地址(字符型指针数组中每个元素存放的是一个字符串的地址)
- }
- }
- }
- void print(char *name[],int n)
- {
- int i;
- for(i=0;i<n;i++) //循环5次
- printf("%s\n",name[i]); //以字符串形式从数组元素对应的首地址开始输出直到遇到\0为止
- }
复制代码
8.7.2指向指针数据的指针变量
在了解了指针数组的基础上,需要了解指向执行数据的指针变量,简称为指向指针的指针
下面定义一个指向指针数据的指针变量
char **p;
表示p指向一个字符指针变量(这个字符指针变量指向一个字符型数据)
【例 8.28】使用指向指针数据的指针变量
编写程序:
- #include <stdio.h>
- int main()
- {
- char *name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer design"};
- char **p; //定义p为字符型多重指针,**p指向*name,p的值是*name的地址
- int i;
- for(i=0;i<5;i++) //循环执行5次
- {
- p=name+i; //每循环依次指向下一数组元素,p得到首元素地址
- printf("%s\n",*p); //以字符串形式输出,p得到的首元素地址中的字符串
- }
- system("pause");
- return 0;
- }
复制代码
【例8.29】有一个指针数组,其元素分别指向一个整型数组的元素,用指向指针数据的指针变量,输出整型数组各元素的值。
编写程序:
- #include <stdio.h>
- int main()
- {
- int a[5]={1,3,5,7,9}; //定义a为一维整型数组
- int *num[5]={&a[0],&a[1],&a[2],&a[3],&a[4]}; //定义num为一维整型数组指针,每个指针指向a元素
- int **p,i;
- p=num; //p为指向num的指针
- for(i=0;i<5;i++)
- {
- printf("%d ",**p); /*循环输出**p,每次循环给p+1,p的值是num的地址,num的值是a[]的地址;**p则是指向num的的值(num的值是指向a[]的值,所以**p的值是a[]的值),p没加1,num指向下一个a数组元素*/
- p++;
- }
- printf("\n");
- system("pause");
- return 0;
- }
复制代码
8.7.3指针数组做main函数的形参
某些情况下,main函数可以有参数,即:
int main(int argc, char *argv[])
注意:如果用带参数的main函数,其第一个形参必定是int型,用来接收形参个数,第二个形参必须是字符指针数组,用来接收从操作系统命令行传来的字符串中首字符的地址
命令行的一般形式为
命令名 参数1 参数2……参数n
如果有一个名为file1的文件,它包含以下的main函数
int main(int argc,*argv[]) //main函数包含3个形参,一个字符指针。
{
while(argc>1) //argc值为3。
{
++argv; //数组参与运算的是下标。下标从0开始,++argv后为argv[1],数组的第2个元素
printf("%s\n",*argv); //输出*argv的值,*argv指向数组的第2个元素
--argc; //每循环一次先给argc-1,得到的值返回循环参与运算;argc--的话就是argc先给条件赋值,运算一次回来再减1
}
return
}
main函数指针指向情况:
argv→argv[0]→file1
argv[1]→china
argv[2]→beijing
以上程序得到的输出结果是:
china
beijing
8.8动态内存分配与指向它的指针变量
8.8.1什么是动态内存分配
汇编语言的时候我们不用建立stack,系统就能自动入栈出栈。这个栈等同于这里所说的栈,由编译系统自动分配
堆区:是需要程序员向系统申请才能使用的
8.8.2怎样建立内存的动态分配
对内存的动态分配是通过系统提供的函数库来实现的,主要有malloc,calloc,ferr,realloc这4个函数
1,用malloc函数开辟动态存储区
其函数原型为
void *mailloc(unsigned int size);
其作用时在内存的动态存储区中分配一个长度为size的连续空间。形参size的类型定位无符号整数(不允许为负数)。此函数的值(即”返回值“)是所分配区域的第一个字节的地址。如:
malloc(100); //开辟100个字节的临时分配域,函数值为其第一个字节的地址
注意指针的基类型为void,即不执行任何类型的数据,只提供一个内存地址。如果此函数未能成功执行(如内存空间不足),则返回空指针(NULL)
2,用calloc函数开辟动态存储区
其函数原型为
void *calloc(unsigned n,unsigend size);
其作用时在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组
用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。这就是动态数组。函数返回指向所分配域的第一个字节的指针;如果分配不成功,返回NULL。如:
p=calloc(50,4); //开辟50*4个字节的临时分配域,把首地址赋值给指针变量p
3,用realloc函数重新分配动态存储区
其函数原型为
void *realloc(void *p,unsigned int size);
如果已经通过malloc函数或calloc函数获得了动态空间,想改变其大小,可以用recalloc函数重新分配
用realloc函数将p所指向的动态空间的大小改变为size。p的值不变。如果重新分配失败,返回NULL。如:
realloc(p,50); //将p指向的已分配的动态空间改为50字节
4,用free函数释放动态存储区
其函数原型为
void free(void *p);
其作用时释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。p应是最近一次调用calloc或malloc函数时得到的函数返回值。如:
free(p); //释放指针变量p所指向的已分配的动态空间
free函数无返回值
以上这4个函数的声明在stdlib.h头文件中,在用到这些函数时应当用#include <stdlib.h>指令把stdlib.h头文件包含到程序文件中
8.8.3void指针类型
void成为空指针,它不指向任何一种具体的类型数据,值提供一个地址,不能存储数据。可以自动把指向的地址赋值给有基类型的指针,系统自动转换基类型
【例8.30】建立动态数组,输入5个学生的成绩,另外用一个函数放检查其中有无低于60分的。输出不合格的成绩
编写程序:
- #include <stdio.h>
- #include <stdlib.h>
- int main()
- {
- void check(int *); //声明check函数
- int *p1,i;
- p1=(int *)malloc(5*sizeof(int)); //开辟5个int型空间,转换成int指针赋值给p1,*p1指向开辟的5个int型临时空间
- for(i=0;i<5;i++)
- scanf("%d",p1+i); //循环输入5个十进制数据,存放在p1指向的空间中,每次循环p1指向下一个临时空间地址
- check(p1); //调用函数check,把p1传递给函数,p1的值为地址指向临时空间的第一个整型数据
- system("pause");
- return 0;
- }
- void check(int *p)
- {
- int i;
- printf("They are fail:");
- for(i=0;i<5;i++) //循环5次
- if(p[i]<60) //如果p指向的临时空间中的数值小于60
- printf("%d ",p[i]); //输出p指向的这个值
- printf("\n");
- }
复制代码
8.9有关指针的小结
小结也就不写了我也不想看了,全是概念。既然指针如此重要,并且直接操作内存的,整章很少提及内存地址的相关知识。用的是纯地址来讲解的指针,如果没有先看汇编。这一章不可能这么快看完,也不可能理解。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|