8.1 指针是什么
一个变量的地址就是该变量的“指针”
定义一个变量,系统为其分配空间 有地址指向变量空间(变量单元),地址就称为“指针”。
指针是内存空间的地址,存储单元就是所分配的内存空间。
直接访问:直接通过变量名访问。
间接访问:将变量i的地址存在另一个变量中,然后通过该变量来找到变量i的地址
指针:一个变量的地址。
指针变量:存放另一个变量的地址的变量。
8.2 指针变量
8.1.1 使用指针变量的例子
指针变量:存放另一个变量的地址的变量。
例8.1 通过指针变量访问整形变量
# include "stdio.h"
# include "string.h"
# include "math.h"
int main()
{
int a=100,b=200;
// * pointer_a表示pointer_a所指向的变量(对象)
// pointer_a(地址) 表示指向
int * pointer_a,* pointer_b;
// 把变量a的地址指针赋值给指针变量pointer_a
pointer_a=&a;
pointer_b=&b;
printf("a=%d,b=%d\n",a,b);
printf("* pointer_a=%d,* pointer_b=%d\n",* pointer_a,* pointer_b);
return 0;
}
8.2.2 怎样定义指针变量
定义指针变量一般形式:类型名 * 指针变量名;
说明:
指针变量前面的 * 表示该变量的类型为指针型变量
在定义指针变量时必须指定基类型。不指定基类型的话,我们不知道所指向的数据在内存中所占字节数和存放方式。
知道存放数据类型,才能按存储单元的长度以及数据的存储形式正确地取出该数据。
一个变量的指针含义包括两个方面,一是以存储单元编号表示的地址,一是它指向的存储单元的数据类型。
如何表示指针类型。指向整形数据的指针类型表示 int * , 读作“指向int的指针或简称“int指针”。
指针变量中只能存放地址,不要将一个整数赋给一个指针变量。如:
int * pointer_1,a=100; * pointer_1=100;//这是不合法的
8.2.3 怎样引用指针变量
在引用指针变量时,可能有3种情况:
- 给指针变量赋值。
- 引用指针变量指向的变量。如:pirntf(“%d”,* p);
- 引用指针变量的值。
注意:
- &为取地址符。&a是变量a的地址。
- *为指针运算符, *p 代表指针变量p指向的对象。
int main()
{
int *p,*p1,*p2,a,b;
printf("请输入两个数字:\n");
scanf("%d%d",&a,&b);
if(a<b)
{
p2=&a;//使p2变量指向变量a
p1=&b;//使p1变量指向变量b
}
printf("a=%d\tb=%d\n",a,b);
printf("max=%d\tmin=%d",* p1,* p2);//输出p1和p2所指向的变量
return 0;
}
8.2.4 指针变量作为函数参数
指针类型变量作为函数参数的作用:将一个变量的地址传送到另一个函数中。
例如:
# include "stdio.h"
/*
对输入的两个整数按大小顺序输出。
*/
int main()
{
void swap(int * p1,int * p2);
int a,b;
int * pointer_a,* pointer_b;
printf("请输入两个数\n");
scanf("%d%d",&a,&b);
pointer_a=&a;
pointer_b=&b;
if(a<b)
swap(pointer_a,pointer_b);
printf("max=%d\tmin=%d\n",a,b);
return 0;
}
void swap(int * p1,int * p2)
{
int temp;
/*
下面交换的是变量的值,不是地址值,temp是int类型,不是int指针类型
*/
temp= * p1;
* p1= * p2;
* p2= temp;
}
如果想通过函数调用得到n个要改变的值,可以这样做:
- 在主调函数中设n个变量,用n个指针变量指向它们。
- 设计一个函数,有n个指针形参。
- 在主调函数中调用这个函数,在调用时将这n个指针变量作为实参,将它们的地址传给该函数的形参;
- 在执行该函数的过程中,通过形参指针变量,改变它们所指的n个变量的值;
- 在主调函数中就可以使用这些改变了值的变量。
# include "stdio.h"
/*
对输入的两个整数按大小顺序输出。
*/
int main()
{
void exchange(int * p1,int * p2,int * p3);
int a,b,c;
int * pointer_a,* pointer_b,* pointer_c;
printf("请输入三个数\n");
scanf("%d%d%d",&a,&b,&c);
pointer_a=&a;
pointer_b=&b;
pointer_c=&c;
exchange(pointer_a,pointer_b,pointer_c);
printf("the order is %d\t%d\t%d\n",a,b,c);
/*
C语言中实参变量和形参变量之间的数据传递是单向的“值传递”方式
用指针变量做函数参数时同样要遵循这个规则。
不能通过执行调用函数来实现改变实参指针变量的值,但是可以改变实参指针变量所指的变量的值。
*/
printf("%d%d%d",* pointer_a,* pointer_b,* pointer_c);
return 0;
}
void exchange(int * p1,int * p2,int * p3)
{
void swap(int * sp1,int * sp2);
if( * p1<* p2) swap(p1,p2);
if( * p1<* p3) swap(p1,p3);
if( * p2<* p3) swap(p2,p3);
}
void swap(int * sp1,int * sp2)
{
int temp;
temp= * sp1;
* sp1= * sp2;
* sp2= temp;
}
8.3 通过指针引用数组
8.3.1 数组元素的指针
数组元素的指针就是数组元素的地址。
引用数组元素可以用下标法,也可以用指针法,使用指针法能使目标程序质量高。
C语言中,数组名代表数组元素中首元素的地址。因此,下面两个语句等价:
p=&a[0];
p=a;
8.3.2 在引用数组元素时指针的运算
在一定条件下,允许对指针进行加和减的算术运算。如:指针变量p指向数组元素
指针指向数组元素时,可以对指针进行一下运算:
- 加一个整数(用+或+=),如p+1
- 减一个整数(用-或-=),如p-1
- 自增运算,如p++,++p
- 自减运算,如p–,–p
- 两个指针相减,如p1-p2,这个两个指针变量要指向统一数组时,才有意义
分别说明:
- 如果指针变量p已指向数组中的一个元素,则p+1指向同一个数组中的下一个元素,p-1指向统一数组中的上一个元素。加1代表的是加上一个数组元素所占的字节数。
- 如果p的初值为&a[0] p+1等同于a+1
- *(a+5)指向的是a[5] [] 是变地址符 将a[i]按a[i+1]计算地址,然后找出地址单元中的值
- 如果指针变量p1和p2都指向统一数组,如执行p2-p1,再用两个地址之差除以数组元素的长度,这样就可以知道他们所知元素的相对距离。地址相加是没有意义的。
8.3.3 通过指针引用数组元素
- 下标法
- 指针法,如*(a+1)或者 *(p+1)
# include "stdio.h"
/*
有一个整形数组a,要求输出数组中的全部元素。
*/
int main()
{
int a[5];
int i;
printf("please enter 5 integer numbers:\n");
for (i=0;i<5;i++)
scanf("%d",a+i);
for (i=0;i<5;i++)
printf("%d\t",*(a+i));
return '\0';
}
注意:C语言没有像Java那样的异常检查机制。所以我们要尽量避免逻辑上的错误,如:数组下标越界的错误。
指向数组的指针变量也可以带下标,如p[i]:对p[i]处理成*(p+i),必须知道p指向数组哪个元素的地址。
利用指针引用数组元素,比较灵活方便,有不少技巧。
1、p++; *p;
p++使p指向下一元素a[1]。然后若再执行*p,则得到下一个元素a[1]的值。
2、*p++;
++和*同优先级,结合方向为自右向左,因此他等价于 *(p++),先引用p的值,实现 *p的运算,然后再使p自增一。
如:for(i=0;i<10;i++,p++)
printf(“%d”,* p);
可以改写为
for(i=0;i<10;i++)
printf(“%d”,* p++);
3、*(p++)和 *(++p)作用是否相同,不相同。前者是先取 *p的值,然后再使p+1后者是先使p+1 再取 *p
4、++(*p)。表示p所指向的元素值加1,如果p=a,那么++( *p)和++a[0]等价。
5、*(p–)== a[i–]; *(++p)== a[++i]; *(–p)== a[–i]
8.3.4 用数组名作函数参数
C编译都是将形参数组名作为指针变量来处理的。
所以:void swap(int arr[],int n) 可以改写成 void swap(int * arr,int n)
在函数调用进行虚实结合的方法都是采用“值传递”,它的值就是实参数组首元素的地址。函数执行期间,它可以再次被赋值。
# include "stdio.h"
/*
将数组中n个整数按相反顺序存放
*/
int main()
{
void inv(int *x,int n);
int i,a[10]={3,7,5,8,9,4,2,1,0,6};
printf("初始数组为\n");
for(i=0;i<10;i++)
printf("%d\t",a[i]);
printf("\n");
inv(a,10);
printf("调换顺序后的数组是\n");
for(i=0;i<10;i++)
printf("%d\t",a[i]);
printf("\n");
return 0;
}
void inv(int * x,int n)//*x: 传入数组首个元素地址, n:需要换几个数
{
int *i,*j,temp,*p,m=(n-1)/2;
i=x;
j=x+n-1;
p=x+m;
for(;i<=p;i++,j--)
{
temp=*i;
*i=*j;
*j=temp;
}
}
归纳分析:如果一个实参数组,要想在函数中改变次数组中的元素的值。实参和形参对应关系如下:
- 形参和实参都用数组名
- 实参用数组名,形参用指针变量
- 实参和形参都用指针变量
- 实参为指针变量,形参为数组名。 实参作为指针变量,必须先指针变量指向数组常量。
# include "stdio.h"
/*用指针法对10个整数按由大到小排序(选择排序).c*/
int main()
{
void sort(int a[],int n);
int *p,a[10],i;
p=a;
printf("请输入10个不相同的数\n");
for(i=0;i<10;i++)
scanf("%d",p++);
p=a;
sort(p,10);
for(i=0;i<10;i++)
printf("%d\t",* p++);
return 0;
}
void sort(int a[],int n)
{
int i,temp,j;
for(i=0;i<n-1;i++)
for(j=i+1;j<n;j++)
{
if(a[i]<a[j])
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
8.3.5 通过指针引用多维数组
1.多维数组元素的地址
C语言中,数组名代表数组元素中首元素的地址。
a[0] 代表以为数组a[0]中第0列元素地址,即&a [0] [0]
a[1]代表&a[1] [0]
所以a[0] [1]==a[0]+1(求取地址)
二维数组指针以及指针运算规律
二维数组a的有关指针
二维数组名指向行,一维数组名指向列。
在指向行的指针前面加一个*, 就转换为指针指向列的指针。
如:a 都是指向行的 加个 * ,就是a[0] [0]就列首地址
在指向列的指针前面加&,就成为指向行的指针。
2.指向多维数组元素的指针变量
指向数组元素的指针变量
# include "stdio.h"
/*遍历数组元素*/
int main()
{
int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
int *p;
for(p=a[0];p<a[0]+12;p++)
{
if((p-a[0])%4==0)
printf("\n");
printf("%d\t",*p);
}
return 0;
}
指向由m个元素组成的一维数组的指针变量
# include "stdio.h"
/*
输出任意一行任意一列的元素的值
*/
int main ()
{
int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
/*
int (* p)[4]
表示(* p)有4个元素,每个元素为整形。
相当于p所指的对象是有4 个整形元素的数组
p的类型不是 int* 而是int (*)[4] p被定义为指向一维数组的指针变量
*/
int (* p)[4],i,j;
p=&a;//这里不能写成p=a,这样表示p=a[0]
printf("请输入要输出的数的坐标(行和列)\n");
scanf("%d%d",&i,&j);
printf("a[%d][%d]=%d",i,j, * ( *(p+i)+j));
return 0;
}
用指向数组的指针作为函数参数
用指针变量作形参,来接受实参数组名传递来的地址。可以有两种方法:
- 用指向变量的指针变量
- 用指向一维数组的指针变量
# include "stdio.h"
/*
计算总平均分数以及第n个学生的成绩
*/
int main ()
{
void average(float *p,int n);
void search(float (* p)[4],int n);
float score[3][4]={{65,70,67,60},{80,87,90,81},{90,99,100,98}};
average( * score,12);
search(score,2);
return 0;
}
void average(float *p,int n)//*p 指的是二维数组0,0的坐标地址
{
float * p_end,sum=0,aver;
p_end=p+n-1;
for(;p<p_end;p++)
sum+= * p;
aver=sum/n;
printf("average=%.5f\n",aver);
}
/*
此时p只能指向一个包含4个元素的一维数组,不能指向一维数组中的某一元素。
p的值就是 该一维数组的起始位置。
score+i == p+i
*/
void search(float (* p)[4],int n)
{
int i;
printf("the score of No.%d are :\n",n);
for(i=0;i<4;i++)
printf("%5.2f\t",*(*(p+n)+i));
printf("\n");
}
8.4 通过指针引用字符串
使用字符串的更加灵活方便的方法——通过指针引用字符串。
8.4.1 字符串的引用方式
在C程序中,字符串是存放在字符数组中的。引用字符串可以有两种方法:
用数组存放一个字符串,可以通过数组名和下标引用字符串中一个字符,可以通过数组名和格式声明”%s”输出该字符串。
用字符指针变量指向一个字符串常量,通过字符指针变量引用字符串常量。
例如:
# include "stdio.h" int main() { /* 对字符指针变量p的初始化, 实际上是把字符串第一个元素地址赋值给指针变量 */ char * p="I love China."; /* %s是输出字符串所用的格式字符,在输出项中给出字符指针变量名p 系统会输出p所指向字符串第一个字符,然后自动使p加1,使之指向下一个字符 输出该字符,直到遇到字符串结束标志'\0'为止 */ printf("%s\n",p); return 0; }
在C语言中只有字符变量,没有字符串变量
8.4.2 字符指针作函数参数
如果想把一个字符串从一个函数“传递”到另一个函数,可以地址传递的方法。在被调用函数中可以改变字符换的内容,在主调函数中可以引用改变后的字符串。
例题8.20 用函数调用实现字符串的复制
1、用字符数组名作为函数参数
# include<stdio.h>
int main ()
{
void cope_string(char form[],char to[]);
char a[]="I am a teacher.";
char b[]="You are a student.";
printf("string a is %s\nstring b is %s\n",a,b);
copy_string(a,b);
printf("string a is %s\nstring b is %s\n",a,b);
return 0;
}
void copy_string(char form[],char to[])
{
int i=0;
for(;form[i]!='\0';i++)
{
to[i]=form[i];
}
to[i]='\0';
}
2、用字符型指针变量作实参
# include<stdio.h>
int main ()
{
void cope_string(char form[],char to[]);
char a[]="I am a teacher.";
char b[]="You are a student.";
char *p1,*p2;
printf("string a is %s\nstring b is %s\n",a,b);
p1=a;p2=b;
copy_string(p1,p2);
printf("string a is %s\nstring b is %s\n",a,b);
return 0;
}
void copy_string(char form[],char to[])
{
int i=0;
for(;form[i]!='\0';i++)
{
to[i]=form[i];
}
to[i]='\0';
}
3、用字符型指针变量作形参和实参
# include<stdio.h>
int main ()
{
void cope_string(char *form,char *to);
char *a="I am a teacher.";
char b[]="You are a student.";
char *p1,*p2;
printf("string a is %s\nstring b is %s\n",a,b);
p2=b;
copy_string(a,p2);
printf("string a is %s\nstring b is %s\n",a,b);
return 0;
}
void copy_string(char *form,char *to)
{
char temp;
for(;*form!='\0';form++,to++)
{
*to=*form;
}
*to='\0';
}
注意使用%s时不会输出’\0’,而使用%c时可以输出’\0’
PDF282页上有程序优化,自己去看
8.4.3 使用字符指针变量和字符数组的比较
字符数组和字符指针都能实现字符串的存储和运算
但二者有区别,主要有:
字符数组由若干个元素组成,每个元素中放一个字符,字符指针变量中存放的是地址,存的不是字符串。
赋值方式。可以对字符指针变量赋值,但不能对数组名赋值。
如:
char *a; a="I love China!"; //量字符串元素地址赋值给指针变量,合法。但赋值 //给a的不是字符串,而是字符串第一个元素的地址。
char str[14]; str[0]='I'; str="I love China!"; //数组名是地址,是常量,不能被赋值。
初始化的含义。
对字符指针变量赋初始值,赋予第一个元素的地址
定义字符数组,并把字符串赋值给各个元素
存储单元的内容
数组是若干个存储单元,存着各元素的值。
字符指针变量,只分配一个存储单元(Visual C++为指针变量分配4个字符)。
注意:指针必须先赋值(指定指针指向),在使用。
指针变量的值是可以改变的,数组名代表一个固定的值,不能改变。
字符数组中各个元素的值是可以改变的(可以对它们再赋值),但字符指针变量指向的字符串常量中的内容是不可以改变的。
引用数组元素。都可以用下标,都可以用指针 ,使用指针之前,确保指针指向相同数组
char *format; format="a=%d,b=%d\n"; printf(foramt,a,b); //因此只要改变指针变量所指的字符串,就可以该变输入输出的格式,这种printf函数称为可变格式输出函数。
使用字符数组时,只能采用在定义数组时初始化或逐个对元素赋值的方法。
8.5 指向函数的指针
8.5.1 什么是函数指针
定义了一个函数,在编译时,编译系统为函数代码分配了一段存储空间,这段存储空间的起始地址(又称入口地址),称为这个函数的指针。
int (*p)(int,int);
说明:
- 这表示定义一个指向函数的指针变量,用来存放某一函数的起始地址,这意味着此指针变量指向该函数。
- p的类型用int(*)(int,int)表示
8.5.2 用函数指针变量调用函数
调用函数:
1.通过函数名调用;
2.通过指向函数的指针变量调用该函数
例题8.22用函数求整数a和b中的大者
# include "stdio.h"
int main()
{
int (*p)(int,int);
int max(int a,int b);
p=max; //将函数指针变量指向max函数 ,将max函数的入口地址赋值给了p
int a=5,b=6,c;
c=(*p)(a,b);
printf("%d",c);
return 0;
}
int max(int a,int b)
{
return a>b?a:b;
}
注意:
int (*p)(int,int);
(*p):两侧括号不能省略,表示p先与 *结合 ,是指针变量,再与后面的()结合,()表示的是函数。表明这个指向变量指向的是函数。
如果写成:int *p(int,int);
这样相当于 int *(p(int,int)); 就成了声明一个p函数,这个函数的返回值是指向整形变量的指针。(主要是因为()的优先级高于 *)
8.5.3 怎样定义和使用指向函数的指针变量
一般形式:
类型名 (* 指针变量名) (函数参数列表);
说明:
1.定义指向函数的指针变量,只能指向规定的返回类型,且参数列表相同的函数。
2.如果要用指针调用函数,必须先使指针变量指向该函数。(指针变量先赋值再使用)
3.再给函数指针变量赋值时,只须给出函数名而不必给出参数
4.用函数指针变量调用函数时,直接用(*p)代替函数名,后面()填写相应的实参。
5.对指向函数的指针变量不能进行算术运算。进行加减是没有实际意义的
6.用函数名调用函数,只能调用一个,通过指针调用函数比较灵活,可以根据不同情况进行调用。
例题:输入两个数,让用户选择输出最大最小的数。
# include "stdio.h"
int main()
{
int (*p)(int,int);
int max(int a,int b);
int min(int a,int b);
p=max; //将函数指针变量指向max函数
int a=5,b=6,c,n;
printf("pleanse chose one or two:\n");
scanf("%d",&n);
if(n==1)p=max; //输入的是1的话则使指针指向max
if(n==2)p=min; //输入的是2的话则使指针指向min
c=(*p)(a,b);
printf("%d",c);
return 0;
}
int max(int a,int b)
{
return a>b?a:b;
}
int min(int a,int b)
{
return a>b?b:a;
}
8.5.4 用指向函数的指针做函数参数
例如:
void fun(int (*p)(int),int (*p2)(int,int))//定义函数,参数两个函数指针变量
{ //将两个函数名传递给函数指针变量
int a,b,i=3,j=5;
a=(*p)(i); //调用f1函数,i是参数
b=(*p2)(i,j); //调用f2函数,i,j是参数
}
函数只有被调用时,才分配存储单元。
使用函数指针变量,可以简化程序的编写,减少冗余代码,
通过一个例子来展示指针函数参数的应用
# include "stdio.h"
int main()
{
int (*p)(int,int);
int max(int a,int b);
int min(int a,int b);
int add(int a,int b);
int fun(int a,int b,int (*p)(int,int));
int a=5,b=6,c,n;
printf("pleanse chose one , two or three:\n");
scanf("%d",&n);
if(n==1)p=max; //输入的是1的话则使指针指向max
if(n==2)p=min; //输入的是2的话则使指针指向min
if(n==3)p=add; //输入的是3的话则使指针指向add
fun (a,b,p);
printf("%d",c);
return 0;
}
void fun(int a,int b,int (*p)(int,int))
{
printf("%d",(* p)(a,b));
}
int max(int a,int b)
{
return a>b?a:b;
}
int min(int a,int b)
{
return a>b?b:a;
}
int add(int a,int b)
{
return a+b;
}
8.6 返回指针值的函数
int *a (int x,int y);
在a的两侧分别为* 和 () 而()的优先级要比*的高,所以a先与()结合,显然这是函数的形式。
函数前有*, 表是这是一个指针型函数。
定义返回指针值的函数的一般形式:
类型名 * 函数名(参数列表);
例题:
# include <stdio.h>
int main()
{
float score[][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66}};
float * search(float (*p)[4],int n);
float * res;
int i,k;
printf("enter the number of student:\n");
scanf("%d",&k);
printf("the score of No.%d are:\n",k);
res=search(score,k);
for(i=0;i<4;i++,res++)
printf("%.2f\t",* res);
printf("\n");
return 0;
}
float * search(float (* p)[4],int n)
{
float *pt;
pt = * (p+n); //pt的值是&score[k][0]
return pt;
}
# include <stdio.h>
/*
找出成绩不及格的学号以及输出学生的分数
*/
int main()
{
float score[][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66}};
float * search(float (*p)[4]);
float * res;
int j,i;
for(j=0;j<3;j++)
{
res=search(score+j);
if(res==*(score+j))
for(i=0;i<4;i++,res++)
printf("%.2f\t",* res);
printf("\n");
}
return 0;
}
float * search(float (* p)[4])
{
float *pt;
int i;
pt=NULL;
for(i=0;i<4;i++)
if(*(*p+i)<60)
pt = * p; //成绩不合格的,pt的值是&score[k][0]
return pt;
}
8.7 指针数组和多重指针
8.7.1 什么是指针数组
一个数组,所有元素都是指针类型数据,称为指针数组。如:int *p[3];
优先级:()>[]>*
先与优先级高者结合,然后在与优先级低者结合。
int (*p)[4]; //这是指向一维数组的指针变量
指针数组比较适合用来指向若干个字符串,使字符串更加方便灵活。
# include <stdio.h>
# include <string.h>
/*
数组指针的简单使用
*/
int main()
{
void swap_string(char *str[],int n);
void print_str(char *str[],int n);
char *str[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer Design"};
int n=5;
swap_string(str,n);
print_str(str,n);
return 0;
}
void swap_string(char *str[],int n)
{
char *temp;
int i,k,j;
for(i=0;i<n-1;i++)
{
for(j=i+1;j<n;j++)
{
if(strcmp(str[i],str[j])>0)
{
temp=str[i];
str[i]=str[j];
str[j]=temp;
}
}
}
}
void print_str(char *str[],int n)
{
int i=0;
while(i<n)
{
printf("%s\n",str[i]);//按指针数组元素的顺序输出它们所指向的字符串
i++;
}
}
8.7.2 指向指针数据的指针
char * str[]={“Follow me”,”BASIC”}
char *p;
p=str[1];
p就是指向指针型数据的指针变量
定义一个指向指针数据的指针变量:char **p;
的结合性是从右到左 char *p 等同于 char *( *p);
(*p)表示这是一个指针变量,char * 表示p 指向的是一个字符型指针变量
# include "stdio.h"
# include "string.h"
/*
使用指向指针数据的指针变量
*/
int main()
{
char * str[]={"Follow me","BASIC","Great Wall"};
char **p;
int i;
for(i=0;i<3;i++)
{
p=str+i;
printf("%s\n",*p);
}
return 0;
}
# include "stdio.h"
# include "string.h"
/*
指针元素指向实型数据或整形数据。
*/
int main()
{
int a[]={1,3,5,7,9};
int * b[]={&a[0],&a[1],&a[2],&a[3],&a[4]};
int **p;
int i;
for(i=0;i<5;i++)
{
p=b+i;
printf("%d\n",*p); //*p是b[i]的地址,**p是a[i]的值
}
return 0;
}
如果在一个指针变量中存放一个目标变量的地址,这就是“单级间址”。指向指针数据的指针用的是“二级间址”方法。
8.7.3 指针数组作main函数的形参
/*
argc :参数的个数
argv :命令行参数
命令行一般形式: file China Bejing
命令名与个参数之间用空格分隔
file也算一个参数
命令行参数都是字符串
main函数有操作系统调用
*/
int main(int argc,char * argv[])
{
}
8.8 动态内存分配与指向它的指针变量
8.8.1 什么是内存的动态分配
动态存储区被称为栈
建立内存动态分配区域,以存放一些临时用的数据。这块区域被称为堆。
临时数据不必在程序中定义声明,无需关心其声明存在期。需要时随时开辟,不需要时随时释放。
由于未在声明部分定义他们为变量或数组,因此只能通过指针来引用。不能通过变量或数组引用。
8.8.2 怎样建立内存的动态分配
内存的动态分配是通过系统提供的库函数实现的:
malloc
其函数原型:
void * malloc (unsigned int size);
作用:在动态存储区分配一个长度为size的连续空间
返回值是所分配区域的第一个字节地址。
注意:
指针的基类型是void ,即不指向任何类型的数据,只提供一个地址。
如果函数没有成功执行(内存空间不足),则返回空指针
calloc
函数原型:
void * calloc(unsigned n,unsigned sized);
作用:在内存的动态存储区中分配n个长度为size的连续空间
用calloc函数可以为一维数组开辟动态存储空间,n为元素个数,每个元素长度为size。这就是动态数组。
函数返回指向所分配域的起始位置的指针;分配不成功,返回null
free
函数原型:
void free(void * p);
作用:是释放指针变量p所指向的动态空间,使这部分空间能被其他变量使用。p应是最近一次调用calloc或malloc函数时得到的函数返回值。
realloc
函数原型:
void * realloc (void *p,unsigned size);
作用: 修改已经通过malloc函数或calloc函数获得了动态空间
返回值p不变,重新分配不成功返回NULL
以上4个函数的声明在stdlib.h头文件中
C99标准以前,malloc,calloc,realloc函数的基类型为char,c99把这些函数的基类型定义为void,这种指针称为无类型指针。仅提供一个存地址,而不指向具体对象。
8.3.3 void 指针类型
C99允许使用基类型为void的指针空类型。
void类型应理解为空类型或不指向确定的类型,不应理解指向任何类型的数据
int a=3;
int *p1=&a;
char * p2;
void * p3;
p3=(void *)p1;
p2=(char *)p3;
当void指针赋值给不同积累性的指针变量(相反)时,编译系统会自动进行转换,不必用户自己进行强制转换。
赋值后p3得到a的地址,但不指向a,不能通过*p3输出a的值。
# include "stdio.h"
# include "stdlib.h"
/*
了解动态内存分配和void指针的使用
内存的动态分配主要应用于简历程序中的动态数据结构(如:链表)
*/
int main()
{
void check(int *);
int *p1,i;
/*
sizeof运算符测定不同系统中存放的一个整数的字节数。
p1是空类型不确定指向,所以要进行强转 表明指向整形数据
*/
p1=(int *)malloc(5*sizeof(int));
for(i=0;i<5;i++)
{
*(p1+i)=i+1;
}
check(p1);
free(p1);
return 0;
}
void check(int *p)
{
int i=0;
for(;i<5;i++)
{
if(p[i]<60)
printf("%d",p[i]);
}
printf("\n");
}
8.9 有关指针的小结
简单小结:
指针的含义,指针就是地址
指针是地址本身,指针变量是用来存放地址的变量
类型名表示的是指针指向的数据类型,而不是 指针是类型名。
什么叫“指向”?地址就意味着指向,因为通过地址能找到该地址的对象
void * 指针是一种特殊的指针,不指向任何的数据,如果需要用地址指向某类型的数据,应先对地址进行类型转换。
要深入掌握在对数组的操作中正确地使用指针,搞清楚指针的指向
指针变量的归纳比较:
指针运算
- 指针变量加(减)一个整数
- 指针变量赋值(赋值的是地址)
- 两个指针变量可以相减(指向同一数组)
- 两个指针变量比较(指向同一个数组,可以元素的相对位置)
指针变量可以有NULL(空值),表示该指针变量不指向任何变量。
NULL 在stdio.h预编译处理 # define NULL 0
指针的优点:
- 提高程序效率
- 在调用函数时当指针指向的变量值改变时,这些值能够为主调函数使用,即可以从函数调用中得到多个可改变的值。
- 可以实现动态内存分配。
- 本文链接:https://www.nscblog.top/posts/659d25f6/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。
若没有本文 Issue,您可以使用 Comment 模版新建。
GitHub Issues