深入了解C语言指针

一、指针的概念

   1、地址: 在计算机中,为了方便管理内存,内存被划分为以字节为单位的内存空间,也就是说是一个内存单元,而每一个内存单元的大小是一个字节,为了方便找到每个内存单元,我们给他们都编了一个特定的编号,这里的编号就是他们的地址,而地址在c语言中被称为指针

    2、&:变量创建的本质就是在内存中开辟空间,所以说每个变量都有他们特定的地址,而我们对于的他们的地址的获取要使用&(取地址操作符)。如下面的代码,我们就可以获得变量a的地址。对于a来说,因为a是整形变量,在内存中占四个字节,而我们拿到的是a四个字节第一个字节的地址(地址较小的地址)。

#include "stdio.h"
int main()
{
 int a=0;
 printf("%d\n",&a);
}

     3、*:我们通过指针变量来存放变量的地址。这里的p就是指针变量,存放的a的地址,这里p的数据类型为int*,int表示p指向的对象为int类型,而*表示p为指针变量。*是解引用操作符/间接访问操作符,*p就是通过p中存放的地址,找到地址指向的空间(a)。

#include "stdio.h"
int main()
{
 int a=0;
 int* p=&a;
}

    4、指针变量:对于指针变量大小来说,因为指针变量是用来存放地址,指针变量大小取决于地址需要多大空间,不同环境下,存放地址的空间不同。

  (1)在x86环境中,地址都是32个0/1组成的二进制序列,也就是需要4个字节,所以此时指针变量大小为4个字节。

    (2)在x64环境中,地址都是64个0/1组成的二进制序列,也就是8个字节,所以此时指针变量大小为8个字节。

     指针变量大小都是4/8个字节,与类型无关,在相同环境中,大小都是相同的。

    指针变量的类型决定了指针+1/-1一次跳过几个字节,如int*类型指针变量+1,跳过4个字节,char*指针变量+1,跳过1个字节。

   5、const修饰指针:const修饰变量使变量不能被修改。

        当const放在*左边时候,说明可以改变p1的指向(p1),但不能改变p1指向的内容(*p1);

     而const在*右边时候,可以改变p2的指向的内容(*p2),但不能改变p2指向(p2)。.

const int* p1=&a1;
int* const p2=&a2;

二、指针的运算    

      1、指针+-整数

            指针变量加1,向后移动1*sizeof(指针变量指向元素的数据类型)字节。

            指针变量减1,向前移动1*sizeof(指针变量指向元素的数据类型)字节。

    2、指针-指针

           指针减指针的绝对值是指针之间元素的个数。

           如下面代码,ret的值就是指针之间元素的个数9。

int arr[0];
int* p1=&arr[1];
int* p2=&arr[2];
int ret=p2-p1;

三、野指针

    1、野指针就是指向的位置不可知、随机的,指向已被释放的内存空间的指针。

    2、野指针是由于指针未初始化、指针越界访问、指针指向的空间被释放导致。

    3、如何规避野指针

           对指针进行初始化。

           小心指针越界。

            指针变量不再使用,及时置NULL,指针使用前检查它的有效性。

            避免返回局部变量。

四、指针的使用

   1、传址调用

         这里我们通过交换两个数来展示传址调用的作用。

          

#include <stdio.h>
void Swap2(int*px, int*py)
{
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d b=%d\n", a, b);
Swap1(&a, &b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}

          这里交换两个数字只可以使用传址调用,而不可以使用传值调用,因为对于传值调用来说,形参的改变不影响实参,所以无法通过函数的传值调用来交换两个数字。

2、strlen的模拟实现

       strlen函数是测量字符串长度,会一直向后找\0字符,直到找到为止。根据定义我们可以写出my_strlen函数来模拟strlen函数,具体代码如下

#include "stdio.h"
size_t my_strlen(char* str)
{
	size_t count = 0;
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
	 
}
int main()
{
	char arr[] = "hello";
	size_t ret = my_strlen(arr);
	printf("%d\n", ret);
}

五、常规的指针变量

      1、二级指针变量

          指针变量就是用来存放变量的地址,而指针变量自身也有地址,只有我们就要使用二级指针变量来存储指针变量的地址。

        如下面代码所示,这里的pp就是二级指针变量,它存储的就是指针变量p的地址,同样和指针变量定义相似,int*表示它指向的是指针变量p的数据类型,第二个*表示pp是指针变量,同样还有三级指针int** *ppp=&pp;但三级指针以上就运用很少了。

    

int a=0;
int*p=&a;
int* *pp=&p

   2、字符指针变量  

        这里有两种类型:

        一种是存放字符的地址,如这里p就是存放ch的地址。

        另一种就是存放字符串的地址,而这里的pp是将字符串的首元素a的地址存放在p中。此时的*p就是a,并且此时字符串"abcdefgh"是常量字符串,不可以被更改。

char ch='a';
char*p=&ch;
char* pp="abcdefgh";

3、数组指针变量

      数组指针变量核心就是就是指针变量,是指向数组的指针,存放的数组的地址。

      定义如下:这里的定义我们类比整型指针变量的定义,int* p;int是p指向元素的数据类型,*说明p是指针变量,而数组指针变量也是指针变量,所以*p表明是指针变量,int是数组元素的类型,[10]表示数组元素个数,这里其实其实就是把int [10]看作是数组的数据类型来理解,这样就和指针变量的总体定义相符合。

int arr[10]={0};
int (*p) [10]=&arr

     同样,下面代码同理。

char ch[8]={0};
char (*p2) [8]=&ch;

4、函数指针变量

     函数指针变量也是指针变量,指向函数的指针,存放的是函数的地址。

     特别的是,对于函数来说,&函数名和函数名都表示函数的地址。

      这里对于函数指针变量定义,*p表示p是指针变量,int表示函数返回值类型为int,(int x,int 有),表示指向函数的参数类型以及个数。

int Add(int x,int y)
{
 return x+y;
}
int main()
{
 int(*p)(int x,int y)=&Add;
}

     这里通过p(3,4)来实现函数的使用。

六、指针数组

1、指针数组

     指针数组就是存放指针的数组,与整型数组定义相似就是存放元素的类型 数组名[数组元素个数]。指针数组每个元素都是地址。

      

int* arr[20];
char* p[5];

     指针数组可以用来模拟二维数组具体的代码如下:

        

#include "stdio.h"
int main()
{
int arr1[] = {1,2,3,4,5};
int arr2[] = {2,3,4,5,6};
int arr3[] = {3,4,5,6,7};
int* parr[3] = {arr1, arr2, arr3};
int i = 0;
int j = 0;
for(i=0; i<3; i++)
{
for(j=0; j<5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
return 0;
}

2、函数指针数组

       函数指针数组就是存放都是函数指针变量的数组

       int (*)  (int,int)是函数指针变量数据类型,所以具体定义如下:

int (* parr[4])(int x,int y)={Add,Sub,Mul,Div};

        其中的Add,Sub,Mul,Div都是函数的地址

        这里我们可以用函数指针数组去实现

                    计算器的简单应用

        具体代码如下:

int add(int x, int y)

{

  return x + y;

}

int sub(int x, int y)

{

  return x - y;

}

int mul(int x, int y)

{

  return x * y;

}

int div(int x, int y)

{

  return x / y;

}

int main()

{

  int x = 0;

  int y = 0;

  int count = 0;

  int ret = 0;

  int(*p[5])(int x, int y) = {0,add,sub,mul,div};

  do

  { 

    printf("*************\n");

    printf("****1.add****\n"); 

    printf("****2.sub****\n");

    printf("****3.mul****\n");

    printf("****4.div****\n");

    printf("****0.exit***\n");

    printf("*************\n");

    printf("请输入你的选择\n");

    scanf("%d", &count);

    if (count >= 1 && count <= 4)

    {

      scanf("%d %d", &x, &y);

      int ret=p[count](x, y);

      printf("%d\n", ret);

    }

    else if (count == 0)

    {

      printf("退出使用");

    }

    else

      printf("输入数字有误,请重新输入\n");

    } while (count);





   

}

   这里使用函数指针数组很大程度简化了代码。

   对于指针的讲解就告以段落,对于指针在c语言中有着举足轻重的地位,我们深入了解指针,熟练运用指针,之后博主会发布一些关于指针的笔试题来加深大家对指针的理解。如果大家认为博主写的还不错的话,可以点赞支持一下博主,谢谢大家支持哦。

  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值