指针是什么?
指针就是内存地址,用来存放内存地址的变量。和其它变量和常量一样,必须在使用之前,对其进行声明。
指针变量声明的一般形式:
type *var_name
type:指针的基类型
var_name:指针变量的名称
以下是一些有效的指针声明:
int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */
所有实际数据类型对应指针的值的类型都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是指针所指向的变量或常量的数据类型不同。
怎么使用?
使用指针时会频繁进行以下几个操作:
1、定义一个指针变量;
2、把变量地址赋值给指针;
3、访问指针变量中可用地址的值。
这些是通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值。
例如:
#include <stdio.h>
int main ()
{
int var = 20; /* 实际变量的声明 */
int *p; /* 指针变量的声明 */
p = &var; /* 在指针变量中存储 var 的地址 */
printf("var 变量的地址: %p\n", &var );
/* 在指针变量中存储的地址 */
printf("p 变量存储的地址: %p\n", p );
/* 使用指针访问值 */
printf("*p 变量的值: %d\n", *p );
return 0;
}
执行结果:
var 变量的地址: 00000093c55ffeb4
p 变量存储的地址: 00000093c55ffeb4
*p 变量的值: 20
NULL指针
赋为 NULL 值的指针被称为空指针。NULL 指针是一个定义在标准库中的值为零的常量。
#include <stdio.h>
int main()
{
int *p = NULL;
printf("p 的地址是 %p\n", p);
return 0;
}
该程序的执行结果为:
p 的地址是 0000000000000000
可以用以下if语句检查某个指针是否为空指针:
if(p) /* p非空,则执行。 */
if(!p) /* p为空,则执行。 */
指针的算术运算
可以使用以下四种运算符对指针进行运算操作:++、--、+、-。
例:如果p是指向地址1000的整形指针,则经过p++后,其将指向地址1004。
整形指针每增加一次,指针将往后移4字节。
例:如果p指向地址为1000的字符,则经过p++后,其将指向地址1001。
指针的每一次递增,它其实会指向下一个元素的存储单元。
指针的每一次递减,它都会指向前一个元素的存储单元。
指针在递增和递减时跳跃的字节数取决于指针所指向变量数据类型长度,比如 int 就是 4 个字节。
指针递增
变量指针可以递增,而数组不能递增。以下是一个程序例子。
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *p;
/* 指针中的数组地址 */
p = var;
for ( i = 0; i < MAX; i++)
{
printf("存储地址:var[%d] = %p\n", i, p );
printf("存储值:var[%d] = %d\n", i, *p );
/* 指向下一个位置 */
p++;
}
return 0;
}
执行结果:
存储地址:var[0] = 00000078d33ffa64
存储值:var[0] = 10
存储地址:var[1] = 00000078d33ffa68
存储值:var[1] = 100
存储地址:var[2] = 00000078d33ffa6c
存储值:var[2] = 200
指针递减
与递增同理。
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *p;
/* 指针中最后一个元素的地址 */
p = &var[MAX-1];
for ( i = MAX; i > 0; i--)
{
printf("存储地址:var[%d] = %p\n", i-1, p );
printf("存储值:var[%d] = %d\n", i-1, *p );
/* 指向下一个位置 */
p--;
}
return 0;
}
执行结果:
存储地址:var[2] = 000000c6f69ffbfc
存储值:var[2] = 200
存储地址:var[1] = 000000c6f69ffbf8
存储值:var[1] = 100
存储地址:var[0] = 000000c6f69ffbf4
存储值:var[0] = 10
指针的比较
指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。
下面的程序修改了上面的实例,只要变量指针所指向的地址小于或等于数组的最后一个元素的地址 &var[MAX - 1],则把变量指针进行递增:
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *p;
/* 指针中第一个元素的地址 */
p = var;
i = 0;
while ( p <= &var[MAX - 1] )
{
printf("存储地址:var[%d] = %p\n", i, p );
printf("存储值:var[%d] = %d\n", i, *p );
/* 指向上一个位置 */
p++;
i++;
}
return 0;
}
运行结果:
存储地址:var[0] = 0000007b9c1ff8b4
存储值:var[0] = 10
存储地址:var[1] = 0000007b9c1ff8b8
存储值:var[1] = 100
存储地址:var[2] = 0000007b9c1ff8bc
存储值:var[2] = 200
指针数组
指针数组通常用于处理多个数据对象,例如字符串数组或其他复杂数据结构的数组。
以下程序用到了一个由3个整数组成的数组:
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i;
for (i = 0; i < MAX; i++)
{
printf("Value of var[%d] = %d\n", i, var[i] );
}
return 0;
}
结果:
Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200
有时我们要让数组存储指向 int 或 char 或其他数据类型的指针。
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *p[MAX];
for ( i = 0; i < MAX; i++)
{
p[i] = &var[i]; /* 赋值为整数的地址 */
}
for ( i = 0; i < MAX; i++)
{
printf("Value of var[%d] = %d\n", i, *p[i] );
}
return 0;
}
结果:
Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200
如果使用的是指向字符的数组:
#include <stdio.h>
const int MAX = 4;
int main ()
{
const char *names[] = {
"Zara Ali",
"Hina Ali",
"Nuha Ali",
"Sara Ali",
};
int i = 0;
for ( i = 0; i < MAX; i++)
{
printf("Value of names[%d] = %s\n", i, names[i] );
}
return 0;
}
结果:
Value of names[0] = Zara Ali
Value of names[1] = Hina Ali
Value of names[2] = Nuha Ali
Value of names[3] = Sara Ali
以下是一个简单的例子:
#include <stdio.h>
int main() {
int num1 = 10, num2 = 20, num3 = 30;
// 声明一个整数指针数组,包含三个指针
int *ptrArray[3];
// 将指针指向不同的整数变量
ptrArray[0] = &num1;
ptrArray[1] = &num2;
ptrArray[2] = &num3;
// 使用指针数组访问这些整数变量的值
printf("Value at index 0: %d\n", *ptrArray[0]);
printf("Value at index 1: %d\n", *ptrArray[1]);
printf("Value at index 2: %d\n", *ptrArray[2]);
return 0;
}
结果:
Value at index 0: 10
Value at index 1: 20
Value at index 2: 30
指向指针的指针
指向指针的指针是一种多级间接寻址的形式。
使用类似于下面的代码进行声明。
int **var;
#include <stdio.h>
int main () {
int V;
int *Pt1;
int **Pt2;
V = 100;
/* 获取 V 的地址 */
Pt1 = &V;
/* 使用运算符 & 获取 Pt1 的地址 */
Pt2 = &Pt1;
/* 使用 printf 获取值 */
printf("var = %d\n", V );
printf("Pt1 = %p\n", Pt1 );
printf("*Pt1 = %d\n", *Pt1 );
printf("Pt2 = %p\n", Pt2 );
printf("**Pt2 = %d\n", **Pt2);
return 0;
}
结果:
var = 100
Pt1 = 000000fc221ffd14
*Pt1 = 100
Pt2 = 000000fc221ffd08
**Pt2 = 100
传递指针给函数
C 语言允许传递指针给函数,只需简单声明函数参数为指针类型。
以下是以指针作为参数的例子:
#include <stdio.h>
#include <time.h>
void getSeconds(unsigned long *par)
{
/* 获取当前的秒数 */
*par = time( NULL );
return;
}
int main ()
{
unsigned long sec;
getSeconds( &sec );
/* 输出实际值 */
printf("Number of seconds: %ld\n", sec );
return 0;
}
Number of seconds: 1715861345
以下是以数组作为参数的例子:
#include <stdio.h>
/* 函数声明 */
double getAverage(int *arr, int size) {
int i, sum = 0;
double avg;
for (i = 0; i < size; ++i) {
sum += arr[i];
}
avg = (double)sum / size;
return avg;
}
int main () {
/* 带有 8 个元素的整型数组 */
int balance[8] = {10, 34, 63, 517, 50, 152, 55, 14};
double avg;
/* 传递一个指向数组的指针作为参数 */
avg = getAverage( balance, 8 ) ;
/* 输出返回值 */
printf("Average value is: %f\n", avg );
return 0;
}
Average value is: 111.875000
从函数返回指针
C 允许从函数返回指针。声明方式:
int * yourFunc()
{
/*
type your function code here...
*/
}
C 不支持在调用函数时返回局部变量的地址,除非定义局部变量为 static 变量。
以下程序将返回15个随机数,并随数附赠表示其指针的数组名。
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
/* 要生成和返回随机数的函数 */
int * getRandom( ) {
static int r[15];
int i;
/* 设置种子 */
srand( (unsigned)time( NULL ) );
for ( i = 0; i < 15; ++i) {
r[i] = rand();
printf("%d\n", r[i] );
}
return r;
}
/* 要调用上面定义函数的主函数 */
int main () {
/* 一个指向整数的指针 */
int *p;
int i;
p = getRandom();
for ( i = 0; i < 15; i++ ) {
printf("*(p + [%d]) : %d\n", i, *(p + i) );
}
return 0;
}
20333
3255
2565
21573
18937
23224
17757
6006
28927
8978
1598
4158
14499
2421
8560
*(p + [0]) : 20333
*(p + [1]) : 3255
*(p + [2]) : 2565
*(p + [3]) : 21573
*(p + [4]) : 18937
*(p + [5]) : 23224
*(p + [6]) : 17757
*(p + [7]) : 6006
*(p + [8]) : 28927
*(p + [9]) : 8978
*(p + [10]) : 1598
*(p + [11]) : 4158
*(p + [12]) : 14499
*(p + [13]) : 2421
*(p + [14]) : 8560
函数指针
函数指针是指向函数的指针变量。函数指针类型的声明:
typedef int (*func_p)(int,int); // 声明一个指向同样参数、返回值的函数指针类型
下方例子声明函数指针p指向函数max:
#include <stdio.h>
int max(int x, int y) {
return x > y ? x : y;
}
int main(void) {
/* p 是函数指针 */
int (* p)(int, int) = & max; // &可以省略
int a, b, c, d;
printf("请输入三个数字:");
scanf("%d %d %d", & a, & b, & c);
/* 与直接调用函数等价,d = max(max(a, b), c) */
d = p(p(a, b), c);
printf("最大的数字是: %d\n", d);
return 0;
}
结果:
请输入三个数字:12489 19233 21904
最大的数字是: 21904
回调函数
函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。
#include <stdlib.h>
#include <stdio.h>
void pop_arr(int *arr, size_t arrsize, int (*getNextValue)(void)) {
for (size_t i = 0; i < arrsize; i++)
arr[i] = getNextValue();
}
// 获取随机值
int random(void) {
return rand();
}
int main(void) {
int marr[10];
/* random 不能加括号,否则无法编译,因为加上括号之后
相当于传入此参数时传入了 int , 而不是函数指针*/
pop_arr(marr, 10, random);
for (int i = 0; i < 10; i++) {
printf("%d\t", marr[i]);
}
printf("\n");
return 0;
}
结果:
41 18467 6334 26500 19169 15724 11478 29358 26962 24464