第八章 善于使用指针
什么是指针?
1、通过地址能找到所需的变量单元,地址指向该变量单元,地址形象化地称为“指针”;
2、直接访问和简接访问
直接访问:通过变量名找到对应存储单元;
简接访问:将变量a的地址存储到另一个变量中,通过这个变量找到a的地址,访问a变量;
指针变量
3、存放地址的变量:指针变量;
a_pointer=&a;
a_pointer为指针变量;&为取地址符,指针变量的值(假设a地址为2000)为地址(即指针);
区别:指针是一个地址,而指针变量是一个存放地址的变量;
4、使用指针变量访问变量的例子
1)通过指针访问整型变量:
#include <stdio.h> int main() { int a,b; int *pointer_1,*pointer_2; // 定义两个指针变量 a=100;b=10; pointer_1=&a; // 给指针变量取a的地址 pointer_2=&b; // 给指针变量取b的地址 printf("a=%d,b=%d\n",a,b); printf("*pointer_1=%d,*pointer_1=%d\n\n",*pointer_1,*pointer_2); // *表示取a,b变量的值 //如果后面输出没有“*”则是取到了a,b的地址,*表示取值符 }
5、怎么定义指针变量
// 基类型 *指针变量名;
int *pointer_1,*pointer_2;
float *pointer_3; // pointer_3是指向float型变量的指针变量
char *pointer_4; // pointer_4是指向字符型变量的指针变量
int *pointer_1=&a,*pointer_2=&b; // 定义指针变量同时初始化指向a,b
注意点:1)为什么要指定基类型?
① 不同的类型的数据在内存中的字节数是不同的(如:整型占4个字节、双精度数据占8字节)
② 指针的运算(加、减)会有应用到,例如指针移动位置+1或者-1,意思是地址值+4,地址值-4,而双精度则是+8或者-8。
2)赋值给指针变量的数据只能是变量地址,而且该变量的类型要与指针变量的基类型相同。
6、怎么引用指针变量
1)给指针变量赋值;
p=&a;
2)引用指针变量指向的变量;
p=&a;
printf("%d",*p);
*p=1; // 将1赋给p所指向的变量,如果指向a,则a为1
3)引用指针变量的值。
printf("%o",p); // 以八进制输出p的值,如果p指向a,则是a的地址&a
小结: (1)&取地址运算符,&a是变量a的地址;
(2)*指针运算符(简接访问运算符),*p是指针变量p指向的对象的值。
例子:输入a和b两个整型,按先大后小的顺序输出a和b
#include <stdio.h> int main() { int *p1,*p2,*p,a,b; // 定义两个整型变量和三个整型指针变量 scanf("%d,%d",&a,&b); // 输入两个整型的变量给a和b p1=&a;p2=&b; // p1指向a的地址,p2指向b的地址 if(a<b) { p=p1;p1=p2;p2=p; // 交换指向 printf("a=%d,b=%d\n",a,b); printf("Max=%d,Min=%d\n",*p1,*p2); return 0; } }
ps:这里已经体现到a和b的值保持不变,实际上是改变了p1和p2的值,所以交换的不是整型变量的值,而是指针变量的值。
注意点:纯地址和类型(地址的基类型),若a和b的纯地址相同(假设 2000),但是因为其类型不同,所以&a,&b是两个不同的地址,定义时一定要注意。
7、指针变量作为函数参数
函数参数可以是 整型、浮点型、字符型,当然可以指针类型啦!作用是将一个变量的地址 传送到另一个函数中。
例子1:比较a和b的大小,先大后小输出,用函数处理和用指针变量作函数参数。
思路:将指向两个变量的指针变量(内放两个变量的地址)作为实参传递给形参,在形参中交换两个变量的值。
#include <stdio.h> int main() { void swap(int *p1,int *p2); // 对swap函数进行声明 int a,b; int *pointer_1,*pointer_2; // 定义两个指针变量 scanf("%d,%d",&a,&b); pointer_1=&a;pointer_2=&b; // 分别指向a和b的地址 if(a<b) { swap(pointer_1,pointer_2); // 如果a<b,调用swap函数 } printf("max=%d\nmin=%d\n",a,b); return 0; } void swap(int *p1,int *p2) { int temp; temp=*p1; // *p1和*p2互换 *p1=*p2; *p2=temp; }
调用函数时,将实参变量的值传送为形参变量,采用的依然是“值传递” 方式,p1和poiner_1都是指向变量a,p2和poiner_2都是指向变量b,函数执行完后p1和p2已经被释放了。但是main函数中的a和b是已经交换过的值。本例采用的是交换a和b的值,和p1和p2的值不变,与上面相反,是通过改变形参的值然后传回到实参函数中,交换完之后形参就释放了。
例子2:企图通过改变 指针形参的值 来改变 指针实参的值
#include <stdio.h> int main() { void swap(int *p1,int *p2); int a,b; int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); pointer_1=&a;pointer_2=&b; if(a<b) { swap(pointer_1,pointer_2); } printf("max=%d\nmin=%d\n",a,b); return 0; } void swap(int *p1,int *p2) // 形参是指针变量 { int *p; p=p1; // 交换p1和p2的指向 p1=p2; p2=p; }
这种是错误的,因为C语言形参和实参之间的数据传递都是单向的 “值传递 ” 方式, 不可能通过执行调用函数来改变实参的 指针变量的值(地址),但是可以通过改变实参指针变量所指变量的值(具体数据)
所以,回归到了例子1的思想,只能是改变形参的值来传回到实参函数中指针变量pointer具体指向的值,下面更好分析一点。
#include <stdio.h> int main() { void swap(int *p1,int *p2); int a,b; int *pointer_1,*pointer_2; printf("Please enter a b\n"); scanf("%d,%d",&a,&b); pointer_1=&a;pointer_2=&b; if(a<b) { swap(pointer_1,pointer_2); // 如果a<b,调用swap函数 } printf("a=%d\nb=%d\n",a,b); printf("max=%d\nmin=%d\n",a,b); return 0; } void swap(int *p1,int *p2) { int temp; temp=*p1; // *p1和*p2互换 *p1=*p2; *p2=temp; printf("p1的值:%d\n",*p1); printf("p2的值:%d\n",*p2); printf("p1的地址:%o\n",p1); printf("p2的地址:%o\n",p2); }
例子3:输入3个整数a,b,c,要求按大小顺序将它们输出。用函数实现改变这3个变量的值。
#include <stdio.h> int main() { void exchange(int *q1,int *q2,int *q3); // 声明函数 int a,b,c,*p1,*p2,*p3; printf("Please enter three numbers:\n"); scanf("%d,%d,%d",&a,&b,&c); p1=&a;p2=&b;p3=&c; exchange(p1,p2,p3); // 指针变量作实参,调用exchange函数 printf("a=%d\nb=%d\nc=%d\n",a,b,c); return 0; } void exchange(int *q1,int *q2,int *q3) // 定义将3个变量的值交换的函数 { void swap(int *pt1,int *pt2); // 对swap函数声明 if(*q1<*q2) swap(q1,q2); // 取指针变量q1(形参)从实参传过来的地址的值(即实参p1指向a的地址的值)与b比较,如果b大那么交换位置 if(*q1<*q3) swap(q1,q3); if(*q2<*q3) swap(q2,q3); } void swap(int *pt1,int *pt2) { int tmp; tmp=*pt1; // *p1和*p2互换 *pt1=*pt2; *pt2=tmp; }
通过指针引用数组
8、数组元素的指针是数组元素的地址
int a[10] //定义a为包含10个整型数据的数组 int *p; // 定义p为指针整型变量的指针变量 p=&a[0]; // 把a[0]元素的地址赋值给指针变量p
8、数组名代表数组中的首元素(即序号为0的元素)的地址,下面两条语句等价:
p=&a[0];
p=a; // a为首元素的地址
ps:形参数组不占内存空间。
9、定义指针变量(数组)
int *p=&a[0];
等价于
int *p;
p=&a[0]
int *p=a;
10、通过指针引用数组元素
1)下标法,用数组名加下标,如a[i]形式;
2)指针法,即地址法,数组名代表首元素的地址,因此通过 数组名计算数组中序号为 i 的元素的地址,形式为 *(a+i)。用一个指针变量p指向首元素,然后用*(p+i)调用a数组中序号为i的元素
例子1:有一个数组存放10个学生的年龄,用不同的方法输出数组中的全部元素
① 用数组名加下标找到所需要的数组元素;
② 通过数组名计算数组元素地址,找到所需要的数组元素;
③ 通过指针变量计算数组元素地址,找到所需要的数组元素;
④ 用指针变量先后指向各数组元素。
#include <stdio.h> int main() { int a[10]={15,16,21,23,12,17,18,19,20,24}; int i,*p=a; // 定义指针变量p并指向数组首元素 printf("a[i]:\n"); for(i=0;i<10;i++) // ① 用数组名加下标 { printf("%d\t",a[i]); } printf("\n*(a+i):\n"); // ② 用数组名计算数组元素地址,找到元素 for(i=0;i<10;i++) { printf("%d\t",*(a+i)); } printf("\n*(p+i):\n"); // ③ 用指针变量计算数组元素地址,找到元素 for(i=0;i<10;i++) { printf("%d\t",*(p+i)); } printf("\n(a+10):\n"); // ④ 用指针变量先后指向数组中各个元素 for(p;p<(a+10);p++) { printf("%d\t",*p); } printf("\n"); return 0; }
注意:
① 如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素。
p+1不是将p的值简单的加1,而是加一个数组元素 所占用的字节数。如果数组元素是float型,每个元素占4字节,则p+1意味着使p的值(是地址)加4字节。p+1所代表的地址实际上是p+1×d,d是一个数组元素所占的字节数,若p的值是2000,则p+1的值不是2001,而是2004。
② 如果指针变量p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址,或者说,它们指向a数组的第i个元素;
③ *(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。例如*(p+5)或*(a=5)就是a[5]。 也就是说:*(p+5)、(a+5)和a[5]三者等价;
④ 方括号[ ]实际上是变址运算符,即将a[i]按a+i计算地址,然后找出此地址单元中的值。找的元素的地址,然后找出该单元中的内容。若数组a的首元素的地址为1000,设数组为float型,则a[3 ]的地址是这样计算的: 1000+3×4=1012,然后从1012地址所指向的float型单元取出元素的值,即a[3]的值。
例子2:通过指针变量读入数组的10个元素,然后输出这10个元素:
#include <stdio.h> int main() { int a[10]; int i,*p=a; // 定义指针变量p并指向数组首元素 for(i=0;i<10;i++) { scanf("%d",p++); // p=a a++,即相当于 a+所占字节数*i } p=a; // 此处划重点,如果不加的话,在执行完第一个for循环后,p的值已经是p+10,所以要重新指回数组首元素地址 for(i=0;i<10;i++,p++) { printf("%d\t",*p); } return 0; }
注意点:(1)在执行完第一个for循环后,p的值已经是p+10,所以要重新指回数组首元素地址。
(2)a[10]包含10个元素,指针变量p指向某一数组元素,但是实际,上指针变量p可以指向数组以后的内存单元。如果在程序中引用数组元素a[10],虽然并不存在这个元素(最后一个元素是a[9]),编译是合法的。系统把它按* (a+10)处理,即先找出(a+10)的值(是一个地址),然后找出它指向的单元的内容。这样做虽然是合法的(在编译时不出错),但应避免出现这样的情况,要确保数组中的有效元素。
(3)指向数组的指针变量也可以带下标,如p[i]。对p[i]处理成*(p+i),但要确保p[i]当前的值。
用数组名作函数参数
11、数组名作为函数参数时,各元素的值发生了变化,实参数组元素的值随之变化,这是为什么?
因为实参数组名代表该数组首元素的地址,而形参是用来接收从实参 传递过来的数组首元素地址 的,编译时将形参数组名作为指针变量来处理。
例如:下面两种形式是等价的。
void f(int arr[],int n); // 形参为数组形式
void sort(int *arr,int n); // 定义为指针变量形式,即arr首地址
即*arr等于int arr[0],*(arr+1)、*(arr+2)、*(arr+3) 分别等于array[1]、array[2]、array[3],*a(arr+i)和arr[i]无条件等价。
12、变量名和数组名作为函数参数的比较

- C语言调用函数时虚实结合的方法都是采用“值传递”方式;
- 当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数实参时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量;
- 用户可以认为有一个形参数组,它从实参数组那里得到起始地址,因此形参数组与实参数组共占同一段内存单元,在调用函数期间,如果改变了形参数组的值,也就是改变了实参数组的值。当然在主调函数中可以利用这些已改变的值;
- 实参数组名代表一个固定的地址,或者说是指针常量,但形参数组并不是一个固定的地址值,而是作为指针变量,在函数调用开始后,它的值等于实参数组首元素的地址,在函数执行期间,它可以再被赋值。
void f(arr[],int n)
{
printf("%d\n",*arr); // 输出array[0]的值
arr=arr+3;
printf("%d\n",*arr); // 输出array[3]的值
}
例1:将数组a中n个整数按反顺序存放
#include <stdio.h> int main() { void inv(int x[],int n); int i,a[10]={3,5,7,9,11,13,15,17,19,21}; printf("The original array:\n"); for(i=0;i<10;i++) { printf("%d\t",a[i]); } printf("\n"); inv(a,10); printf("The array has been inverted:\n"); for(i=0;i<10;i++) { printf("%d\t",a[i]); } printf("\n"); return 0; } void inv(int x[],int n) { int i,j,temp,m=(n-1)/2; for(i=0;i<m;i++) { j=n-1-i; temp=x[i]; x[i]=x[j]; x[j]=temp; } printf("\n"); }
void inv(int *x,int n) { int *p,*i,*j,temp,m=(n-1)/2; i=x;j=x+n-1;p=x+m; for( ;i<=p;i++,j--) { temp=*x; *i=*j; *j=temp; } printf("\n"); }
总结:归纳起来,如果有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况。
(1) 形参和实参都用数组名。
(2) 实参用数组名,形参用指针变量。
(3) 实参形参都用指针变量。先使实参指针变量p指向数组a,然后将p作实参,将 &a[0] 传给形参指针变量x。x的初始值也是&a[0]通过x值的改变可以使x指向数组a的任一元素。
(4) 实参为指针变量,形参为数组名。应该注意,必须先使实参指针变量有确定值,即指向数组一个元素。
例2 :用选择排序对10个整数按由大到小顺序排序,用数组名作为实参。
#include <stdio.h> int main() { void sort(int x[],int n); int i,a[10]={67,14,43,132,141,13,112,111,19,21}; int *p=a; printf("The original array:\n"); for(i=0;i<10;i++) { printf("%d\t",a[i]); } printf("\n"); sort(p,10); printf("The array has been inverted:\n"); for(i=0;i<10;i++) { printf("%d\t",a[i]); } printf("\n"); return 0; } void sort(int x[],int n) { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) { if(x[j]>x[k]) // 如果下一项比前一项要大 k=j; // 那么就交换位置 } if(k!=i) // 如果k不等于当前下标,即后面的大嘛,如果等于当前下标那就剩一个元素啦 { t=x[i]; x[i]=x[k]; x[k]=t; } } }
函数的头部也可以改为如下形式:
void sort(int *x,int n)
函数也可以改为如下:
void sort(int *x,int n)
{
int i,j,k,t;
for(i=0;i<n-1;i++)
{
k=i;
for(j=i+1;j<n;j++)
{
if(*(x+j)>*(x+k)) // 如果下一项比前一项要大
k=j; // 那么就交换位置
}
if(k!=i) // 如果k不等于当前下标,即后面的大嘛,如果等于当前下标那就剩一个元素啦
{
t=*(x+i);
*(x+i)=*(x+k);
*(x+k)=t;
}
}
}
通过指针引用字符串
13、定义一个字符指针,使它指向一个字符串。
#include <stdio.h>
int main()
{
char string[20]={"I love china!"};
char *p;
p=string;
// char *string="I love china!"; // 等价于上面三条语句和下面两句,这里没有数组名,只能通过指针变量名来引用
// char *string; // 这里也可以先声明字符指针变量,再给其赋值
// string={"I love China!"};
printf("%s\n",p);
return 0;
}
注意点:① 可以用指针指向字符串常量,但是不能通过指针变量去改变字符串常量。
② 字符指针变量也是取字符串的首个字符的地址,内存中,字符串最后会自动加 ‘\0’ ,字符串遇到 ‘\0’ 结束。
③ 取字符串的个别字符可以用 下标法 和 指针法
例子1:有一个字符数组a,在其中存放字符串"I am a boy." ,要求把该字符串复制到字符数组b中。
1)数组方法实现
#include <stdio.h> int main() { char a[]="I am a boy.",b[20]; int i; for(i=0;*(a+i)!='\0';i++) // *(a+i)!='\0'好好理解这句的意思,循环条件是当前字符不为结束符时'\0' { *(b+i)=*(a+i); } *(b+i)='\0'; // 在已复制到b数组中的字符串最后加'\0',表示字符串结束 printf("string a is :%s\n",a); printf("string b is :"); for(i=0;b[i]!='\0';i++) { printf("%c",b[i]); // 下标方法访问数组元素 } printf("\n"); return 0; }
程序中a和b都定义为字符数组,可以通过地址访问其数组元素。在for语句中,先检查a[i]是否为'\0' (程序中的a[i]是以* (a+i)形式表示的)。如果不等于\0',表示字符串尚未处理完,就将a[i]的值赋给b[i],即复制一个字符到数组b中的相应位置。在for循环中将a串全部复制给了b串。最后还应将\0复制过去。
2)指针变量方法实现
#include <stdio.h> int main() { char a[]="I am a boy.",b[20]; char *p1,*p2; p1=a;p2=b; // p1指针变量指向a的第一个字符,p2指针变量指向b的第一个字符 int i; for(;*p1!='\0';p1++,p2++) { *p2=*p1; // 将p1指向的a串中的字符复制到p2所指向的b串中的位置 } p2='\0'; // p2执行完for语句之后,p2指向最后一个位置应该加上'\0'表示结束 printf("string a is :%s\n",a); printf("string b is :"); for(i=0;b[i]!='\0';i++) { printf("%c",b[i]); } printf("\n"); return 0; }
14、字符指针作为函数参数
1、字符串函数从一个函数传递到另一个函数,可以用地址传递,即字符数组名作为参数,也可以是使用 指向字符的指针变量 作为参数。
例子1:复制字符串,用函数调用函数实现
#include <stdio.h> int main() { void copy_string(char * from,char * to);// 函数声明 char *a="I am a teacher."; // 定义a为字符指针变量,指向一个字符串 char b[]="Your are a student."; // 定义b为字符数组,内放一个字符串 char *p=b; // p指针变量指向字符数组b的首元素 printf("string a=%s\nstring b=%s\n",a,p); printf("\ncopy string a to string b:\n"); copy_string(a,p); // 用字符串作实参 printf("\nstring a=%s\nstring b=%s\n\n",a,p); return 0; } void copy_string(char * from,char * to) // 形参是字符指针变量 { for( ;*from!='\0';from++,to++) // from[0]和a[0]是同一个存储单元 { *to=*from; // 只要a串没结束就复制到b数组中 } *to='\0'; }
例子2、有两个字符串:字符串a的内容为"I am a teacher.",字符串b的内容为"You are a student."。 要求把字符串b连接到字符串a的后面。即字符串a的内容为"I am a teacher. You are a student."。
#include <stdio.h> int main() { void link_string(char *arr1,char *arr2); char a[40]="I am a teacher."; // 定义a字符数组 char b[]="You are a teacher."; // 定义a字符数组 char *p1=a,*p2=b; // 定义字符指针变量p1,p2分别指向a和b printf("string a: %s\nstring b: %s\n",p1,p2); // 输出连接前的字符串 link_string(p1,p2); // 调用link_string函数,指针变量作为参数(形参?) printf("string a: %s\nstring b: %s\n",a,b); return 0; } void link_string(char * arr1,char * arr2) // 形参是字符指针变量 { int i; for(i=0;*arr1!='\0';i++) // 将指针变量指向'\0',不为'\0',arr1地址自加。这不用i也可以实现 { arr1++; // 当遇到'\0'后,arr1当前的指针指向a数组"."后一位,即'\0' } for( ;*arr2!='\0';arr1++,arr2++) { *arr1=*arr2; // 只要arr2串没结束就复制到arr1中 } *arr1='\0'; // 在复制结束后加一个结束表示符'\0' }
15、调用函数时实参与形参的对应关系

16、使用字符指针变量和字符数组的区别
1)本质
字符数组:由若干个元素组成,每个元素存放一个字符;
字符指针变量:存放的是一个地址(字符串中第1个字符的地址);
2)赋值方式
① 字符数组:只能对各个元素赋值
char b[]={'I',' ','a','m',' ','h','a','p','p','y'};
char str[14]={"I love China!"};
② 字符指针
char *a;
a="I love China!";
等于
char *a="I love China!";
3)字符数组,编译时分配的是一段确定的地址;字符指针变量,分配的内存单元是可以用来存放一个字符变量的地址,是一个字符型数据,如果未赋值,它的指向是未知的。
char str[10];
scanf("%s",str);
char *a,str[10];
a=str;
scanf("%s",a);
错误做法如下:虽然不会错误,但是会提示警告,提醒未给指针变量赋初值。&a是内存分配一段临时的地址,但a是值在内存中是未知的,所以这样做是非常危险的。
char *a;
scanf("%s",a);
printf("%s",a);
4)改变指针变量的值
例1:改变指针变量的值
char *a="I love China!";
a=a+7; // a一开始指向第1个字符,a+7指向的是第8个字符
printf("%s\n",a);
return 0;
下面是错误做法:
char str[]="I love China!";
str=str+7; // 数组名是一个常量,代表数组初始地址,是不能改变的
printf("%s\n",str);
5)指针变量带下标的形式
char *a="I love China!";
a [5] 指的是第6个字符,即“e”。虽然未定义数组a,但是字符串在内存中是以数组形式存放的,A[5]按*(a+5)处理。
6)数组元素可以再赋值,而字符指针变量指向的字符串是不可以被取代的。
char a[]="House";
char *b="House";
a[2]='r'; // 合法,再赋值
b[2]='r'; // 非法,字符串常量(地址)不能改变
提高部分
1、在字符指针作为函数参数(14点)例子1 的改进
1)在本程序中将“* to= * from”的操作放在while语句括号内的表达式中,而且把赋值运算和判断是否为'\0'的运算放在一个表达式中,先赋值后判断。
void copy_string(char * from,char * to)
{
while((*to=*from)!='\0')
{
to++;from++;
}
}
2)把上面程序的to++和from++运算与*to=*from合并,它的执行过程是,先将*from赋给*to,然后使to和from增值。
void copy_string(char * from,char * to)
{
while((*to++ = *from++)!='\0');
}
3)当*from不等于\0时,将*from赋给*to,然后使to和from增值。
void copy_string(char * from,char * to)
{
while(*from!='\0')
{
*to++=*from++;
*to='\0';
}
}
4)用其ASCII码来代替“ while(*from!= '\0') ”可以用“ while( *from!=0) ”代替('\0'的ASCII代码为0)。而关系表达式“ *from!=0 ”又可简化为“ *from ”。这是因为若* from的值不等于0,则表达式“
* from”为真,同时“ * from!=0 ” 也为 真。
void copy_string(char * from,char * to)
{
while(*from)
{
*to++=*from++;
*to='\0';
}
}
5)进一步化简
while(*to++=*from++);
等价于
while((*to++=*from++)!='\0');
6)for循环实现
for(;(*to++=*from++)!=0;);
等价于
for(;*to++=*from++;);
7)用字符数组名作为函数形参,在函数中另定义两个指针变量p1,p2,指向copy_string函数形参接收到的实参传过来的地址。
void copy_string(char * from,char * to)
{
char *p1,*p2;
p1=from;p2=to;
while((*p2++=*p1++)!='\0');
}
2、多维数组的指针
int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}};
int (*p)[4]; // 定义指向一维数组的指针变量,表示一维数组中有4个元素,p的值就是该一维数组的起始地址
p=a+1; // 指向数组序号为1的行(a[1]开头的:a[1][0]、a[1][1]....)
- a+1代表序号为1的行的首地址。
- 若二维数组的首行的首地址为2000,一个整型数据占4字节,则a+1的值应该是2000+4X4=2016,因为第0行有4个整型数据。
- a+1指向a[1](即a[1]的首地址),a+2指向a[2](即a[2]的首地址).....啰嗦哈哈哈
3、指向函数的指针
- 定义的函数都会分配一段存储空间,这段存储空间的起始地址(入口地址),称为 函数的指针。
- 定义一个指向函数的指针变量:
定义:一般形式:数据类型 (* 指针变量名)(函数参数列表)
int (*p)(int ,int ); // 指向函数的指针变量
调用前:指针调用函数,必须先使指针变量指向该函数:
p=max // p指向max函数,就是把max函数的入口地址给了p指针变量
调用:只需将 (*p) 代替为函数名即可(p为指针变量名)
p=max;
c=(*p)(a,b);
相当于
c=max(a,b);
指向函数的指针变量 的一个重要用途是把一个函数的入口地址 作为实参 传递给其他函数的形参,此时形参是指向函数的指针变量。这样就能够在被调用的函数中使用实参函数。
4、返回指针值的函数
函数可以返回一个整型值、实型值、字符型值,当然可以返回指针型的数据。
一般定义形式:
类型名 *函数名(参数列表);
int *a(int x,int y);
a为函数名,前面有“ * ”表示此函数是指针型函数(函数值是指针),int表示返回的指针是指向整型变量的,x和y是函数a的形参(整型)。调用它以后能得到一个指向整型数据的指针(地址)。
注意:定义时,*a 两侧没有括号,如果有括号就是指向函数的指针变量了。
5、指针数组
- 数组元素均为指针类型的数据(全是存放地址),成为 指针数组
- 一般定义形式:
类型名 数组名[数组长度];
int *p[4];
前面的“ * ”表示此数组是指针类型的,每个数组元素(相当于一个指针变量)都可指向一个整型变量。
6、多重指针
指针数据的指针变量,称为 指向指针的指针。(套娃)
name是一个指针数组,它的每一个元素是一一个指针型数据,其值为地址。name数组的每一元素都有相应的地址。数组名name代表该指针数组首元素的地址。Name+i是name[i]的地址。Name+i就是指向指针型数据的指针。还可以设置一个指针变量p,它指向指针数组的元素。p就是指向指针型数据的指针变量。
char **p; // 定义一个指向指针数据的指针变量
p的前面有两个*号。* *p相当于*( * p),显然* p是指针变量的定义形式。
如果没有最前面的*,那就是定义了一个指向字符数据的指针变量。现在它前面又有一个*号,表示指针变量p是指向一个字符指针变量 (即指向字符型数据的指针变量)的。
* p就是p所指向的另一个指针变量,利用指针变量访问另一个变量就是“间接访问”。
如果在一个指针变量中存放一个目标变量的地址,这就是“单级间址”。指向指针数据的指针用的是“二级间址”方法。
p的前面有两个*号。* *p相当于*( * p),显然* p是指针变量的定义形式。如果没有最前面的¥,那就是定义了一个指向字符数据的指针变量。现在它前面又有一个*号,表示指针变量p是指向一个字符指针变量(即指向字符型数据的指针变量)的。* p就是p所指向的另一个指针变量,在本章开头已经提到了“间接访问”的方式。利用指针变量访问另

浙公网安备 33010602011771号