关于指针的使用示例
指针(指针与数组,指针与字符串,指针数组)
掌握指针、地址和数组间的关系;
掌握通过指针操作数组元素的方法;
掌握数组名作为函数参数的编程方法;
掌握指针、地址和字符串间的关系;
掌握通过指针操作字符串的方法。
什么是指针?下面通过3个例子感受和理解一下。
例子1:
//下面是一个关于指针的例子,请通过该例子了解指针的作用、指针所代表的地址、指针自己的地址等概念。
#include <stdio.h>
void main()
{
int *p,q;
q=98;
printf("\n变量q被初始化赋值后其值为:%d ,其内存地址为:%d \n",q,&q); // &为取地址符
printf("\n在未指向任何变量时候,指针p自己的值为:%d \n",p);
p=&q; //现在让指针p指向变量q,此后p的值代表变量q的地址, *p代表q的值。
printf("\n在指向变量q后,指针p自己的值为:%d\n",p);
printf("\n指针p所指向的内存变量(实际上就是q)的值为:%d \n",*p);
printf("\n我们通过上面的输出应该可以看出:");
printf("指针p的值和它所指向的变量p的地址应该是相同的 \n");
printf("并且*p的值应该就是变量q的值。 \n\n");
}
例子2:
#include "stdio.h"
void swap(int p1,int p2) //没学指针前的两个数交换值
{
int temp;
temp = p1;
p1 = p2;
p2 = temp;
}
int main()
{
int x = 99,y = 66;
printf("交换前:%d %d\n",x,y);
swap(x,y);
printf("交换后:%d %d\n",x,y);
}
例子3:
#include "stdio.h"
void swap(int *p1,int *p2)
{
int temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main()
{
int x = 99,y = 66;
printf("交换前:%d %d\n",x,y);
swap(&x,&y);
printf("交换后:%d %d\n",x,y);
}
编程示例和提示
(1)利用指针作为参数,返回值
#include <stdio.h>
void change(int *p)
{
*p=9;
}
void main(void)
{
int a=5;
change(&a);
printf("a=%d\n",a);
}
讲解:在上述程序中,输出a=9。由此可见,假如函数需要有多个返回值,可以通过在输入参数中传递指针的方式实现。
(2)指针与数组
示例代码1:求一维数组中元素的和
#include <stdio.h>
int sum(int *array, int num)
{
int i;
int sum=0;
for (i=0;i<num;i++)
{
sum=sum+*(array+i);
// sum=sum+array[i];
}
return sum;
}
void main(void)
{
int a[5]={5,4,3,2,1};
int s=sum(a,5);
printf("sum=%d\n",s);
}
讲解:一维数组的名称也标识了数组元素的首地址
示例代码2:求二维数组中元素的和
#include <stdio.h>
int sum(int *a, int x, int y)
{
int i,j;
int sum=0;
for (i=0;i<x;i++)
{
for (j=0;j<y;j++)
{
sum=sum + *(a+i*y+j);//注意如何引用数组中的值
}
}
return sum;
}
void main(void)
{
int array[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
printf("sum=%d\n",sum(array[0],3,4));//注意参数的传递
}
(3)指针与字符串
示例代码:求字符串的长度
#include <stdio.h>
int strLen(char *a)
{
int cnt=0;
//字符串中一般用结束符'\0'来控制循环
while(*a!='\0')
{
a++;
cnt++;
}
return cnt;
}
void main(void)
{
char *p="Happy";
char a[80]="New year";
char *pStr=a;
printf("字符串%s长度为%d\n",p,strLen(p));
printf("字符串%s长度为%d\n",a,strLen(a));
printf("字符串%s长度为%d\n",pStr,strLen(pStr));
}
(4)动态分配空间
1)需要的头文件stdlib.h和 malloc.h
2)分配空间函数malloc的原型如下:
void * malloc(size_t size);
用malloc申请一块长度为length的整数类型的内存,程序如下:
int *p = (int *) malloc(sizeof(int) * length);
3) 释放空间函数free的原型如下:
void free( void * memblock );
free(p);
为什么free函数不象malloc函数那样复杂呢?这是因为指针p的类型以及它所指的内存的容量事先都是知道的,语句free(p)能正确地释放内存。如果p是NULL指针,那么free对p无论操作多少次都不会出问题。如果p不是NULL指针,那么free对p连续操作两次就会导致程序运行错误。
示例代码:
求n个整数的值,这里n从键盘输入
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
int sum(int *array, int num)
{
int i;
int sum=0;
for (i=0;i<num;i++)
{
sum=sum+array[i];
}
return sum;
}
void main(void)
{
int n;
int *p=NULL;
int i;
int s;
printf("请输入n:");
scanf("%d",&n);
p=(int *)malloc(sizeof(int)*n);
printf("输入%d个元素的值:",n);
for (i=0;i<n;i++)
{
scanf("%d",p+i);
}
s=sum(p,n);
printf("sum=%d\n",s);
}
讲解:数组在定义的时候,其中包含多少个元素,必须定下来。而进行动态分配空间则没有这样的限制,可以让用户输入需要的值,然后再进行空间分配。
(5)指针数组(即数组中存放的是指针)
例子:用指针数组存放5个字符串,然后将这些字符串从小到大排序后输出
#include <stdio.h>
#include <string.h>
void fsort(char *color[],int n)
{
int k,j;
char *temp;
for (k=1;k<n;k++)
{
for (j=0;j<n-k;j++)
{
if (strcmp(color[j],color[j+1])>0)
{
temp=color[j];
color[j]=color[j+1];
color[j+1]=temp;
}
}
}
}
void main(void)
{
int i;
char *pColor[]={"red","blue","yellow","green","purple"};
fsort(pColor,5);
for (i=0;i<5;i++)
{
printf("%s\n",pColor[i]);
}
}
(6)指向函数的指针
#include <stdio.h>
int sum(int *array, int num)
{
int i;
int sum=0;
for (i=0;i<num;i++)
{
sum=sum+*(array+i);
}
return sum;
}
void main(void)
{
int a[5]={5,4,3,2,1};
int s;//sum
int (*funptr)(int *,int);//定义了一个函数指针
funptr=sum;//给函数指针赋值,函数名代表函数地址 pointer
s=funptr(a,5);
printf("sum=%d\n",s);
}
讲解:定义函数指针的时候,注意将来要指向的函数的输入输出参数,返回值都需要一一对应。
编程规范
(1)不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。
(2)避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。
(3)动态内存的申请与释放必须配对,防止内存泄漏。
(4)用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
(5)任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。例如
char *p = NULL;
char *str = (char *) malloc(100);
容易出现的错误
(1)错误一:指针没有被赋值就使用
错误代码如下:
#include <stdio.h>
void main(void)
{
char *p ; //这里p指向一个不确定的单元,我们这里还没有分配空间存放字符串
scanf("%s",p);
printf("%s",p);
}
该程序编译链接,可能会有一个警告,说p没有赋初值。运行后,当输入值后,会发现出现错误提示对话框。
讲解:指针只有在被赋值后才能正确被使用。指针如果没有被赋值,其值是不确定的,即指向一个不确定的单元。使用这样的指针,可能会出现难以预料的结果。因此要有好的编程习惯,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
该程序编写正确的做法应该如下:
#include <stdio.h>
void main(void)
{
char a[80];
char *p=a;
scanf("%s",p);
printf("%s",p);
}
或者动态分配空间如下:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
void main(void)
{
char *p=(char *)malloc(sizeof(char)*80);
scanf("%s",p);
printf("%s",p);
free(p);
}
(2)错误二:内存分配虽然成功,但是尚未初始化就引用它。
犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。
(3)错误三:内存分配成功并且已经初始化,但操作越过了内存的边界。
例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for循环语句中,循环次数很容易搞错,导致数组操作越界。
(4)释放了内存却继续使用它。
错误代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
void main(void)
{
char *p=(char *)malloc(sizeof(char)*80);
scanf("%s",p);
free(p);
printf("%s",p);
}
该程序编译链接不会出现问题,但是,运行时候却发生了错误
讲解:在该程序中p所指向的空间已经被释放,然后p成为野指针(p的值没有变,但是,p所指向的空间已经不应该被访问了),而后面又用到p,就出现问题了。
如下改正后,虽然此时p的空间也被释放了,但是,不会出现运行时的问题,会有提示。
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
void main(void)
{
char *p=(char *)malloc(sizeof(char)*80);
scanf("%s",p);
free(p);
p=NULL;// 用free释放了内存之后,立即将指针设置为NULL,防止产生“野指针”
printf("%s",p);
}
(5)错误五:修改指针的指向的内容遇到的问题
错误代码如下:
#include <stdio.h>
void main(void)
{
char a[] = "hello";
char *p1=a;
char *p = "world"; // 注意p指向常量字符串
p1[0] = 'X'; //这一句没有问题
p[0] = 'X'; // 这一句有问题,但编译器不能发现该错误
printf("%s\n",a);
printf("%s\n",p);
}此程序编译链接不会发现错误,但是,运行的时候会出现错误对话框。
讲解:字符数组a的容量是6个字符,其内容为hello\0。a的内容可以改变,如a[0]= ‘X’。指针p指向常量字符串“world”(位于静态存储区,内容为world\0),常量字符串的内容是不可以被修改的。从语法上看,编译器并不觉得语句p[0]= ‘X’有什么不妥,但是该语句企图修改常量字符串的内容而导致运行错误。
单步调试程序
这里主要是观察指针值的变化,以及指针所指向的内存区域的值。要调试观察的代码示例如下:该代码用于求取数组中所有元素的值的和
#include <stdio.h>
int sum(int *array, int num)
{
int i;
int sum=0;
for (i=0;i<num;i++)
{
sum=sum+*(array+i);
}
return sum;
}
void main(void)
{
int a[5]={5,4,3,2,1};
int *p=NULL;
int s;//sum
p=a;
s=sum(a,5);
printf("sum=%d\n",s);
}
(1)设置断点,因为想观察p的值变化情况,所以,将断点进行如下设置
(2)按F5进入编译调试
程序在断点处停止,黄色的箭头指示的下一条要执行的语句。从上图的右下角watch窗口中输入a和p,可以看到此时的a值和p值。
(3)按F10单步调试
观察到p的值变为0,黄色箭头指向下一条要执行的语句。
再按F10一下,又执行一条语句,值的变化如下图
若要具体观察p所指向的内存区域的值,可以点击其左边的加号,展开。
编程题
(1)在数组中查找指定元素。输入一个正整数n(n<=10),然后输入n个整数存入数组a中,再输入一个整数x,在数组a中查找x,如果找到则输出相应的下标,否则输出“Not found!” 。
要求:定义和调用函数int search(int *list,int n, int x),在数组list中查找元素x,若找到则返回相应下标,否则返回-1。
(2)有n个学生,每个学生参加m门课程的考试,要求编写一函数,能检查n个学生有无不及格的课程。如果某一学生有一门或一门以上的课程不及格,就输出该学生的学号(学号从0算起,即0,1,2,……)及其全部课程成绩。
要求:用二维数组保存学生的成绩信息。用指针实现对数组的操作及作为函数调用的参数。
(3)输入5个字符串,输出其中长度最长的字符串
要求:这里是需要修改代码,不是自己从头到尾对代码的编写
程序代码如下:
#include<stdio.h>
#include<string.h>
int main()
{
int i;
char str[80],max[80];
printf("Input 5 strings:\n");
scanf("%s",str);
max=str;
for(i=1;i<5;i++)
{
scanf("%s",str);
if(max<str)
max=str;
}
printf("Max is:%s \n",max);
return 0;
}
(4)字符复制。输入一个字符串t和一个正整数n,将字符串s中从第n个字符开始的全部字符复制到字符串s中,再输出字符串s。
要求:用字符指针定义并实现函数strscpy(s,t,n),它的功能是将字符串t中从第n个字符开始的全部字符复制到字符串s中。
如:输入t:How are you!
n:5
输出s:are you!
(5)输入一个整数,如果该数在[1,12]之内,则输出与该数相对应的月份的英文名称,否则输出***。用指针数组记录各月份英文单词的首地址。
输入:6 输出:June
输入:139 输出:***
(6)编写一个解密藏尾诗和藏头诗的程序。输入一首藏尾诗(假设只有4句),输出其藏尾的真实含义。
要求:用返回字符指针的函数实现(该指针返回最后一个汉字所在字符串中的位置)。通过使用指向函数的指针进行函数调用。注意:汉语一个文字是占用两个英文字符的空间。
输入:悠悠田园风
然而心难平
兰花轻涌浪
兰香愈幽静
输出:风平浪静
2020-12-15