C语言指针和指针变量-学习笔记(九)

一、基本概念

1、地址

(1)内存(内部存储器):由存储单元组成,其特点是存储单元线性连续。存储单元的最小单位是字节。
(2)地址:内存区的每一个字节有一个编号, “地址” 。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。
(3)变量的地址:变量在内存中总占用几个连续的字节,开始字节的地址,就是变量的地址。

2、变量的访问方式

(1)直接访问:按变量i地址存取变量值的方式(按变量名访问变量);
如:

printf("%d\n",i);
scanf("%d",&i);
k=i+j; 

(2)间接访问:将变量i的地址存放在另一个变量中。

3、指针和指针变量

(1)指针:一个变量的地址称为该变量的“指针”。
如:地址2000是变量i的指针。
(2)指针变量:若有一个变量专门用来存放另一变量的地址(即指针),则它称为“指针变量”。

i_pointer =&i;//将i的地址(2000)存放到i_pointer中。
定义指针变量i_pointer用来存放整型变量的地址,它被分配地址为(2000)、(2001)的两个字节。此时,i_pointer的值就是(2000) ,即变量i所占用单元的起始地址。要存取变量i的值,可采用间接方式:先找到存放“i的地址”的变量i_pointer ,从中取出i的地址(2000),然后到2000 、2001字节取出i的值(5)。
换句话说即是指针变量的值(即指针变量中存放的值)是地址(即指针), 它指向某一变量。

二、指针

1、指针变量中的“*”含义

(1)指针变量定义中的“*”
形式:基类型  *指针变量名;如:int *ptr;
指针变量定义中的“*”表示指针变量和变量之间是一种指向的关系,即“*”表示所定义的变量是指针变量。变量名是ptr,而非*ptr。
(2)引用中的“*”表示变量本身。也叫解引用(取目标)->* 返回指针指向的内存位置中的值
如:i_pointer   *i_pointer
这里*i_pointer表示指针变量i_pointer所指向的那个变量,也就是变量i。所以引用时*i_pointer==i。
如:int var=12,*ptr=&var;
    int temp = *ptr;	//等价于temp=var;
(3)指针变量“指向”变量 取地址->& 是一元运算符,它返回操作数的内存地址
如:i_pointer=&i
其中&是取地址运算符,表示把变量i的地址值送到变量i_pointer中。从而实现把指针变量i_pointer指向了变量i。
如:  int var=12,*ptr;
	 ptr = &var;
(4)“&”和“*”两个运算符的优先级别相同,但按自右而左方向结合
如已执行了语句pointer_1=&a; pointer_2=&b  ;
①&* pointer_1 
先进行* pointer_1运算,它就是变量a,再执行&运算。&* pointer_1与&a相同,即变量a的地址,就是将&a(a的地址)赋给pointer_2。
pointer_2=&* pointer_1;它的作用是将&a(a的地址)赋给pointer_2 ,如果pointer_2原来指向b,经过重新赋值后它已不再指向b了,而指向了a。
②*&a
先进行&a运算,得a的地址,再进行*运算。即&a所指向的变量,也就是变量a。*&a和*pointer_1的作用是一样的,它们都等价于变量a。即*&a与a等价。
③(*pointer_1)++ 和 *pointer_1++ 
(*pointer_1)++:*pointer_1相当a,所以整个表达式相当于a++*pointer_1++:++*同一优先级,结合方向自右而左,相当于*(pointer_1++)。由于++在pointer_1的右侧,是“后加”,因此先对pointer_1的原值进行*运算,得到a的值,然后使pointer_1的值改变,这样pointer_1不再指向a了。

2、指针的运算

指针只可进行加、减、关系运算,指针的运算实际上是地址的运算。
(1)算术运算

公式:(p)+n*数据类型长度;
形式:指针±整数  或者  指针 - 指针
①指针与整型值加减的结果是指针,表示使该指针指向该指针下移或上移存储单元个数(整型值)之后的内存地址。存储单元的大小就是该指针的数据类型所需的内存大小。
②指针与指针的减运算要求相减的两个指针属于同一类型,其结果是整数,表示两个指针之间的数据的个数。
(指针-指针)/数据类型长度=两个指针之间该类型的数据个数
如:int  *p1 , *p2 ;//p1==2000 p2==2006
故  p2-p1=(2006-2000)/2=3
如:
	int var, *ptr_var;
	ptr_var = &var;
	ptr_var ++;	

假定var存储在地址1000中,因为整数的长度是 4 个字节,ptr_var的值将是1004。
指针递增时,将指向其类型的下一个元素的内存位置,反之亦然。

用法:
#include 

int main()
{
	int a=10,b=12;
	int *p1=NULL,*p2=NULL;
	int var,*ptr_var;
	p1=&a;
	p2=&b;
	
	printf("p1=%d,p2=%d\n",p1,p2);//p1=1703724,p2=1703720
	printf("i=%d\n",p1-p2);//i=1
	
	ptr_var=&var;
	printf("ptr_var=%d\n",ptr_var);//ptr_var=1703708
	ptr_var++;
	printf("ptr_var=%d\n",ptr_var);//ptr_var=1703712
	
	while(1);
	return 0;
}

(2)关系运算
指向同一块连续存储单元(通常是数组)的两个指针之间可以进行关系运算。(相同类型)
如:
i)p1 若p1指向位置在p2之前,表达式成立,值为1。
若p1指向位置在p2之后,表达式不成立,值为0。
ii)p1= =p2
成立的话,表示p1、p2指向同一个位置。
指针比较
前提:两个指针都指向相同类型的变量,假设ptr_a和ptr_b分别指向a和b
C语言指针和指针变量-学习笔记(九)_第1张图片
(3)指针++运算

①p++:指针指向下一个元素。
②*p++++*运算同级,右结合,所以++是作用在p上,而不是作用在*p上。因为++写在右边,是先把p拿来参与*运算,得到*p,然后p自己再进行++,也就是移动指针指向下一个元素。等同于*(p++)。
设p指向数组a的第i个元素,则*(p++)相当于a[i++]  即a[i],i++
*(++p)相当于a[++i]  即i++,a[i] 
*(--p)相当于a[--i].*++p:p先++,然后对新p求*运算。
④(*p)++:p指向的元素的值加1

3、两种特殊指针

(1)悬浮指针:未指向实体的指针是“危险指针”
一个指针未指向任何实体就被引用,属于”内存盗用”!因为该指针将随意指向内存中某一单元,轻则误取或破坏其他实体的值,重则破坏操作系统的工作。
(2)空指针
在stdio.h中,定义#define NULL 0所以p=NULL;相当于p=0;它使p的存储单元中所有二进制位均为0。
可以用关系运算来判断指针变量p是否是空指针。(空指针不指向任何存储空间,只表示指针的一种状态)

例如: p= = 0 (p= =NULL)
      p!=0 (p!=NULL

在指针p指向某个实体的地址之前,不可对*p进行赋值。否则可能发生意想不到的错误(p随便指向某个单元)。
错误用法:

int *p1=NULL,*p2;
*p1=100;
*p2=200; 
printf("%d,%d\n",*p1,*p2);

正确用法:

#include 

int main(int argc,char const *argv[])
{
	int a;//定义
	int *p=NULL;//定义p为指针变量,此处*为标识,标识一个指针变量
	p=&a;//初始化,a的地址给p
	*p=200;
	printf("a=%d,a=%d\n",a,*p);//a=200,a=200
	while(1);
	return 0;
}

三、指针变量

1、指针变量的类型

该指针指向的内存空间所存储的数据类型。
一个变量的地址只可以存放在基类型与它本身类型相同的指针变量当中;基类型表示该指针变量将来可以指向的变量的类型。

2、指针变量定义和初始化及所占的字节空间大小

#include 

int main()
{
	int a=4;//定义
	short t=5;
	float b=35.62;
	char c='M';
	double d=3.1425926;
	
	int *p;//定义,p为指针变量,此处*为标识,标识一个指针变量
	short *p0;
	float *p1;
	char *p2;
	double *p3;
	
	p=&a;//初始化,a的地址给p
	p0=&t;
	p1=&b;
	p2=&c;
	p3=&d;
	
	printf("a=%d a=%d\n",a,*p);//a=4 a=4,此处*为取值,*p相当于a
	printf("a=%p a=%p\n",p,&a);//a=0019FF2C a=0019FF2C
	printf("sizeof(int):\n");
	printf("a=%d,*p=%d,p=%d\n",sizeof(a),sizeof(*p),sizeof(p));//a=4,*p=4,p=4
	printf("sizeof(short):\n");
	printf("t=%d,*p0=%d,p0=%d\n",sizeof(t),sizeof(*p0),sizeof(p0));//t=2,*p0=2,p0=4
	printf("sizeof(float):\n");
	printf("b=%d,*p1=%d,p1=%d\n",sizeof(b),sizeof(*p1),sizeof(p1));//b=4,*p1=4,p1=4
	printf("sizeof(char):\n");
	printf("c=%d,*p2=%d,p2=%d\n",sizeof(c),sizeof(*p2),sizeof(p2));//c=1,*p2=1,p2=4
	printf("sizeof(double):\n");
	printf("d=%d,*p3=%d,p3=%d\n",sizeof(d),sizeof(*p3),sizeof(p3));//d=8,*p3=8,p3=4
	
	while(1);
	return 0;
}

结论:VC6.0下无论什么类型的指针变量,所占字节空间大小都为4 Bytes。

3、指针变量的赋值

(1)把变量的地址赋给指针变量

如:int  a, *point_a; 
    point_a=&a;  

还可在定义指针变量的同时对其进行赋值如:int a; int *pointer_a=&a ;
也可以:int a , *pointer_a=&a ;
(2)用指针变量对指针变量进行赋值
C语言中允许用一个已经赋值过的指针变量向另一个同类型的指针变量赋值。
如:int a ; int *point_a=&a,*p ; p=point_a ;
(3)给指针变量赋值为符号常量NULL 例如:float *ptr_var3=NULL;
说明:NULL是一个空指针,表示该指针变量的值没有意义。头文件: “stdio.h”。
作用:避免对没有被初始化的指针变量的非法引用。

#include 

int main()
{
    int a,*pointer_a=&a;
	int *p=NULL;
	p=pointer_a;
	*p=10;//用指针方式对变量赋值
	printf("%d\n",a);//输出:10
	return 0;
}

(4)指针变量中只能存放地址(指针),不要将一个整数(或任何其他非地址类型的数据)赋给一个指针变量。若不同类型的指针变量之间可以赋值,但是会产生错误。

: int  i , *p1=&i ;
    char *p ;
    p=p1 ;

为了避免错误的产生,建议不这样使用。若想不同类型指针变量之间赋值,可以采用强制类型转换。
如:p=(char * ) p1 ;

4、指针变量基础应用

实例1:

#include 

int main()
{
	int a=4,b=5;//定义整型变量
	int *p,*s;;//定义p为指针变量,此处*为标识,标识一个指针变量
	p=&a;//初始化把a的地址给p
	s=&b; //初始化把b的地址给s
	
	printf("a=%d\n",*p);//a=4
	printf("p1=%p\n",p);//p1=0019FF2C
	printf("*p++=%d\n",*p++);//*p++=4,此处*为取值,*p相当于a
	printf("p2=%p\n",p);//p2=0019FF30
	//++和*为同一优先级,自右而左结合,*p++==*(p++)。++在p右侧,是"后加"
	//故先对p原值进行*运算,得到a值,后使p值+4,p不再指向a。
	
	printf("b=%d,*s=%d,s1=%p\n",b,*s,s);//b=5,*s=5,s1=0019FF28
	printf("(*s)++=%d\n",(*s)++);//(*s)++=5
	//*s相当于b,故整个表达式相当于b++
	printf("s2=%p\n",s);//s2=0019FF28
	while(1);
	return 0;
}

实例2:

#include 

int main(int argc,char const *argv[])
{
	int a=1;//定义整型变量a
	int *p;//定义整型指针变量p
	char ch='a';//定义字符型变量ch
	char *pc;//定义字符型指针变量pc
	p=&a;//整型指针变量p指向a
	pc=&ch;//字符型指针变量pc指向ch
	
	printf("a值:%d,a地址:%p\n",a,&a);//变量a值:1,a地址:0019FF2C
	printf("ch值:%c,ch地址:%p\n",ch,&ch);//ch值:a,ch地址:0019FF24
	
	printf("p:a值:%d,a地址:%p\n",*p,p);//p:a值:1,a地址:0019FF2C
	//变量a(*p)的值:1 (变量a的地址/指针变量p的值:a0)
	printf("pc:ch值:%c,ch地址:%p\n",*pc,pc);//pc:ch值:a,ch地址:0019FF24
	//变量ch(*pc)的值:1 (变量ch的地址/指针变量pc的值:9f)
	
	printf("p值:%p,p地址:%p\n",p,&p);//p值:0019FF2C,p地址:0019FF28
	//指针变量p的值:a0  指针变量p的地址:a4
	printf("pc值:%p,pc地址:%p\n",pc,&pc);//pc值:0019FF24,pc地址:0019FF20
	//指针变量pc的值:9f 指针变量pc的地址:a8
	
	while(1);
	return 0;
}

实例3:

#include 
#include 

int main()
{
    int a=10,b=1703716,c=20;
	int *p=NULL;
	int *p1=NULL;
	p=&a;
	printf("%d,%d\n",*p,p);

	p1=&c;
	p1=(int *)(b);
    
    printf("%d,%d\n",p1,*(int *)(b));
    
    //用例:#define GPIOC_CRL *(unsignedint*)(GPIOC_BASE+0x00) 
	return 0;
}
输出:
10,1703724
1703716,20

5、指针变量作为函数的参数

将指针作为函数的参数,传递变量的地址,进而在多个函数中访问相同的内存数据。
指针也可以作为函数的返回值,但是不要返回指向局部变量的指针。因为函数返回以后,其局部变量所占用的内存将随函数栈一起被释放,所得到的指针为野指针。如下:

int add()
{
   int* p;
   ....
   return p;
}//error

(1)指针变量实质为地址的原理:
①函数的参数可以是指针类型,实际参数可以是地址常量或指针变量,形式参数则可为指针变量,这种传递是单向值传递,即将实参指针变量的值传给对应的形参。但由于指针变量存放的是地址值,作为函数的参数来进行传送的是实参的值–变量的地址。
②实参用指针变量或者简单变量的地址,形参用指针变量,在子函数执行过程中使形参指针变量所指向的变量的值发生变化,在函数调用结束后返回主函数,使用这些已经变化了的变量值。这样就实现了“通过函数调用使变量值发生变化”的功能。
指针按地址传递:
①指针可以作为参数,把实参的地址传给形参
②允许函数访问内存位置,被调函数能够修改主调程序的参数的值
定义: getstr(char *ptr_str, int *ptr_int);
使用: getstr(pstr, &var);

(2)普通变量作为函数的参数
①调用函数输出两个数的和 (值传递,参数无法传回)

#include 

void add(int a,int b,int c)
{
	c=a+b;
	printf("add:a=%p,b=%p,c=%p\n",&a,&b,&c);
	printf("add:a+b=%d\n",c);
}

int main(int argc,char const *argv[])
{
	int a=10;
	int b=20;
	int c=0;
	add(a,b,c);//调用加法函数
	printf("add:a=%p,b=%p,c=%p\n",&a,&b,&c);
	printf("add:sum=%d\n",c);
	while(1);
	return 0;
}
输出:a+b=30,sum=0

②调用函数输出两个数的和 (值传递,可以返回值)

#include 

int add(int a,int b,int c)
{
    c=a+b;
	printf("add:a=%p,b=%p,c=%p\n",&a,&b,&c);
	printf("add:a+b=%d\n",c);
	return c;
}

int main(int argc,char const *argv[])
{
	int a=10;
	int b=20;
	int c=0;
	int sum=0;
	
	sum=add(a,b,c);//调用加法函数
    printf("add:a=%p,b=%p,c=%p\n",&a,&b,&c);
	printf("add:sum=%d\n",sum);
    while(1);
	return 0;
}
输出:
add:a=0019FEC8,b=0019FECC,c=0019FED0
add:a+b=30
add:a=0019FF2C,b=0019FF28,c=0019FF24
add:sum=30

(3)指针变量作为函数的参数
①调用函数输出两个数的和 (地址传递,不需返回值,值同步)

#include 

void add(int a,int b,int *pc)
{
	*pc=a+b;
	printf("add:a=%p,b=%p,c=%p\n",&a,&b,pc);
	printf("add:a+b=%d\n",*pc);//add:a+b=30
}

int main(int argc,char const *argv[])
{
	int a=10;
	int b=20;
	int c=0;
	
	add(a,b,&c);//调用加法函数
    printf("main:a=%p,b=%p,c=%p\n",&a,&b,&c);
	printf("main:sum=%d\n",c);//main:sum=30
    while(1);
	return 0;
}
输出:
add:a=0019FECC,b=0019FED0,c=0019FF24
add:a+b=30
main:a=0019FF2C,b=0019FF28,c=0019FF24
main:sum=30

②比较大小 (地址传递,不需返回值,值同步)

#include 

void cmp(int a,int b,int *pc)//比较得出最大值
{
	if(a>=b)
	{
		*pc=a;
	}
	else
	{
		*pc=b;
	}
}

int main(int argc,char const *argv[])
{
	int a=0;
	int b=0;
	int c=0;
	printf("please input a:");//please input a:12
	scanf("%d",&a);
	printf("please input b:");//please input b:13
	scanf("%d",&b);
	cmp(a,b,&c);//调用比较函数
	printf("max=%d\n",c);//max=13
	while(1);
	return 0;
}

③实现两个变量值交换 (地址传递,不需返回值,值同步)

#include 

void swap(int *a,int *b)
{
	int c=0;
	c=*a;
	*a=*b;
	*b=c;
}

int main(int argc,char const *argv[])
{
    int a=4,b=3;
	printf("a=%d,b=%d\n",a,b);//a=4,b=3
	swap(&a,&b);
    printf("a=%d,b=%d\n",a,b);//a=3,b=4
    while(1);
	return 0;
}

编辑 2020-06-21 12:30 首次编辑
增改 2021-07-06 23:24 内容结构优化

注:本文旨于作为自己的学习笔记,不作他用。

你可能感兴趣的:(C语言,c语言)