指针变量是包含内存地址的变量,它指向内存中的一块区域,通过指针的值,可以间接访问到相应的内存单元的数据,并做相应的修改。
1、指针的定义和简单使用
定义一个指针变量和定义一般的变量类似,只需在变量名前面加一个“*”。对一个指针变量赋值可以用取地址符&来获取到一个变量的地址,如果要获得指针指向的内存区域的数据,用解参考运算符*(也称为间接运算符,它返回其操作数指向的对象的值)。指针的值为NULL(NULL是stdio.h中定义的符号变量,实际上是0)说明其不指向任何的内存单元,0是唯一直接可以赋值给指针变量的整数值。实际上,*和&是互补的,当两个运算符连续应用于一个指针变量时,无论顺序如何,运算结果相同。同时可以用printf中的格式化字符串%p来输出指针变量的值,下面是一个简单的程序。
#include
int main()
{
int a;
a=9;
//定义并初始化一个指针,命名就可以看出
int *aPtr=NULL;
//将指针指向变量a
aPtr=&a;
printf("The address of a is %p"
"\nThe value of aPtr is %p",&a,aPtr);
printf("\n\nThe value of a is %d"
"\nThe value of *aPtr is %d",a,*aPtr);
printf("\n\nShowing that * and & are complements of "
"each other\n&*aPtr = %p"
"\n*&aPtr = %p\n",&*aPtr,*&aPtr);
return 0;
}
运行结果如下:
2、用指针做函数的参数
2.1 通过指针实现的引用传递
程序设计语言的参数传递方式,大致分两种:值传递和引用传递。C语言中没有引用传递,但是C语言通过指针间接实现了引用传递。通过用指针变量作为函数的参数,可以传递变量的地址(只需要在变量前面加上&运算符就可以),这样,用该地址就可以访问到主调函数中的该变量的内存地址,并可以进行相应的修改。这样,在函数执行完毕之后,修改仍然可以得到保留。
2.2 const
const限定符可以告诉编译器特定的变量的值是不能被修改的。如果想确保函数不会修改传递进来的参数值,应该将参数声明为const。这样对于C语言中用指针实现的引用传递,有四种情况:指向非常量数据的非常量指针(int *aPtr),指向非常量数据的常量指针(int *const aPtr),指向常量数据的非常量指针(const int *aPtr)和指向常量数据的常量指针(const int * const aPtr)。简单的说,就是指针变量自身和指针指向的变量都有可能是const,这样就产生了四种情况,这四种情况提供了四种不同的访问权限,下面分别解释。
指向非常量数据的非常量指针(int *aPtr):指针的值本身和指针指向变量的值都可以在函数中被修改。
指向非常量数据的常量指针(int *const aPtr):指针的值不能被修改,但是指针指向的变量的值可以被修改。
指向常量数据的非常量指针(const int *aPtr):指针指向的值不能被修改,但是指针本身的值可以被修改。
指向常量数据的常量指针(const int * const aPtr):指针本身和指针指向变量的值都不能被修改。
从别的书上看到的一个记忆的方法,沿着*划一条线:如果const位于*的左侧(如const int *aPtr),那么可以理解为const是修饰指针指向的变量,这样就不能通过该指针间接地修改该变量的值;如果const位于*的右侧(int *const aPtr),那么说明这个const是修饰的指针本身,这样的话就不可以修改该指针使它指向其它变量;如果两边都有const(const int * const aPtr),那说明指针本身和指针指向变量的值都不可以修改。至于左侧右侧容易记混的问题,其实很容易解决,因为这样想就好了,*左侧是指针指向的变量类型啊,const放在这儿,很明显是在修饰指针指向的对象啊,记住左侧,右侧就反着好了
。
3、sizeof和指针运算
3.1 sizeof
sizeof是C语言中特殊的一元运算符,可以应用在变量名称、数据类型和常量之前,它在程序编译期间以字节为单位来确定数组或其他数据类型的大小。当应用于数组时,sizeof返回数组中的字节总数。如float a[20],sizeof(a)的值应该是4*20,80。当然,如果想获得数组的大小可以采用sizeof(a)/sizeof(float)。
3.2 指针运算
实际上,指针变量可以进行的算术操作是有限的:递增,递减,将指针和整数相加,从指针中减去一个整数或者一个指针减去另一个指针。需要注意的是,对于指针的算术运算,其单位长度并不是一般意义上的1,而是sizeof(TYPE)。这样,如果float a[14]; float *aPtr=a;(或者int *aPtr=&a[0]); aPtr++;这样aPtr应该指向的是数组a的第二个元素,也就是a[1],这里的单位长度就是sizeof(float)。同样地,如果aPtr=aPtr+5;,这样aPtr又指向了数组a的第6个元素。如果aPtr-a,这样可以得到两个指针之间的元素间隔个数。应该是6。
进行指针运算要注意:
(1)如果将一个指针的值赋给另外一个指针,那么这两个指针的类型必须相同,否则应该用类型转换运算符进行类型转换。但是,有一个例外就是指向void类型的指针,它是通用指针,可以代表任何指针类型。因此,所有指针类型都可以赋值给void指针,而void指针也可以赋值给任何类型的指针,而不需要任何类型转换运算符。但是,void指针不能解参考,编译器知道指向int类型的指针引用的是32位计算机上的4个字节内存,但指向void的指针仅包含未知数据类型的内存位置,也就是说,编译器不知道指针所引用的字节数。编译器必须知道数据类型,才能确定所引用的字节数。
(2)除非两个指针变量都指向的是一个数组中的元素,否则对它们相减的结果没有任何意义,因为我们不能假设两个变量在内存中是连续的。
4、指针和数组
4.1 数组和指针的共性
实际上,数组名称的本质是一个常量指针。因此,int a[6]; int *aPtr;定义一个数组和指针之后,通过a[3],*(a+3)和*(aPtr+3)都可以访问到数组的第四个元素的值。但是区别在于,aPtr=aPtr+3;这样aPtr就指向了a数组的第四个元素,但是,不能a=a+3;,因为a是一个数组名,它是一个常量指针它的值不能被修改,更加具体地说,它应该是一个指向非常量数据的常量指针。
4.2 指针数组
数组元素也可以是指针类型,指针数组常见的用途就是构成由字符串组成的数组,简单地说就是字符串数组,数组中的每一个元素都是字符串。下面是一个例子,const限定符说明不能修改每个元素指针所指向的字符串。
#include
int main()
{
const char *suit[4]={"Hearts","Diamonds","Clubs","Spades"};
int i;
for(i=0;i<4;i++)
printf("sizeof pointer to \"%s\" :%d\n",suit[i],sizeof(suit[i]));
printf("\nsizeof suit:%d\n",sizeof(suit));
return 0;
}运行结果如下:
从这个例子中,可以看出const char *suit[4]={"Hearts","Diamonds","Clubs","Spades"};定义了一个指针数组,但是,其中的每个元素只是一个指针,而不是数组名(如果是数组名的话,sizeof的结果不应该都是4)。这样定义字符串数组可以节省空间。
5、函数指针
和数组名实际上就是数组第一个元素在内存中的地址类似,函数迷你实际上就是执行函数任务的代码在内存中的起始地址。函数指针包含函数在内存中的地址,可以传递给函数、从函数返回、存储在数组中或者是赋值给其它的函数指针,下面是两个函数指针的例子。
(1) 用函数指针实现升序/降序排序
#include
#define SIZE 10
int ascending(int a,int b)
{
return a>b;
}
int descending(int a,int b)
{
return a
}
void swap(int *aPtr,int *bPtr)
{
int temp=*aPtr;
*aPtr=*bPtr;
*bPtr=temp;
}
void bubble(int work[],const int size,int(*compare)(int a,int b))
{
int pass;
int count;
for(pass=1;pass
for(count=0;count
{
if((*compare)(work[count],work[count+1]))
swap(&work[count],&work[count+1]);
}
}
int main()
{
int order;
int counter;
int a[SIZE]={2,6,4,8,10,12,89,68,45,37};
printf("\nData items in original order:\n");
for(counter=0;counter
printf("%5d",a[counter]);
printf("\nEnter 1 to sort in accending order,\n"
"Enter 2 to sort in descending order: ");
scanf("%d",&order);
if(order==1)
{
bubble(a,SIZE,ascending);
printf("\nData items in ascending order:\n");
}
else
{
bubble(a,SIZE,descending);
printf("\nData items in descending order:\n");
}
for(counter=0;counter
printf("%5d",a[counter]);
printf("\n");
return 0;
}运行结果如下:
(2) 函数指针数组
#include
void function1(int a);
void function2(int a);
void function3(int a);
int main()
{
void (*f[3])(int)={function1,function2,function3};
int choice;
printf("Enter a number between 0 and 2, 3 to end: ");
scanf("%d",&choice);
while(choice >=0 && choice<3)
{
(*f[choice])(choice);
printf("Enter a number between 0 and 2, 3 to end: ");
scanf("%d",&choice);
}
printf("Program execution completed.\n");
return 0;
}
void function1(int a)
{
printf("You entered %d so funtion1 was called\n\n",a);
}
void function2(int a)
{
printf("You entered %d so funtion2 was called\n\n",a);
}
void function3(int a)
{
printf("You entered %d so funtion3 was called\n\n",a);
}运行结果:
参考,呵呵,基本算是摘取了。Whatever,说明白最重要。