Fork me on github

关于指针的使用示例

指针(指针与数组,指针与字符串,指针数组)

掌握指针、地址和数组间的关系;

掌握通过指针操作数组元素的方法;

掌握数组名作为函数参数的编程方法;

掌握指针、地址和字符串间的关系;

掌握通过指针操作字符串的方法。

 

编程示例提示

编程规范

容易出现的

单步调试程序

编程题

 

 

什么是指针?下面通过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)能正确地释放内存。如果pNULL指针,那么freep无论操作多少次都不会出问题。如果p不是NULL指针,那么freep连续操作两次就会导致程序运行错误。

示例代码:

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)用freedelete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。指针pfree或者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\0a的内容可以改变,如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窗口中输入ap,可以看到此时的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中。

如:输入tHow are you!

n5

输出sare you!

 

(5)输入一个整数,如果该数在[1,12]之内,则输出与该数相对应的月份的英文名称,否则输出***。用指针数组记录各月份英文单词的首地址。

输入:6     输出:June

输入:139   输出:***

(6)编写一个解密藏尾诗和藏头诗的程序。输入一首藏尾诗(假设只有4句),输出其藏尾的真实含义。

要求:用返回字符指针的函数实现(该指针返回最后一个汉字所在字符串中的位置)。通过使用指向函数的指针进行函数调用。注意:汉语一个文字是占用两个英文字符的空间。

输入:悠悠田园风

然而心难平

兰花轻涌浪

兰香愈幽静

输出:风平浪静

 

 

 

 

 2020-12-15

posted @ 2020-12-15 17:16  北孤清茶。  阅读(573)  评论(0编辑  收藏  举报