指针(pointer)简介
指针是一个值为内存地址的变量
例如
int year;
year = 2016;
定义一个整型变量,系统就给这个整个变量分配了一个内存空间,这个内存空间的名字叫做year,空间里的值为2016,内存在计算机中的地址为0028FF44(计算机中的内存地址一般用16进制表示)。
int* ptr_year; //ptr_year为一个变量,可以把int*看作是一种数据类型,一种指向地址的数据类型
ptr_year=&year // "&"为取地址符,相当于把变量year的地址取出来赋给ptr_year这个变量,赋值时一定要用&取地址符,而不能使用常量进行直接赋值
上图即为原理流程图
基本用法:
数据类型* 指针变量名;
例如:
int* ptr_year;
char* ptr_name;
float* ptr_score;
间接运算符 * 在声明指针变量时和在对地址操作时的作用是不同的
int num;
num = 1; //定义一个整型变量,并赋初值为5
int* ptr_num; //这个星号表示的是ptr_num是一个指针变量
ptr_num = # //定义一个指针变量,指向num num的值为1
*ptr_num = 1111; //把20赋值给*ptr_num地址所对应空间, num的值变为1111
单独一个*加上一个指针表示的是对这个指针所对应的的空间进行操作
注意:
指针的变量的命名规则和其他变量的命名规则一样
指针不能与现有变量同名
指针可存放c语言中的任何基本数据类型,数组和其他所有高级数据结构的地址
若指针已声明为指向某种类型数据的地址 ,则它不能用于存储其他类型数据的地址
应为指针指定一个地址后,才能在语句中使用指针
在头文件<stdio.h>中,NULL被定义为常量
int* pr_num = NULL; //表示指针的初值为空,表示指针不指向任何地址
指针变量必须初始化,否则不知道指向哪个地址。
数组元素的指针
1、简介
一个变量有地址,一个数组包含若干个元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既可以指向变量,当然也可以指向数组元素,所谓的数组元素的指针就是数组元素的地址。
可以用一个指针变量指向一个数组元素,例如
int a[10] = { 1, 2 ,3,4,5,6,7,8,9,10 }; //定义了一个包含有十个整型数据的数组
int* p; //定义了p为指向整型变量的指针变量
p = &a[0]; //把a[0]元素的地址赋值给指针变量p
在c语言中规定,数组名即为数组的首地址,所以以下写法等效
p = &a[0] ; <==> p = a;
注意:数组名不代表整个数组,只代表数组里的第一个元素的地址。
2、通过指针引用数组元素
引用数组元素可以用下面两种方法
(1)下标法
如a[i]形式
(2)指针法
如 *(a+i)或*(p+i)。其中a是数组名,p是指向数组首地址的指针变量,p = a。
数组指针与指针数组
数组指针(也称行指针)
定义 int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
所以数组指针也称指向一维数组的指针,亦称行指针。
指针数组
定义 int *p[n];
[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1是错误的,这样赋值也是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
如要将二维数组赋给一指针数组:
int *p[3];
int a[3][4];
for(i=0;i<3;i++)
p[i]=a[i];
这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
所以要分别赋值。
结构体指针
1、简介
所谓的结构体指针就是指向该结构体变量的指针,一个结构体变量的起始地址就是这个结构体变量的指针。如果把一个结构体变量的起始地址存放在一个指针变量中,那么这个指针变量就指向该结构体变量。
指向结构体对象的指针变量既可以指向结构体变量,也可以指向结构体数组中的元素。指针变量的基类型必须与结构体变量的类型相同。
例如: struct Student * p; //p可以指向struct Student 类型的变量或数组元素
struct stu{ char *name; //姓名 int num; //学号 int age; //年龄 char group; //所在小组 float score; //成绩 } stu1 = { "Tom", 12, 18, 'A', 136.5 }; //结构体指针 struct stu *pstu = &stu1;
也可以在定义结构体的同时定义结构体指针:
struct stu{ char *name; //姓名 int num; //学号 int age; //年龄 char group; //所在小组 float score; //成绩 } stu1 = { "Tom", 12, 18, 'A', 136.5 }, *pstu = &stu1;
注意,结构体变量名和数组名不同,数组名在表达式中会被转换为数组指针,而结构体变量名不会,无论在任何表达式中它表示的都是整个集合本身,要想取得结构体变量的地址,必须在前面加&
,所以给 pstu 赋值只能写作:
struct stu *pstu = &stu1;
而不能写作:
struct stu *pstu = stu1;
还应该注意,结构体和结构体变量是两个不同的概念:结构体是一种数据类型,是一种创建变量的模板,编译器不会为它分配内存空间,就像 int、float、char 这些关键字本身不占用内存一样;结构体变量才包含实实在在的数据,才需要内存来存储。下面的写法是错误的,不可能去取一个结构体名的地址,也不能将它赋值给其他变量
struct stu *pstu = &stu;
struct stu *pstu = stu;
2、通过指针引用结构体变量
通过结构体指针可以获取结构体成员,一般形式为:
(*pointer).memberName
或者:
pointer->memberName
第一种写法中,.
的优先级高于*
,(*pointer)
两边的括号不能少。如果去掉括号写作*pointer.memberName
,那么就等效于*(pointer.memberName)
,这样意义就完全不对了。
第二种写法中,->
是一个新的运算符,习惯称它为“箭头”,有了它,可以通过结构体指针直接取得结构体成员;这也是->
在C语言中的唯一用途。
上面的两种写法是等效的,我们通常采用后面的写法,这样更加直观。