码迷,mamicode.com
首页 > 其他好文 > 详细

13.指针

时间:2018-08-16 00:59:04      阅读:192      评论:0      收藏:0      [点我收藏+]

标签:浮点型   fir   问题   ati   判断   如何使用   随机   average   整数   

//指针
/*
	C语言的指针简单又有趣,通过指针,可以简化一些C编程任务的执行,还有一些任务,
	如动态内存的分配,没有指针是无法执行的。所以对于C语言,学习指针是非常有必要的。
	在介绍指针之前,前面已经多次出现指针的概念了。
	也正如前面所讲的,每一个变量都有一个内存位置,每一个内存位置都定义了可使用&访问内存的地址,
	它代表了内存中的一个地址
*/
//举个栗子
#include <stdio.h>
int main01()
{
	int var1;
	int var2[10];
	printf("var1的变量地址:%p\n", &var1); //var1的变量地址:0061FF2C
	printf("var2的变量地址:%p\n", &var2); //var2的变量地址:0061FF04
	return 0;
}

//什么是指针?
/*
	指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。
	就像其他变量或者常量一样,必须在使用指针储存其他变量地址之前,对指针进行声明,
	声明指针的形式和声明变量的形式比较类似,type *var-name,就是在变量之前加上一个*,
	举个例子:
		int    *ip;     一个整型的指针 
		double *dp;     一个 double 型的指针 
		float  *fp;     一个浮点型的指针
		char   *ch;     一个字符型的指针 
	正所谓指针也是需要空间来进行存储的,也需要有一个变量来指向它,这个变量叫做指针变量。
	指针变量 int *p=&a; 整形变量int a=1;等等性质都是一样的,都是一个变量指向一个值。
	只不过p指向的变量是一个指针类型,所以叫做指针变量,指针变量指向的是一个指针,
	通过加上*那么可以找到指针指向的值。
	那么这个指针变量同样需要空间来进行存储,对于指针来说无论它指向的值有多大,占多少空间,
	对于指针本身来说,长度永远是固定的,对于相同位数的机器来说,一般只占4个字节。
	并且是一个十六进制数,因此指针之间是比较类似的,可能不同的就是所指向的值不一样。
	这里插一句:
		对于python来说,其列表按照索引查询的时间复杂度为o(1),并且不随着元素的增大而有所变化。
		这是为什么呢?因为python的列表是采用元素外置的方法,也就是说列表里面存储的不是元素本身,
		而是元素的指针,不管元素的值多大,其指针的大小是固定的,是4个字节,
		而列表又是一块连续的存储空间,通过首地址加上索引,可以瞬间计算出指针的偏移量,
		从而找到相应的元素的指针,在打印的时候会自动打印指针所指向的值。
		所以时间复杂度才为o(1),可见学习C语言对理解python还是很有帮助的,因为在py里,
		指针这种概念被高度封装起来,并不是摒弃掉,python里面大量用到了指针的概念,只不过我们接触不到罢了,
		但是学习指针,对了解python内部的数据结构是非常有帮助的。
*/

//如何使用指针
/*
	定义一个指针 int *p;
	获取指针指向的值*p,
	定义一个int变量,int a=1;
	获取a的地址&a
	把指针赋值给指针变量
	p = &a
*/
int main02()
{
	int var=20; //声明一个变量
	int *p; //声明一个指针
	p = &var; //在指针变量p中储存var的地址
	printf("var=%d,also=%d\n",var, *p);  //var=20,also=20
	printf("address of var=%p,also=%p\n", &var,p); //address of var=0061FF28,also=0061FF28
	return 0;
}

//C中的NULL指针
/*
	在指针变量的声明时,如果没有确切的地址可以赋值,那么最好为指针赋值为NULL。
	指针为NULL的话,表名这是一个空指针,NULL指针是一个定义在标准库中值为0的常量
*/
//举个栗子
int main03()
{
	int *p=NULL;
	printf("address of p=%p\n",p); //address of p=00000000
	return 0;
}

/*
	在大多数的操作系统上,程序不允许访问地址为0的内存,因为该内存是为操作系统保留的。
	内存地址0有特别重要的意义,它表明该指针指向一个不可访问的内存位置。
	如果想判断一个指针是不是空指针可以使用if语句
	if(p)
*/

//指针的算数运算
/*
	C 指针是一个用数值表示的地址。因此可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。
	假设 ptr 是一个指向地址1000的整型指针,是一个32位的整数,让我们对该指针执行下列的算术运算:
	ptr++
	在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,
	它都将指向下一个整数位置,即当前位置往后移 4 个字节.
	这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。
	如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。
*/
//递增一个指针
/*
	我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,因为数组是一个常量指针。
	下面的程序递增变量指针,以便顺序访问数组中的每一个元素:
*/
const int max=3;
int main04()
{
	int var[]={11,22,33};
	int i,*ptr;
	//指针指向数组地址
	ptr = var;//可以是var也可以是&var,也可以是&var[0],反正都是首元素的地址
	for (i=0;i<max;i++)
	{
		printf("store position:var[%d]=%x\n",i,ptr);
		printf("store value:var[%d]=%d\n",i,*ptr);
		//移动下一个位置
		ptr++;
	}
	/*
	store position:var[0]=0061FF1C
	store value:var[0]=11
	store position:var[1]=0061FF20
	store value:var[1]=22
	store position:var[2]=0061FF24
	store value:var[2]=33
	*/
}
//递减一个指针
int main05()
{
	int var[]={11,22,33,44};
	int i,*ptr;
	ptr = (var+3);
	for (i=sizeof(var)/sizeof(var[0])-1;i>=0;i--)
	{
		printf("store address:var[%d]=%x\n", i,ptr);
		printf("store value:var[%d]=%d\n",i, *ptr);
		ptr--;
	}
	/*
		store address:var[3]=61ff24
		store value:var[3]=44
		store address:var[2]=61ff20
		store value:var[2]=33
		store address:var[1]=61ff1c
		store value:var[1]=22
		store address:var[0]=61ff18
		store value:var[0]=11
	*/
}

//指针数组
int main06()
{
	int var[]={11,22,33};
	int i;
	for (i=0;i<3;i++)
	{
		printf("value of var[%d]=%d\n",i,var[i]);
	}
	/*
	value of var[0]=11
	value of var[1]=22
	value of var[2]=33
	*/
	return 0;
}

const int num=4;
int main07(){
	int var[]={11,22,33,44};
	int i,*ptr[num];//声明一个指针数组,里面有4个元素都是指针类型
	for (i=0;i<num;i++)
	{
		ptr[i]=&var[i];//每一个var里面每一个元素的指针都复制个ptr里面的指针变量
	}
	for (i=0;i<num;i++)
	{
		printf("var[%d]=%d,also=%d\n",i,var[i],*ptr[i]);
	}
	return 0;
	/*
		var[0]=11,also=11
		var[1]=22,also=22
		var[2]=33,also=33
		var[3]=44,also=44
	*/
}
//也可以用一个指向字符的指针数组来存储一个字符串列表
int main08()
{
	const char *name[]={
		"satori",
		"mashiro",
		"nagisa",
		"matsuri"
	};
	char s[10];
	int i;
	for (i=0;i<4;i++)
	{
		printf("value of name[%d]=%s\n",i,name[i]);
		//之前也说过,数组的地址就是首元素的地址
		printf("first element address=%p,but !=%p,also=%p\n",name[i],&name[i],&(name[i][0]));
		/*
			value of name[0]=satori
			first element address=00405148,but !=0061FF1C,also=00405148
			value of name[1]=mashiro
			first element address=0040514F,but !=0061FF20,also=0040514F
			value of name[2]=nagisa
			first element address=00405157,but !=0061FF24,also=00405157
			value of name[3]=matsuri
			first element address=0040515E,but !=0061FF28,also=0040515E
		*/
		//关于name[i]为什么不等于&name[i]
		//因为name是一个指针数组,那么name[i]获取到的仍然一个指针,指向了相应的字符串的首元素
		//我们直接按照字符串格式打印是没问题的,打印的就是整个字符串
		//当做指针打印也是没问题的,打印的是首元素的地址,至于为什么&name[i]不行,
		//还记得之前说的对于指向数组的指针来说str和*str是不一样的吗?
		//因为str已经是一个指针了,所以不能再使用&了
		//但对于char str[]="xxx"来说,因为str不是指向字符数组的指针, 而是一个字符数组
		//所以以以指针打印&str是没问题的,获取的就是首元素的地址
		//对于以指针形式打印str,也会自动打印首元素的地址。
		//因此打印首元素的地址,对于字符数组来说,str和&str是一样。
		//但是对于指向字符数组的指针来说,str和&str是不一样的。
		//但是把指向数组的指针当成数组来用的话,就举上面的例子&(name[i][0])和name[i]是一样的
		//因为name[i]虽然是地址,但是name[i][0]依旧可以获取首元素,再加上&也是可以的
	}
}

//指向指针的指针
/*
	指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。
	通常,一个指针包含一个变量的地址。
	当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。
	pointer       pointer        variable
	address       address        value
	
	一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。例如,下面声明了一个指向 int 类型指针的指针:
	int **var;
	当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符,如下面实例所示:
*/
int main09()
{
	int var;
	int *ptr;
	int **pptr;
	var=3000;
	//获取var的地址
	ptr=&var;
	pptr=&ptr;
	//使用pptr获取值
	printf("value of var=%d,also=%d,also=%d\n",var,*ptr,**pptr);//value of var=3000,also=3000,also=3000
	return 0;
}

//传递指针给函数
/*
	C语言允许传递指针个函数,只需声明函数类型为指针即可*/
#include <time.h>
void getSecond(unsigned long *sec);
int main10()
{
	unsigned long sec;
	//定义一个无符号长整型,然后将指针传递给函数
	//这样指针便指向了所在的内存,然后只要修改指针所指向的值
	//就可以进行修改,这在python里面是做不到的,因为在py里会指向一个新值,不会修改原来的
	getSecond(&sec);
	//输出实际值
	printf("Number of seconds=%d\n",sec); //Number of seconds=1534338344
	return 0;
}
void getSecond(unsigned long *sec)
{
	//获取当前的秒数
	*sec = time(NULL);
	return;
}

//在学C之前,有人说C难学,尤其是学到指针的时候,指针和数组之间的关系不晕才怪
//表示自己在学的时候,没感觉到,虽然在反复强调, 现在回想一下,是有些相似哈。
//指针可以指向数组的第一个元素的地址,从而当做数组打印所有元素
//而数组本身的地址又是首元素的地址,所以两者确实是比较相似的
//那么函数的参数是指针类型的话, 除了接收指针,也可以接收数组
double getAvg(int *arr, int size);
int main11()
{
	int arr[]={11,22,33,44,55};//带有5个元素的数组
	double avg1, avg2,avg3;
	//传递一个指向数组的指针作为函数的参数
	//我们的getAvg显然第一个参数要是指针类型,那么这里我们传入arr,&arr,&arr[0]都是一样的
	avg1 = getAvg(arr,5);
	avg2 = getAvg(arr,5);
	avg3 = getAvg(arr,5);
	//输出返回值
	printf("average of arr=%f,also=%f,also=%f\n",avg1,avg2,avg3);
	//average of arr=33.000000,also=33.000000,also=33.000000
}
double getAvg(int *arr,int size)
{
	int i;
	int sum=0;
	double avg;
	for(i=0;i<size;i++)
	{
		sum += arr[i];
	}
	avg = (double)sum / size;
	return avg;
}

//从函数返回指针
/* 函数不支持返回一个数组,但是可以返回一个指向数组的指针,我们知道,返回数组的指针
   就等于是返回数组了,反正指针也可以实现数组的功能,当成数组来用,访问数组的每一个值
   所以说指针和数组比较相似,但不至于晕,
   但是注意的是,指针也不能够直接返回,必须定义static
*/
//由于我们返回的是指针,所以这里必须要加上星号,
//如果不加,表示返回一个整型,加上*表示返回一个指针
int *getRandom()
{
	//必须定义static
	static int r[10];
	int i;
	srand((unsigned)time(NULL));//设置随机种子
	for (i=0;i<10;i++)
	{
		r[i]=rand();
		printf("r[%d]=%d\n",i ,r[i]);
	}
	return r;
}
int main12()
{
	int *p;//定义一个指针 
	int i;
	p = getRandom();
	for (i=0;i<10;i++)
	{
		//p是数组首元素的指针,既可以通过*(p+i)访问,也可以当成数组通过p[i]的方式来访问
		printf("*(p+%d)=%d,also=%d\n",i,*(p+i), p[i]);
	}
}
/*
	r[0]=18171
	r[1]=23618
	r[2]=17990
	r[3]=13924
	r[4]=6267
	r[5]=25621
	r[6]=20508
	r[7]=29240
	r[8]=8512
	r[9]=15428
	*(p+0)=18171,also=18171
	*(p+1)=23618,also=23618
	*(p+2)=17990,also=17990
	*(p+3)=13924,also=13924
	*(p+4)=6267,also=6267
	*(p+5)=25621,also=25621
	*(p+6)=20508,also=20508
	*(p+7)=29240,also=29240
	*(p+8)=8512,also=8512
	*(p+9)=15428,also=15428
*/
/*
	关于c不能返回指针,而定义了static就能返回了呢?
	首先,指针必须要指向一块合法的内存,比方我随便定义一个指针,指向一块内存
	int *p=0xffff;这显然是不合法的,因为0xffff是我瞎几把定义的,没有指向一个合法的值,
	当然我们定义这个指针本身是没有问题,可以随便的赋值,随便的玩耍,但是当我们使用*p的时候,
	那么就会去操作p这个指针指向的内存,而访问的内存必须是已经向操作系统申请完毕的内存,
	显然我们这里没有申请,便直接访问了,这是不允许的,因此在某些编译器上便会出现段错误。
	我们这里也是一样的道理,当我们返回一个指针,这个指针本来是指向一块合法的内存空间,
	但是当函数执行完毕之后,那么里面所有的局部变量就都被释放了,原本指向的内存,已经被操作系统回收了。
	那么当我们再main中再次访问的时候,那片内存空间已经不再是我们可以涉及的领域了。
	因此也就变成了野指针,野指针指的就是指向了不合法地址的指针。
	那么为什么加上static就能返回了呢?因为局部变量是储存在内存的栈区,函数执行完毕,
	局部变量所占的内存便被释放了,函数内的变量不再拥有那个内存地址,所以不能返回指针,
	但是当我们加上了static之后,那么变量的值就会存储在内存中的静态数据区,不会随着函数的执行结束而被清除,
	故能返回其地址
*/

//笔记
/*
	指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。
	在使用指针存储其他变量地址之前,对其进行声明。
	要理解指针就要先理解计算机的内存。计算机内存会被划分为按顺序编号的内存单元。
	每个变量都是存储在内存单元中的,称之为地址。
	并且内存地址是按照字节划分的,一个字节对应一个地址,所以说int类型占4个字节,
	所以会用四个地址。
*/
int main13()
{
	int var=20;//实际变量的声明,此时var这个变量是存在内存地址的,地址对应某个内存单元,该单元存储了数据20
	int *p; //定义了一个指针,即一个内存单元的地址变量
	p = &var;//这指针变量中存储var的地址,就是将地址赋给了这个指针变量;
	printf("value=%d\n",*p);//利用指针指向的内存输出值
	printf("addr=%p,also=%p\n",p,&var);//用p直接输出地址值,因为存储的就是地址的值
	/*
		value=20
		addr=0061FF28,also=0061FF28
	*/
}

//指针的一些说明
/*
	int p;这是一个普通的整型变量
	int *p;首先p与*结合,说明p是一个指针,然后与int结合,说明指针指向的是一个整型元素
	int p[3];首先p与[]结合说明p是一个数组,然后与int结合说明p是一个整型数组,整体就是整形数组的第4个元素
	int *p[3];首先p与[]结合,因为优先级比*高,说明是一个数组,然后与*结合,说明数组里面的每一个元素
			  都是一个指针类型,在与int结合,说明指针指向的是一个整型
	int **p;p与*结合,说明p是一个指针,再与*结合,说明p是一个指向指针的指针,再与int结合,说明*p指向的
			是一个整型
	int p(int);从 p 处起,先与 () 结合, 说明 p 是一个函数, 然后进入 () 里分析, 说明该函数有一个整型变量的参数, 
				然后再与外面的 int 结合, 说明函数的返回值是一个整型数据。
	int (*p)(int);从 p 处开始, 先与指针结合, 说明 p 是一个指针, 然后与()结合, 说明指针指向的是一个函数
				  然后再与()里的 int 结合, 说明函数有一个int 型的参数
				  再与最外层的 int 结合, 说明函数的返回类型是整型, 
				  所以 p 是一个指向有一个整型参数且返回类型为整型的函数的指针。
*/

  

13.指针

标签:浮点型   fir   问题   ati   判断   如何使用   随机   average   整数   

原文地址:https://www.cnblogs.com/traditional/p/9484501.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!