C语言指针
指针
一个变量的地址称为该变量的指针。
使用一个变量来存放另外一个变量的地址,这样的变量被称为指针变量。
指针变量的声明
声明指针变量的一般形式:
类型名 * 指针变量名;
在变量名前面加*
号表示该变量是指针变量。
指针变量的类型就是该指针变量指向的变量的类型。
int *p1, *p2; //指向int类型对象的指针变量
char *r;
double *d;
在声明指针变量时应注意:
定义指针变量时必须指定数据类型。
C语言要求每个指针变量只能指向一种特定类型(引用类型)的对象。
初始化指针变量
初始化指针变量的方法是:使用取地址运算符(&
)把某个变量的地址赋给它。
先声明指针变量,再初始化指针变量。
int a = 10;
int *p;
p = &a; //将a的地址赋给指针p
声明指针变量的同时初始化。
int a = 10;
int *p = &a; //将a的地址赋给指针p
说明:&a 为变量 a 的地址,p 为指针变量, *p 为 p 当前所指向的对象。
不能用一个整数给一个指针变量赋值。
间接寻址运算符
一旦指针变量指向了对象,就可以使用间接寻址运算符(*
)访问存储在对象中的内容。
int a = 10;
int * p = &a; //p指向了变量a
*p = 1; //将1赋给p指向的变量,即a = 1,*p = 1
不要把间接寻址运算符用于未初始化的指针变量。
int *p; //错误,*p、p 的值未定义
*p = 1; //错误,可能会修改存储在未知地址的数据。
指针变量赋值
C语言允许使用赋值运算符进行指针的复制,前提是两个指针具有相同的类型。
int a = 10;
int *p1, *p2; //定义了指向int类型的指针变量
p1 = &a; //p1指向了a
p2 = p1; //把a的地址复制给了p2,p2同样也指向了a
*p1 = 0; //a等于0
*p2 = 5; //a等于5
任意数量的指针变量都可以指向同一个对象。
int i = 1, j = 2;
int *p = &i; //p指向了i
int *q = &j; //q指向了j
*p = *q; //将 j(*q)的值赋值给 i(*p)
指针作函数参数
因为C语言用值进行参数传递,所以在函数调用中用作实际参数的变量无法改变。
变量的地址作为函数的参数,可以实现通过调用函数使变量的值发生变化,在主调函数使用这些改变了的值。
void swap(int* p1, int* p2) {
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main()
{
int a = 10,b = 20;
swap(&a,&b); // a = 20,b = 10
}
用const保护参数
可以使用 const 表明函数不会改变指针参数所指向的对象。
void f(const int *p) {} //p是指向“常整数”的指针,不能试图改变*p
指针作为返回值
指针作为返回值的函数简称指针函数。
定义指针函数的一般形式:
类型名 * 函数名(参数列表);
函数调用()
的优先级高于*
,因此函数名先和括号相结合。
/*返回两个整数中较大的那个整数的地址*/
int* max(int *a,int *b)
{
if(*a > *b)
return a; //返回指针 a
else
return b; //返回指针 b
}
main()
{
int i = 10;
int j = 5;
int *p = max(&i,&j); //*a 是 i 的别名,*b 是 j 的别名
}
函数也可以返回指向外部变量或指向声明为 static 的局部变量的指针。
永远不能返回指向自动局部变量的指针。
指向指针的指针
如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针。
指针变量也是一种变量,也会占用存储空间,也可以使用&
获取它的地址。
C语言不限制指针的级数,每增加一级指针,在定义指针变量时就得增加一个星号*
。
例:
int main()
{
int i = 10;
int* p1 = &i; //p1 是一级指针,指向普通类型的数据
int** p2 = &p1; //p2 是二级指针,指向一级指针
printf("%d,%d\n",*p1);
printf("%d,%d\n",**p2);
return 0;
}
指针数组
一个数组,若其元素均为指针类型数据,该数组称为指针数组。
定义一维指针数组的一般形式为:
类型名 * 数组名 [数组长度];
也就是说,指针数组中的每一个元素都存放一个地址,相当于一个指针变量。
声明一个数组,包含 4 个元素,每个元素的类型是 int 型指针:
int* p[4]; //等价于 int* (p[4])
由于[]
比*
优先级高,因此 P 先与[]
结合,形成数组形式,然后再与*
结合。
int main()
{
int i = 10;
int j = 12;
int k = 15;
int* arr[3] = {&i,&j,&k}; //arr 是指针数组
int** p = arr; //p 是二级指针,指向 int*
printf( "%d\n", **(p + 1) ); //指针访问数组元素 j
printf( "%d\n", *p[1] ); //下标访问数据元素 j
return 0;
}
指向函数的指针
指向函数的指针简称函数指针。
一个函数在编译时被分配给一个起始地址(入口地址);每次调用函数时,都从该地址开始执行此段函数代码。
函数名就代表函数的起始地址。
定义指向函数的指针一般形式:
函数类型 (*变量名) (函数参数列表)
声明 p 是一个指向函数的指针,它可以指向函数类型为整型且有两个整型参数的函数:
int (*p)(int,int); //等价于int (*p)(int a,int b);
括号不能省略,p 先与*
结合,表示它是指针变量。
通过函数指针调用函数:
/*取两个整数中大的那个*/
int max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
int (*p) (int, int);
p = max; //函数指针变量赋值
printf( "%d\n", p(7, 6) );//调用 max 函数
return 0;
}
说明:
- 指向函数的指针,只能指向在定义时指定的类型的函数(函数类型和形参列表匹配)。
- 在给函数指针赋值时,只须给出函数名而不必给出参数。
- 用函数指针调用函数时,须用指针变量名代替函数名。
- 对指向函数的指针不能进行算术运算。
指针的数据类型小结
变量定义 | 类型表示 | 含义 |
---|---|---|
int* p | int* | 定义 p 为指向整型数据的指针变量 |
const int* p | const int* | p 是指向常量的指针变量,不能通过 p 改变其指向的对象的值 |
const int const *p | const int const * | p 是指向常量的常指针,其指向不能改变,且不能通过 p 改变其指向的对象的值 |
int* p() | int* () | p 为返回一个指针的函数,该指针指向整型数据 |
int (*p) () | int (*) () | p 为指向函数的指针,该函数返回一个整型数据 |
int* p[4] | int* [4] | 定义 p 为指针数组,它由4个指向整型数据的指针元素组成 |
int (*p) [4] | int (*) [4] | p 为指向包含4个元素的一维数组的指针变量 |
int* *p | int* * | p 为指针变量,它指向一个指向整型数据的指针变量 |
void* p | void* | 定义 p 为指向空类型的指针变量,不指向具体的对象 |