C语言指针

指针变量是包含内存地址的变量,它指向内存中的一块区域,通过指针的值,可以间接访问到相应的内存单元的数据,并做相应的修改。

1、指针的定义和简单使用

定义一个指针变量和定义一般的变量类似,只需在变量名前面加一个“*”。对一个指针变量赋值可以用取地址符&来获取到一个变量的地址,如果要获得指针指向的内存区域的数据,用解参考运算符*(也称为间接运算符,它返回其操作数指向的对象的值)。指针的值为NULL(NULL是stdio.h中定义的符号变量,实际上是0)说明其不指向任何的内存单元,0是唯一直接可以赋值给指针变量的整数值。实际上,*和&是互补的,当两个运算符连续应用于一个指针变量时,无论顺序如何,运算结果相同。同时可以用printf中的格式化字符串%p来输出指针变量的值,下面是一个简单的程序。

#include <stdio.h>
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<stdio.h>
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 <stdio.h>
#define SIZE 10
int ascending(int a,int b)
{
	return a>b;
}
int descending(int a,int b)
{
	return a<b;
}
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<size;pass++)
		for(count=0;count<size-pass;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<SIZE;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<SIZE;counter++)
		printf("%5d",a[counter]);
	printf("\n");
	return 0;
}
运行结果如下:


(2) 函数指针数组

#include<stdio.h>
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);
}
运行结果:



参考<C语言程序设计经典教程>,呵呵,基本算是摘取了。Whatever,说明白最重要。大笑

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值