第6章 指针 学习目的与要求: 了解指针的概念和相关术语 熟练掌握指向变量、数组和字符串的指针变量的使用方法 了解指向函数的指针变量 第6章 指针 学习目的与要求: 了解指针的概念和相关术语 熟练掌握指向变量、数组和字符串的指针变量的使用方法 了解指向函数的指针变量 掌握返回指针值的函数 熟练掌握指针数组 了解二级指针的定义与使用
基本内容 指针简介 指针与数组 指针与字符串 指向函数的指针 返回指针值的函数 指针数组和多重指针 动态内存分配 本章小结
指针简介 指针是C语言里最强大的工具之一,正确灵活地运用指针,可以有效地表示和使用复杂的数据结构;并可动态分配存储空间,更好地利用内存资源 直接访问:按变量地址存取变量的值的方式。 间接访问:通过存放地址的变量的值(即地址)来存取最 终要访问的变量的值的方式。 指 针:即地址,反映访问变量的指向关系。
变量访问示例 2004 2000 2008 3010 int i,j,k,*p ; i=3; j=6; k=i+j; i 3 j 6 k 9 直接访问 2004 2000 2008 3010 int i,j,k,*p ; i=3; j=6; k=i+j; i 3 j 6 k 9 p=&i p 2000 间接访问
指针变量的定义 在C语言中,地址被称作指针。因此,变量的地址即称变量的指针。可以存储地址的变量称为指针变量 。 定义指针变量的一般形式为: 类型名 *指针变量名[=初始值]; 例如:int a=5,*p; p=&a;
指针变量说明 在定义指针变量时必须指定基类型。例如:int *p; 指针变量名前面的“*”号,表示该变量pointer是一个指针变量。 指针变量中只能存放地址。不要试图将一个整数赋给一个指针变量。 例如:int a=5; int *p=&a; 一般情况下,p作为指针变量名的第一个字母,以便于区分普通变量和指针变量。 例如:int *p=1000; × 注意区分“指针”和“指针变量”两个概念。
指针变量的引用 指针变量定义之后,必须将其与某个变量的地址相关联才能使用。可以通过指针运算符(*)访问指针变量所指的变量的值。 输入一个整数赋给某个变量,输出该变量的值和地址,并用指针变量完成对变量的操作。 #include <stdio.h> int main() { int x,*pointer=NULL; printf("输入一个整数:"); scanf("%d",&x); printf("x的值为:%d\n",x); //输出变量x的值 printf("x的地址为:%x\n",&x); //十六进制形式输出地址
指针变量的引用 通过上面的例子可以看出,对指针变量的引用主要有以下三种方式: pointer=&x; printf("指针变量pointer所指存储单元的值为:%d\n",*pointer); printf("指针变量pointer的值为:%x\n",pointer); printf("指针变量pointer的地址为:%x\n",&pointer); printf("指针变量pointer所占的字节数为:%d\n",sizeof(pointer)); return 0; } 通过上面的例子可以看出,对指针变量的引用主要有以下三种方式: (1)给指针变量赋值。如:pointer=&x; (2)引用指针变量指向的变量。如:printf("%d\n",*pointer); (3)引用指针变量的值。如:printf("%x\n",pointer);
指针变量作为函数参数 函数的参数不仅可以是整型、实型和字符型等基本数据类型,还可以是指针类型。它的作用是将一个变量的地址传送到另一个函数中。 输入两个整数,调用交换函数实现两数的交换。 #include <stdio.h> void swap1(int x, int y); void swap2(int *x, int *y); void swap3(int *x, int *y); int main() { int a,b; int *pt1,*pt2; printf("输入两个整数:\n"); scanf("%d%d",&a,&b); printf("交换前:a=%d, b=%d\n",a,b);
指针变量作为函数参数 swap1(a,b); printf("执行swap1后:a=%d, b=%d\n",a,b); pt1=&a; pt2=&b; swap2(pt1,pt2); printf("执行swap2后:a=%d, b=%d\n",a,b); swap3(pt1,pt2); printf("执行swap3后:a=%d, b=%d\n",a,b); return 0; }
指针变量作为函数参数 值传递 void swap1(int x,int y) { int temp; temp=x; x=y; y=temp; } 特点:被调函数的值发生变化,但主调函数的值没变。 地址传递 void swap2(int *x,int *y) { int temp; temp=*x; *x=*y; *y=temp; } 特点:被调函数的值发生变化,主调函数的值也发生变化。 地址传递 void swap3(int *x,int *y) { int *temp; temp=x; x=y; y=temp; } 特点:虽然是地址传递,但在被调函数中交换的是地址,主调函数的值没有变化。
指针变量作为函数参数变量变化示意图 调用swap1函数,进行的是值传递,变量在函数调用之前、调用时的参数传递、调用中和调用后值的变化如下图所示。
指针变量作为函数参数变量变化示意图 调用swap2函数,进行的是地址传递,变量在函数调用之前、调用时的参数传递、调用中和调用后值的变化如下图所示。
指针变量作为函数参数变量变化示意图 调用swap3函数,进行的是地址传递,变量在函数调用之前、调用时的参数传递、调用中和调用后值的变化如下图所示。
基本内容 指针简介 指针与数组 指针与字符串 指向函数的指针 返回指针值的函数 指针数组和多重指针 动态内存分配 本章小结
指针与数组的关系 不同类型的变量在内存中都有一个具体的地址,数组也是一样,并且数组中的元素在内存中是连续存放的,数组名代表数组的首地址。而指针存放地址的值,因此,既然指针可以指向普通变量,当然也可以指向数组或数组元素,指向数组的指针称为数组指针。事实上,在C语言中,把指针和数组当作同一个概念看待,数组名是指针,指针也是数组,可以认为数组名是常量指针。
指向一维数组的指针 C语言规定,数组名就是数组的首地址,例如: int a[10]={1,2,3,4,5,6,7,8,9,10}; int *pa; //数组名是数组的首地址,指针pa指向数组的首地址 pa=a; //把a[0]元素的地址赋给指针变量pa,指向数组的首地址 pa=&a[0];
指向一维数组的指针 下面的操作是等价的: int a[10],*p=a; 等价于:int a[10],*p; p=a;
指向数组的指针举例 输入10个整数存入一维数组a中,要求输出该数组中所有元素的值。 #include <stdio.h> int main() { int i,a[10],*p; printf("输入十个整数:\n"); for(i=0; i<10; i++) scanf("%d", &a[i]); printf("使用下标法输出数组元素\n"); for(i=0;i<10;i++) printf("%5d",*(a+i)); printf("\n");
指向数组的指针举例 p=a; //将数组的首地址赋给指针变量 printf("使用指针法输出数组元素\n"); for(;p<a+10; p++) printf("%5d",*p); printf("\n"); return 0; }
指向数组的指针说明 数组名不代表整个数组,而是数组第1个元素的地址,即数组的首地址。 “p=a;”不是把整个数组a全部送入p中,而是把数组a的首地址送入p中。 若指针变量p指向数组a的首地址,则一维数组a的地址用p、a和&a[0]来表示是等价的,数组a中下标为i的元素可以分别用*(a+i)、*(p+i)、a[i]和p[i]来引用。
指向数组的指针说明 当指针变量指向数组元素时,可以对指针变量进行一些加减运算或自增、自减运算。 int a[10]={1,2,3,4,5,6,7,8,9,10}; int *p,*q,*r; p=a+3; //p指向数组a中第4个元素a[3]的首地址 q=a+9; //q指向数组a中最后一个元素a[9]的首地址
指向数组的指针说明 自增、自减运算说明,以自增(++)为例: ①p++和++p:表示p的值加1,运行后p指向数组元素a[4]的首地址。 ②*p++和*(p++):表示取出p所指数组元素a[3]的值4,然后p指向数组元素a[4]的首地址。 ③(*p)++:*p表示p所指变量的值,即a[3]的值4,“(*p)++”也是a[3]++,所以操作后a[3]的值变成5,指针变量p仍然指向数组元素a[3]的首地址。 ④*(++p)和*++p:表示p指向下一个数组元素的首地址,即a[4]的首地址,然后取其值5。*(++p)和*++p功能相同。
指向数组的指针说明 数组名是地址常量,在内存中的位置是不会改变的。 int a[10]={1,2,3,4,5,6,7,8,9,10}, *p=a; p++; √ a++; × 指向数组的指针和指向简单变量的指针是不同的。前者可以通过指针运算引用到数组中的每个元素,而后者却不能通过指针引用到其它变量。 由于对指向数组元素的指针变量可进行加减运算,使得其值在程序运行期间会改变,因此使用指针变量时要时刻注意指针变量的当前值,否则程序会出现意想不到的结果。 int a[10],b=3,*pt1,*pt2; pt1=a; //指针变量pt1指向数组a的首地址 pt1+=5; //指针变量pt1指向数组a的第5个元素的首地址 pt2=&b; //指针变量pt2指向变量b的首地址 pt2++; //该语句没有意义
指针变量错误使用的例子 #include <stdio.h> int main() { int a[10],i,*p; p=a; printf("输入十个整数:\n"); for(i=0;i<10;i++) scanf("%d",p++); printf("输出结果为:\n"); for(i=0;i<10;i++) { printf("%12d", *p++); if((i+1)%5==0) printf("\n"); } return 0; 使用前要重新赋值
指向多维数组的指针 用指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素,在C语言中,对于二维数组而言是按行存储的。例如:int a[3][4],在内存中的存储形式如右图所示。 对于二维数组而言,数组名同样代表着数组的首地址。根据二维数组的特性,数组a可以看成是由3个一维数组a[0]、a[1]、a[2]构成的,而每个一维数组就是二维数组的一行(含有4个元素)。
指向多维数组的指针 对于二维数组a,不存在a、a[0]、a[1]、a[2]的存储空间,C系统不给它们分配内存,只分配12个整数的内存空间。事实上,a、a[0]、a[1]、a[2]都是指针常量。对于a[i],如果a是一维数组,则a[i]代表数组a的第i个元素,它是一个变量且有值,并且系统会给该元素分配存储空间。若a是二维数组,则a[i]代表一维数组,它是一个指针常量,系统不给它分配存储空间,它仅仅是一个地址。
指向多维数组的指针 对于二维数组而言,有行地址和列地址之分。二维数组a的首地址有三种表示:a、a[0]、&a[0][0],但它们之间存在着差异。假设指针变量p指向数组的首地址,若指向二维数组a的行地址,则p+1表示指向第1行元素的首地址(即a[1]的地址);若指向二维数组a的列地址,则p+1表示指向第0行第1列元素的首地址(即a[0][1]的地址)。
若改成“p<a+3*4;”,结果会怎样? 多维数组指针——指向数组元素的指针变量 用指向数组元素的指针变量输出二维数组的值。 #include <stdio.h> int main() { int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}, *p; for(p=a[0]; p<a[0]+3*4;p++) { printf("%5d",*p); if((p-a[0]+1)%4==0) printf("\n"); } return 0; 若改成“p<a+3*4;”,结果会怎样?
多维数组指针——指向一维数组的指针变量 C语言提供了一个指向一维数组的指针,它具有与数组名相同的特征,可以更方便地用指针来处理数组。 指向一维数组的指针变量的定义形式如下: 数据类型 (*指针变量名)[N]; 其中,N是整型常量,表示指针变量所指一维数组的元素个数。 例如: int a[3][4], (*p)[4], *ptr; ptr=a[0]; //ptr指向数组元素a[0][1]的首地址 p=a; //指向数组a的首地址,即a[0]的值
多维数组指针——指向一维数组的指针变量 #include <stdio.h> int main() { int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}, (*p)[4]; int i, j; p=a; for(i=0;i<3;i++) { for(j=0;j<4;j++) printf("%5d",*(*(p+i)+j)); //输出a[i][j]的值 printf("\n"); } return 0; 指针变量指向一维数组
指向数组元素的指针作函数参数 输入一组整数,使用直接插入排序法对其进行排序。 #include <stdio.h> #define N 10 void Input(int a[],int n) //输入n个整数,形参是数组名 { int i; for(i=0;i<n;i++) scanf("%d",&a[i]); } void Output(int *ptr,int n) //输出数组中的值,形参是指针变量 { int *ptr_end=ptr+n; for(;ptr<ptr_end;ptr++) printf("%5d",*ptr); printf("\n");
指向数组元素的指针作函数参数 void InsertSort(int *ptr, int n) {//直接插入排序,形参是指针变量 int *p,*q,*p_end,temp; p_end=ptr+n; for(p=ptr+1;p<p_end;p++) //n个元素经过n-1趟排序 if(*p<*(p-1)) { temp=*p; *p=*(p-1); for(q=p-2;q>=ptr&&temp<*q;q--) *(q+1)=*q; *(q+1)=temp; }
指向数组元素的指针作函数参数 int main() { int a[N], *p; printf("输入%d个整数:\n",N); Input(a, N); //实参是数组名 printf("排序前数组元素的值:\n"); Output(a, N); //实参是数组名 p=a; InsertSort(a, N); //实参是指针变量 printf("排序后数组元素的值:\n"); Output(a, N); //实参是指针变量 return 0; }
指向数组元素的指针作函数参数 归纳起来,用指针变量作为函数参数时,实参与形参的类型有以下几种对应关系,如下表所示。
指向数组的指针作函数参数 一个班有n个学生,每个学生有4门课,设计函数计算总平均分以及查找有两门以上(包括两门)成绩在85分以上的学生,并输出满足条件的学生。 #include <stdio.h> #define N 40 float Average(float *ps,int n) {//计算总平均分 float sum=0,aver,*p_end=ps+n-1; while(ps<=p_end) sum+=*ps++; aver=sum/n; return aver; } 指向数组元素的指针变量
指向数组的指针作函数参数 void Search(float (*p)[4],int n,int s[],int *q) {//查找两门以上课程的成绩在85分以上的学生 int i,j,k=0,count; for(i=0;i<n;i++) { count=0; for(j=0;j<4;j++) if(*(*(p+i)+j)>85) count++; if(count>=2) s[k++]=i; } *q=k; //统计满足条件的学生人数 指向数组元素的指针变量
指向数组的指针作函数参数 void Output(float (*p)[4],int s[],int m) {//输出满足条件的学生的信息,其中m表示数组s的大小, //n表示所有学生 int i,j,k; for(k=0;k<m;k++) { i=s[k]; for(j=0;j<4;j++) printf("%7.0f",*(*(p+i)+j)); printf("\n"); } int main() { float score[N][4],*ps,aver; int i,j,m,n,s[N]={0}; //s数组统计成绩在85分以上学生数
指向数组的指针作函数参数 printf("输入学生人数(不超过40):\n"); scanf("%d",&n); for(i=0;i<n;i++) for(j=0;j<4;j++) scanf("%f",&score[i][j]); ps=score[0]; //ps为指向数组元素的指针变量 aver= Average(ps,4*n); printf("\n%d个学生的总平均分为:%5.1f\n",n,aver); m=0; Search(score,n,s,&m); printf("\n有两门以上的课程成绩在85分以上的学生:\n"); Output(score,s,m); return 0; }
基本内容 指针简介 指针与数组 指针与字符串 指向函数的指针 返回指针值的函数 指针数组和多重指针 动态内存分配 本章小结
指向字符串的指针 C语言中没有字符串变量,若要存储字符串,可以使用字符数组。除了用字符数组保存并标识字符串外,还可以使用字符类型指针指向一个字符串,从而使用指针访问它所指的字符串。 例如: char *sp="I am a student.", *sq; char str[]="I am a boy."; sq=str;
指针与数组的关系 指针变量和数组名存储字符串 执行完sq=str后的情况
指向字符串的指针应用举例 使用指针变量实现字符串的连接。 #include <stdio.h> int main() { char str1[80],str2[40]; char *ps1=str1,*ps2=str2; printf("输入第一个串:\n"); gets(str1); printf("输入第二个串:\n"); gets(str2); while(*ps1++!='\0'); ps1--; //将ps1指向第一个串的结束标志 while(*ps1++=*ps2++); //逐个字符进行拷贝 ps1=str1; //重新确定指针指向 printf("连接后的新字符串为:\n"); puts(ps1); return 0; }
指向字符串的指针变量说明 指向字符串的指针变量,它只存放字符串的首地址,而不是存放整个字符串,但它可以指向任何字符串。字符数组名是一个常量,不能将一个字符串赋给一个字符数组。 例如: char str1[20]="I am a teacher.",str2[20]; char *ps1="I am a student.",*ps2; ps="I love China."; × str1="I love China."; √ str2=str1; × ps2=ps1; √ 不论字符串指针还是字符数组,都可以访问其分量,但访问时要十分小心,要注意操作的正确性。
字符数组与字符指针变量的比较
基本内容 指针简介 指针与数组 指针与字符串 指向函数的指针 返回指针值的函数 指针数组和多重指针 动态内存分配 本章小结
指向函数的指针 指针可以作为函数参数,进行地址传递。由于函数名就是函数的入口地址,因此,指针还可以指向函数。 指向函数的指针变量的定义形式如下: 类型名 (*指针变量名)(函数参数表列); 注意:此处的括号一定不能省略,否则就成了返回指针的函数了。函数参数表列表示函数形参的类型。 例如:int (*pf)(int,int); 表示pf是一个指向函数入口的指针变量,该函数有两个整型类型的参数,其返回值是整型类型。
指向函数的指针应用举例 利用指向函数的指针求两个数的最大值和最小值 。 #include <stdio.h> int max(int x, int y) { return x>y?x:y; } int min(int x, int y) return x<y?x:y; int main() { int a,b,c; int (*pf)(int,int); printf("输入两个整数:\n");
指向函数的指针应用举例 scanf("%d%d",&a,&b); c=max(a,b); //通过函数名调用函数max printf("a=%d, b=%d, max=%d\n",a,b,c); pf=max; //将函数的入口地址赋给pf c=(*pf)(a,b); //通过指针变量调用函数max c=min(a,b); //通过函数名调用函数min printf("a=%d, b=%d, min=%d\n",a,b,c); pf=min; //将函数的入口地址赋给pf,pf重新赋值 c=(*pf)(a,b); //通过指针变量调用函数min return 0; }
指向函数的指针使用说明 定义指向函数的指针变量,并不意味着这个指针变量可以指向任何函数,它只能指向在定义时指定的类型的函数。 如果要用指针调用函数,必须先使指针指向该函数。 给函数指针变量赋值时,只需给出函数名而不必给出参数。 如上例中,“pf=max;”表示把函数max的入口地址赋给了指针变量pf。 用函数指针变量调用函数时,只需将(*指针变量)代替函数名即可,在(*指针变量)之后的括号中根据需要写上实参。 如上例中,“pf=max;”是将函数的入口地址赋给pf,与实参和形参的结合问题无关。 在一个程序中,一个指针变量可以先后指向同类型的不同函数,但函数的返回值类型必须相同。 如上例中,c=(*pf)(a,b);
指向函数的指针变量作为函数参数 利用指向函数的指针变量作函数参数,求两个整数的和、差以及乘积。 #include <stdio.h> int Sum(int x, int y); int Difference(int x, int y); int Product(int x, int y); int func(int x, int y, int (*pf)(int,int)); int main() { int a,b,c; printf("输入两个整数a和b:"); scanf("%d%d",&a,&b); printf("%d+%d=",a,b); c=func(a,b,Sum); printf("%d\n",c);
指向函数的指针变量作为函数参数 printf("%d-%d=",a,b); c=func(a,b, Difference); printf("%d\n",c); printf("%d*%d=",a,b); c=func(a,b, Product); return 0; } int Difference(int x, int y){ return x-y; } int Product(int x, int y){ return x*y; } int Sum(int x, int y){ return x+y; } int func(int x, int y, int (*pf)(int,int)){ return (*pf)(x,y); }
基本内容 指针简介 指针与数组 指针与字符串 指向函数的指针 返回指针值的函数 指针数组和多重指针 动态内存分配 本章小结
返回指针的函数 函数的返回值表示函数的计算结果,其类型除了可以是系统定义的简单数据类型外,还可以返回指针型的数据。 指针函数定义的一般形式为: 类型名 *函数名(参数表列); 例如:int *fun(int x,int y) { …… } 表示fun是一个返回指针值的指针型函数,它返回的指针指向一个整型数据。
指针函数的应用举例 从键盘输入一个字符,判断在给定的字符串中是否存在该字符,若存在,给出该字符在字符串中第一次出现的位置。 #include <stdio.h> char *search(char *ps, char ch); int main() { char str[80],*p,ch; printf("输入一个字符串:\n"); gets(str); printf("输入要查找的字符:"); ch=getchar(); p=search(str,ch);
指针函数的应用举例 if(p){ printf("字符串的首地址是%x\n",str); printf("字符%c的地址是%x\n",ch,p); } else printf("字符串中没有字符%c\n",ch); return 0; char *search(char *ps, char ch) { while(*ps!='\0') if(*ps==ch) return ps; else ps++;
基本内容 指针简介 指针与数组 指针与字符串 指向函数的指针 返回指针值的函数 指针数组和多重指针 动态内存分配 本章小结
指针数组 指针数组也是一种数组,数组中的每一个数组元素均为指针类型数据。即每个元素都存放一个地址,相当于一个指针变量。 定义一维指针数组的一般形式为: 类型名 *数组名[数组长度]; 例如,int *pi[10]; //定义一个大小为10的指针数组
指针数组应用举例 使用指针数组处理字符串。 #include <stdio.h> int main() { char str[3][10]={"I", "love", "China"}; char *ps[3]={str[0],str[1],str[2]}; int i,j; printf("通过数组元素输出字符串:\n"); for(i=0;i<3;i++) { for(j=0;str[i][j]!='\0'; j++) putchar(str[i][j]); printf(" "); } printf("\n");
指针数组应用举例 printf("通过一维数组输出字符串:\n"); for(i=0;i<3;i++) printf("%s ",str[i]); printf("\n"); printf("通过指针数组输出字符串:\n"); printf("%s ",*(ps+i)); return0; }
指针数组使用说明 指针数组中数组元素的值虽然是指针,但在使用前应该对其进行赋值,否则数组元素的值是随机地址,直接对其操作会产生错误。 如果用二维数组存放字符串,并且采用指针数组存放每个串的首地址(即每行的首地址),那么对字符串的排序有两种方法:一是交换指针数组的数组元素的值,一种是对字符串按字典序重新进行排列 。 例如下列程序段: int i; char *ps[4]; for(i=0;i<4;i++) gets(ps[i]); puts(ps[i]);
使用指针数组处理字符串应用举例 有n个字符串存储在二维数组中,对这n个字符串进行排序。 #include <stdio.h> #include <string.h> void Sort1(char *ps[], int n); void Sort2(char *ps[], int n); void Input(char *ps[], int n); void Output(char *ps[], int n); int main() { char str[10][80],*pc[10]; int n,i; printf("输入字符串的个数n(n<10):"); scanf("%d",&n); for(i=0;i<n;i++) pc[i]=str[i]; //指针数组pc初始化
使用指针数组处理字符串应用举例 printf("输入%d个字符串\n",n); Input(pc,n); Sort1(pc,n); printf("排序后字符串的顺序:\n"); Output(pc,n); printf("重新输入%d个字符串\n",n); Sort2(pc,n); return 0; }
使用指针数组处理字符串应用举例 void Input(char *ps[], int n) { int i; char ch; for(i=0;i<n;i++) scanf("%s",ps[i]); ch=getchar(); //屏蔽最后一个换行符 } void Output(char *ps[], int n) printf("%10s",ps[i]); printf("\n");
使用指针数组处理字符串应用举例 void Sort1(char *ps[], int n) {//采用冒泡排序法对字符串进行排序 //修改指针数组的值 int i,j; char *p; for(i=0;i<n-1;i++) for(j=0;j<n-i-1;j++) if(strcmp(ps[j],ps[j+1])>0) { p=ps[j]; ps[j]=ps[j+1]; ps[j+1]=p; }
使用指针数组处理字符串应用举例 void Sort2(char *ps[], int n) {//采用冒泡排序法对字符串进行排序 //将字符串按字典序重新排列 int i,j; char p[80]; for(i=0;i<n-1;i++) for(j=0;j<n-i-1;j++) if(strcmp(ps[j],ps[j+1])>0) { strcpy(p, ps[j]); strcpy(ps[j],ps[j+1]); strcpy(ps[j+1],p); }
字符串排序图示 调用Sort1函数排序前后数组元素值的变化情况 调用Sort2函数排序前后数组元素值的变化情况
多级指针 如果一个指针变量存放的是另一个指针变量的地址,则称这个指针变量为二级指针变量,也称为指向指针的指针。 二级指针定义的一般形式为: 类型名 **指针变量名; 例如: int a=3,*pa, **ppa; pa=&a; ppa=&pa; 三者之间的关系
多级指针应用举例 利用二级指针变量,访问字符串。 #include <stdio.h> int main() { char str[][20]={"Qingdao", "Yantai", "Jinan", "Weifang"}; char *ps[]={str[0],str[1],str[2],str[3]}; char **pps=ps; int i; for(i=0;i<4;i++) printf("%10s",*pps++); printf("\n"); return 0; }
main函数的参数 在C语言中,main函数也可以带参数,参数的个数最多3个,并且参数名(习惯上)、参数顺序和参数类型是固定的。带有main函数参数的程序,不适合在集成环境下运行,而要在命令行下执行,其参数就来自命令行下运行该程序时输入的相关信息。
main函数的参数 参数形式如下: void main(int argc, char *argv[], char *env[]) 命令行的一般格式为: 文件名 参数1 参数2 … 参数n 注意:各参数之间用空格隔开。
带参数 main函数举例 编写程序,输出命令行的参数内容。 #include <stdio.h> int main(int argc, char *argv[]) { int i; printf("argc=%d\n",argc); printf("command name: %s\n",argv[0]); for(i=1;i<argc;i++) printf("parameter %d: %s\n",i,argv[i]); return 0; }
基本内容 指针简介 指针与数组 指针与字符串 指向函数的指针 返回指针值的函数 指针数组和多重指针 动态内存分配 本章小结
计算机内存描述 在程序执行期间分配的内存,该内存区域称为堆(heap),堆中的内存是由程序员控制的,由程序员跟踪所分配的内存何时不再需要,并释放这些空间,以便以后再次使用。 对内存的动态分配是通过系统提供的库函数来实现的,主要有:malloc、calloc、free和realloc这4个函数,这4个函数都包含在头文件“stdlib.h”中。
动态申请内存——malloc() 函数 在运行时分配内存的最简单的标准库函数是malloc(),其函数原型为: void *malloc(unsigned int size); 作用:在内存的动态存储区中分配一个长度为size(非负数)的连续空间。 返回值:分配区域的第一个字节的地址。失败时,返回空指针(NULL)。 注意:指针的基类型为void,即不指向任何类型的数据,只提供一个地址。若要返回具体的数据类型,必须进行强制类型转换。 例如:int *p; p= (int*)malloc(32);
malloc() 函数应用举例 从键盘输入n个整数存入动态申请的一个连续存储空间中,并输出这n个整数。 #include <stdio.h> #include <stdlib.h> int main() { int i, n, *p; scanf("%d",&n); p=(int*)malloc(n*sizeof(int)); for(i=0;i<n;i++) scanf("%d",p+i); printf("输出这%d个整数:\n",n); printf("%5d",*(p+i)); printf("\n"); return 0; }
动态申请内存——calloc() 函数 calloc函数。该函数与malloc函数相比有两个优点。第一,它把内存分配为给定大小的数组;第二,它初始化了所分配的内存,所有的位都是0。其函数原型为: : void *calloc(unsigned n, unsigned size); 功能:在内存的动态存储区中分配n个长度为size的连续空间。 例如:int *p; p= (int*)calloc(10,sizeof(int));
释放动态分配的内存 在动态分配内存时,应在不需要该内存时释放它们。堆上分配的内存会在程序结束时自动释放,但最好在使用完这些内存后立即释放。 要释放用malloc函数或calloc函数分配的内存,可以使用free函数,其函数原型为: : void free(void *p); 功能:释放指针变量p所指向的动态空间,使这部分空间重新被其它变量使用。
重新分配内存——realloc() 函数 如果通过malloc和calloc动态分配大小为n的连续的存储空间,若要改变其大小,可以用realloc函数重新分配,其函数原型为: void *realloc(void *p, unsigned size); 功能:重新分配由malloc和calloc申请的存储空间的大小。
realloc() 函数应用举例 输入n个整数存入动态分配的存储空间中,再输入m个整数,将其追加到该组数中。 #include <stdio.h> #include <stdlib.h> int main() { int i, n,m, *p; printf("输入整数个数:"); scanf("%d",&n); p=(int*)calloc(n,sizeof(int)); printf("输入%d个整数:\n",n); for(i=0;i<n;i++) scanf("%d",p+i); printf("输出这%d个整数:\n",n); printf("%5d",*(p+i)); printf("\n");
realloc() 函数应用举例 printf("输入要插入整数个数:"); scanf("%d",&m); printf("再输入%d个整数:\n",m); p=(int*)realloc(p,(m+n)*sizeof(int)); for(;i<m+n;i++) scanf("%d", (p+i)); printf("输出新的一组数:\n"); for(i=0;i<m+n;i++) printf("%5d", *(p+i)); printf("\n"); free(p); return 0; }
使用动态分配的内存的基本规则 避免分配大量的小内存块,分配许多小的内存块比分配几个大的内存块的系统开销大。 仅在需要时分配内存,使用结束后就释放它。 在释放内存之前,确保不会无意中覆盖堆上分配的内存的地址,否则程序会出现内存泄露。
基本内容 指针简介 指针与数组 指针与字符串 指向函数的指针 返回指针值的函数 指针数组和多重指针 动态内存分配 本章小结
本章小结 指针是C语言的精华,其本质体现在对内存的操作。 指针有:指向变量的指针,指向数组的指针,指向字符串的指针,指向函数的指针,指向指针的指针,返回指针值的函数和指针数组。 对于指向任何数据类型的指针变量,要注意的是它也是一种变量,在未赋值前其值是一个不确定的地址,若要正确使用它,引用前首先给其赋值。 参数的传递有值传递和地址传递两种,而指针就是地址,通过指针变量作为函数参数,实参和形参共占同一段存储单元。即可对实参的值进行修改,又可节省存储空间,提高程序效率。
本章小结 指向数组的指针可以进行关系运算和加减运算。与整数进行运算时,每单位增量,所移动的是该指针类型所占的字节数。 函数名是函数的入口地址,函数调用时,除了可以通过函数名外,还可以通过指向函数的指针变量来实现。利用指向函数的指针变量调用函数,可以通过该变量调用具有相同返回类型的不同函数。