C指针知识点

指针是什么?

指针就是内存地址,用来存放内存地址的变量。和其它变量和常量一样,必须在使用之前,对其进行声明。

指针变量声明的一般形式:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值