C语言-指针详解

概念

指针是一种数据类型,同样支持赋值,+和-操作。指针的值代表了内存中的一个地址,一般可以通过指针间接的操作数据。

创建一个指针

例如,我们要创建一个指向int类型的指针,我们可以编写如下代码。

int val = 10;
int* ptr = &val;

在这里“ptr”就是指向“int”类型的指针,其值为变量“val”的地址。

使用指针操作数据

继续使用之前的代码,我们可以通过指针变量“ptr”读写变量“val”的值,具体操作方式如下。

// 通过指针读取变量的值
printf("%d\n",*ptr);
// 通过指针修改变量的值
*ptr = 20;
printf("%d\n",*ptr);

通过如上代码,我们就可以通过指针来获取和修改变量的值了。

理解指针

指针在本质上也是一种数据类型,只不过指针的值代表了内存中的一个地址,对于指针的定义,其形式为value_type* ptr其中value_type为指针所要指向的类型,ptr为指针变量。在这里,ptr的类型为value_type*。基于此理解,我们可以很容易的理解多级指针的概念。

多级指针

基于以上的理解,我们可以得知指针的定义为value_type* ptr。那么,当value_type本身就是指针类型时,使用这种形式定义的指针就是二级指针。例如以下代码。

typedef int* IntPtr;
IntPtr* ptr;

在这里,我们定义了新的数据类型IntPtr,其本质上是int*,然后我们定义了一个指针ptr,从指针的定义方式上可以得出,变量ptr指向的变量其类型应该是IntPtr,而IntPtr又是int*类型的别名,所以我们定义出了一个指向int*类型的指针ptr。该变量ptr就被称为int类型的二级指针。
由以上定义的方法我们可以很容易的推出更多级指针的定义方式。
当然,以上代码只是为了方便理解而编写的,在应用中,对于二级指针或多级指针,我们有更简单的定义方式,示例代码如下。

int** ptr;

这里的变量ptr与之前定义的ptr是完全等价的。

函数中使用指针

指针的用途是很多的,其为开发工作可以提供许多便利。一下就简单的使用几种常见的用法。

(1) 用于函数出参:众所周知,C语言的函数只能有一个返回值,但是有时候我们传出多个值,除了包装一个结构体返回多个值之外,我们还可以使用指针作为传出参数。示例代码如下。

void addAndSub(int n1, int n2, int* addRes, int* subRes)
{
	*addRes = n1 + n2;
	*subRes = n1 - n2;
}

以上代码将会计算n1与n2的和与差,然后通过addRes和subRes两个指针传出,以达到多返回值的目的。
(2) 用于修改传入参数:C语言里,所有的参数都是以值拷贝的形式传入函数的,在这个时候,不论我们对形参如何修改,都无法影响到函数外部的变量,此时我们就可以使用指针传入参数,从而可以在函数内部修改函数外的变量。示例代码如下。

void valMod10(int* val)
{
	*val %= 10;
}

如上函数,会将传入的参数val指向的整形值进行对10取余运算,并且修改其值。
(3) 传入大数据:在C语言中,参数都是以值拷贝的方式传入函数的(包括指针)。假设如下代码运行在32位平台

typedef struct
{
   int nums[256];
}MyStruct;

int numSum(MyStruct s)
{
   int sum = 0;
   for (int i = 0; i < 256; i++)
   {
       sum += s.nums[i];
   }
   return sum;
}

上述代码在运行时将会拷贝256个int(一般是32位)形数据的大小,有较大的系统开销,而如果我们使用指针改写这段代码。

typedef struct
{
   int nums[256];
}MyStruct;

int numSum(MyStruct* s)
{
   int sum = 0;
   for (int i = 0; i < 256; i++)
   {
       sum += s->nums[i];
   }
   return sum;
}

如上代码中,则只会拷贝一次指针,在32位平台下,指针的大小也为32位,如此简单的修改就可以减少256倍的拷贝开销。
(4) 作为函数参数以接收数组:在C语言里,数组是无法作为形参的,即传入的数组参数都会自动转换为指针参数。例如如下代码。

int sum(int* nums, int n)
{
   int sum = 0;
   for (int i = 0; i < n; i++)
   {
       sum += nums[i];
   }
   return sum;
}

int main()
{
   int arr[10] ={ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
   int arrSum = sum(arr, 10);
   printf("%d", arrSum);
}

当然我们可以修改以上代码中的sum函数声明为int sum(int nums[], int n),两种声明是等价的。

使用指针操作数组

假设我们有一个需求,需要计算一个数组内所有元素的和,那么我们可以使用如下方法计算。

// 假设数组定义为int nums[256] = {1, 2, 4, ...};
// 求和方法一:不使用指针
int sum = 0;
for(int i = 0; i < 256; i++)
{
	sum += nums[i];
}
// 求和方法二:使用指针
int* ptr = nums;
int* pEnd = &nums[256];
do{
	sum += *ptr;
}while(++ptr < pEnd)

指针与数组

一般情况下,数组可以隐式的转换为指针,例如如下代码。

int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* p = arr;

但是需要注意,对于多维数组,我们并不能简单的使用多级指针来指向,例如如下代码。

int main()
{
    int arr[2][2] = { 1, 2, 3, 4 };
    int** ptr = arr;
    printf("%d", ptr[1][1]);
}

这段代码在运行时将会报错。
正确的写法如下所示。

int main()
{
    int arr[2][2] = { 1, 2, 3, 4 };
    int(*ptr)[2] = arr;
    printf("%d", ptr[1][1]);
}

值得注意的,在作为函数参数时,数组总是会被转换为指针,所以我们无法在函数内直接获取数组的大小,所以在编写代码时,如果需要使用数组作为参数,一定要再添加一个参数指明数组的长度。

函数指针

在C语言的库中,经常会遇到注册回调函数的操作,我们注册的回调函数都会被库代码以函数指针的形式保存起来。
函数指针的定义形式如下:

RetType (*functionPtrName)(ParaType1, ParaType2 ...);

其中RetType为返回值类型,functionPtrName为函数指针的变量名,ParaType1, ParaType2 ...为各个参数的类型。
示例代码如下。

int sum(int a, int b)
{
    return a + b;
}

int sub(int a, int b)
{
    return a - b;
}

int main()
{
    int(*fun)(int, int) = sum;
    printf("%d", fun(1, 2));
    fun = &sub;
    printf("%d", fun(3, 2));
}

由上述代码可以看出函数指针有以下几个特点。

  1. 为函数指针赋值时,取地址运算符(&)可写可不写。在C语言里,针对函数指针,我们为其赋值时不论是否使用取地址运算符其都能正确运行。
  2. 函数指针定义时的返回值类型,形参列表必须与原函数完全一致。

特别的,在C语言中如果我们定义的函数指针未指明参数列表,则代表可以接受任意个参数。示例代码如下。

int sum(int a, int b, int c)
{
    return a + b + c;
}

int sub(int a, int b)
{
    return a - b;
}

int main()
{
    int(*fun)();	// 未指定形参列表的函数指针
    fun = sum;		// 可以指向三个参数的函数
    printf("%d", fun(1, 2, 3));
    fun = &sub;		// 也可以指向两个参数的函数
    printf("%d", fun(3, 2));
}

如果我们要定义一个不接受参数的函数指针,则应该使用如下所示的定义方式。

RetType(*fun)(void);

形参列表中的void指明了该函数指针不接受任何带有任何参数的函数。

以上代码均未经验证,如有错误请谅解

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值