一、指针、指针变量的引用 1.一个变量的地址称为该变量的指针 例如,地址2000是变量 i 的指针
2.一个变量专门用来存放另一变量的地址(即指针),则它称为指针变量
3.定义指针变量: 类型名 * 指针变量名 指针变量的值是地址
二、指针变量作函数参数
数组元素作实参时,参数传递具有单向性,即实参的改变可以影响形参,而形参的改变无法影响实参。
假设已经定义了一个函数,其原型为:
void swap(int x, int y);
假设函数的作用是将两个形参(x, y)的值交换,今有以下的函数调用:
swap(int a[1], int a[2]);
当数组名作函数参数时,实参和形参的改变可以相互影响。因为实参数组名代表首元素的地址,而形参是用来接收从实参传递过来的数组首元素的地址的,因此形参是一个指针变量。
把变量名作为函数参数与把数组名作为函数参数相互比较,可以得到:
例子: 利用指针变量当形参,将数组a中的n个整数按逆序排列。 代码如下:
# include<stdio.h>
int main()
{
void inv(int x[], int n);
int i, a[10];
printf("Please enter 10 integer numbers:\n");
for(i=0;i<10;i++)
scanf("%d", &a[i]);
inv(a,10);
printf("The array has been inverted:\n");
for(i=0;i<10;i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
void inv(int x[], int n)
{
int temp, j;
for(j=0;j<=(n-1)/2;j++)
{
temp = x[j];
x[j] = x[n-j-1];
x[n-j-1] = temp;
}
}
运行输入数值测试:
这里重点注意函数的形参与指针结合使用方法。分析循环时,注意分析循环结束条件,以及在循环体中使用数组的下标与数组元素的个数、定义的变量的关系表达式。
案例2:使用指针方法给一个数组排序
实现代码:
#include "stdio.h"
void main()
{void exchange(int *q1,int *q2,int *q3);
int a,b,c,*p1,*p2,*p3;
scanf("%d,%d,%d",&a,&b,&c);
p1=&a;p2=&b;p3=&c;
exchange(p1,p2,p3);
printf("The order is:%d,%d,%d",a,b,c);
printf("The order is:%d,%d,%d",*p1,*p2,*p3);
}
void exchange(int *q1,int *q2,int *q3)
{void swap(int *m1,int *m2);
if(*q1<*q2) swap(q1,q2);
if(*q1<*q3) swap(q1,q3);
if(*q2<*q3) swap(q2,q3);
}
void swap(int *m1,int *m2)
{ int t;
t=*m1;
*m1=*m2;
*m2=t;
}
实现效果:
补充说明:这里我是使用了两种不同的输出排好序的数字的方法。 三、通过指针引用数组
引用一个数组元素,可以用以下两种方法: 1.下标法,如 a[i] 形式; 2.指针法,如 *(a+i)或 *(p+i)。其中 a 是数组名,p是指向数组元素的指针变量,其初值为 p=a;
测试案例:通过指针变量输出整型数组 a 的5个元素//指针法
代码如下:
# include<stdio.h>
int main()
{
int i,a[5],*p;
p=a;
printf("Please enter 5 integer numbers:\n");
for(i=0;i<5;i++)
scanf("%d",p++);
for(p=a,i=0;i<5;i++,p++)
printf("%d ",*p);
printf("\n");
return 0;
}
运行结果
注意:在使用指针方法输出数组时,不能使用数组的名称作为指针变量输出,它是一个指针常量。
案例2:使用指针方法将数组中最大的数与最后一个数交换顺序,最小数与数组的第一个数(即a[0])交换顺序。
实现代码:
#include "stdio.h"
void main()
{
void input(int a[],int size);
void change(int a[],int size);
void output(int a[],int size);
int a[10];
input(a,10);
change(a,10);
output(a,10);
}
void input(int a[],int size)
{
int *p=a;
for(;p<a+size;p++)
{
scanf("%d",p);
}
}
void change(int a[],int size)
{
int i,t=0;
void exchange(int *pa,int *pb);
for(i=0;i<size;i++)
{
if(*(a+t)>*(a+i))
{
t=i;
}
}
if(t!=0)
{
exchange(&a[t],&a[0]);
}
t=0;
for(i=0;i<size;i++)
{
if(*(a+t)<*(a+i))
{
t=i;
}
}
if(t!=size-1)
{
exchange(&a[t],&a[size-1]);
}
}
void exchange(int *pa,int *pb)
{
int t;
t=*pa;
*pa=*pb;
*pb=t;
}
void output(int a[],int size)
{
int *p=a;
for(p=a;p<a+size;p++)
{
printf("%d ",*p);
}
}
运行结果:
四、通过指针引用多维数组
1、多维数组元素的地址 首先通过图解二维数组的行列组成结构来进一步理解指针引用多维数组
根据多维数组的定义以及多维数组的图解分析,可以总结出多维数组的相关指针表示形式:
2、指向多维数组元素的指针变量
(1)指向由m个元素组成的一维数组的指针变量 案例:
# 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=a;
printf("please enter row and column:\n");
scanf("%d,%d",&i, &j);
printf("a[%d,%d]=%d\n", i,j,*(*(p+i)+j));
printf("\n");
return 0;
}
运行效果省略.
(2)用指向数组的指针做函数参数
案例:查找一门以上课程不及格的学生,并输出其全部成绩
代码如下:
#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);
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 score are:\n",j+1);
for(i=0;i<4;i++)
printf("%5.1f",*(*(p+j)+i));
printf("\n");
}
}
}
运行测试:
五、通过指针引用字符串 1、字符串的引用方式 (1)通过数组名和下标 思路:正常给字符变量赋值输出即可引用。 (2)通过指针变量 思路:定义个字符类型的指针变量,并给它赋初值。 最后输出验证。
2、字符指针做函数参数 这里还是通过一个输出字符串的案例来说明如何使用字符指针做函数参数
案例:将字符数组a中的字符串复制给字符串b,并输出验证
实现代码:
#include <stdio.h>
int main()
{
void copy_string(char from[], char to[]);
char a[]="I love china!";
char b[]="You are a teacher!";
printf("string a=%s\nstring b=%s\n",a,b);
printf("copy string a to string b:\n");
copy_string(a,b);
printf("string a=%s\nstring b=%s\n",a,b);
return 0;
}
void copy_string(char from[], char to[])
{
int i = 0;
while(from[i]!='\0')
{
to[i]=from[i];
i++;
}
to[i]='\0';
}
这里的形参、实参都是使用了字符数组名,也可以用字符指针变量。
具体实现代码省略,参数用*from,*to代替字符数组。
六、动态内存分配
建立内存的动态分配 接下来介绍四个函数来进行动态分配内存: 1、malloc 开辟动态存储区 函数原型:void *malloc(unsigned int size); 使用说明:使用该函数在动态存储区分配一个长度为size的连续空间,unsigned为无符号整型。 2、calloc 开辟动态存储区 函数原型:void *calloc(unsigned n,unsigned int size); 使用说明:使用该函数在动态存储区分配n个长度为size的连续空间, 3、realloc 重新开辟动态存储区 函数原型:void *realloc(void *p,unsigned int size); 使用说明:在使用前两个函数开辟内存空间的前提下,使用该函数重新分配大小。 4、free 释放动态存储区域 函数原型:void *free(void *p); 使用说明:其作用是释放指针变量 p 所指向的动态空间,使这部分空间能重新被其他变量使用。p 应是最后一次调用 calloc或 malloc 函数时得到的函数返回值
注意:在编写程序时要把"stdlib.h"的头文件放在预处理行的位置中
下面举一个复杂的例子来说明以上函数的用法 案例:通过创建动态链表输出学生的所有信息,其中学生信息定义一个结构体类型存放。
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct Student{
int num;
char name [20];
int age;
};
struct Node{
struct Student stu;
struct Node *next;
};
void main()
{struct Node *head,*p,*n;
int i;
n=(struct Node *)malloc(sizeof(struct Node));
(*n).stu.num=10010;
strcpy((*n).stu.name,"wang");
(*n).stu.age=13;
(*n).next=NULL;
p=n;
head=n;
for(i=0;i<2;i++)
{
n=(struct Node *)malloc(sizeof(struct Node));
scanf("%d%s%d",&((*n).stu.num),&((*n).stu.name),&((*n).stu.age));
(*n).next=NULL;
p->next=n;
p=n;
}
printf("\n");
for(p=head;p!=NULL;p=p->next)
{
printf("%d %s %d\n",(*p).stu.num,(*p).stu.name,(*p).stu.age);
}
}
输入值测试 运行结果:
|