初识指针 不容错过

目录

1. 指针定义

2. 指针类型

3. 野指针

4. 指针运算

5. 指针和数组

6. 二级指针

7. 指针数组


1. 指针

   1. 指针是内存中一个最小单元的编号,也就是地址
   2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
  指针就是地址,口语中说的指针通常指的是指针变量
  指针变量:存放地址的变量叫做指针变量。指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址,
  通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量。
     int *p = &a ;                      对变量 a ,取出它的地址,可以使用 & 操作符  
    p就是一个之指针变量,将 a 4 个字节的第一个字节的地址存放在 p 变量中

地址在32位平台是4个字节,在64位平台是8个字节 

指针的大小在32位平台是4个字节,在64位平台是8个字节 

 2.指针类型

先了解一下指针的定义方式是: type + * 指针名,,所以去掉指针名就是指针类型

例子指针类型
int*ptrint *
是为了存放 int   类型变量的地址。
解引用可以访问4个字节
char*ptrchar*
是为了存放 char 类型变量的地址。
解引用可以访问1个字节
double*pdouble*是为了存放 char 类型变量的地址解引用可以访问8个字节
int(*ptr)[3]int()[3]
int**ptrint **

注意:"指针的类型"和"指针所指向的类型"是两个概念

指针类型决定指针进行解引用的时候,能够访问空间大小

我们来看一个例子:指针 +- 整数
#include <stdio.h>
int main()
{
   int a=0X11223344;
   int * pa =&a;              存放 int   类型变量
   char * pc =&a;          存放 char   类型变量
    printf("%p\n",pa);
    printf("%p\n",pa+1);
    printf("%p\n",pc);
    printf("%p\n",pc+1);
return 0;
}

代码执行结果:

0095FB58

0094FB5C

0095FB58

0095FB59

观察代码运行结果,我们发现int类型增加了4,char类型增加了1

同理我们可以推出:double类型指针跳过8个字节

指针类型决定指针+1跳过几个字节(指针的步长)

解引用

这是对指针的一种应用。解引用是指:通过指针,找到对应的内存和内存中的数据。我们可以通过解引用访问或修改指针指向的内存内容。

​ 解引用后等价于指针指向的变量。

int main()

{
    int arr[10] ={0};

    char*p=arr;      每次只能访问一个字节

    // int *p=arr;      每次可以访问四个字节

    int i=0;

     for(i=0;i<10;i++}

    {  

       *(p+i)=1;         将内存内容改为10个1

    }  
return 0;

}

3.野指针:指向位置不可知的指针

造成原因有:指针未初始化(局部变量未初始化默认是随机值),越界访问
  • 避免方法:
  • 1. 指针初始化{不知道初始化什么值时用NULL(空指针)
  • 2. 小心指针越界
  • 4. 避免返回局部变量的地址
  • 5. 指针使用之前检查有效性

4.指针运算

  1. 指针+- 整数
  2. 指针-指针
  3. 指针的关系运算(比大小)

练习:利用指针打印一组数字 

#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	int* p = &arr[0];
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;

int*p=arr   与int *p=arr[0]   意义相同,访问数组名,即数组首位的地址,即arr[0]

或者也可以写为

#include<stdio.h>
int main()
{
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int i=0;
int sz=siseof(arr)/sizeof(arr[0]);
int *p=arr;
for(i=0;i<sz;i++}
{
printf("%d",*p);
p=p+1;
}
return 0;
}

同理,我们也可以跳着打印,只需要将p++(p=p+1)换为p+=2

我们也可以倒着打印,只需要将for循环改为for(i=sz;i>0;i--)

所以,指针+- 整数,只是将访问的位置向前或向后跳跃

指针-指针,指针是一个地址,那么地址-地址是个什么东西呢?我们可以验证一下

#include<stdio.h>

int main()

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

      printf("%d\n",&arr[9]-&arr[0]);  //把它打印出来看看

      return 0;

}

 

我们发现结果为9,恰好为9-0的值,这是不是巧合呢? 当然不是,指针-指针 得到的值就是两指针间元素个数(大地址-小地址,且在同一空间中)

说到元素个数,我们会想到strlen可以求字符串长度,那么我们可不可以利用指针-指针来实现这一目的?但愤然可以。

//用指针-指针实现strlen的作用(求字符串长度,即元素个数)
#include<stdio.h>
int my_strlen(char* str)
{
	char* start = str;
	char* end = str;
	while(*end != '\0')  //‘\0’是字符串结束的标志
	{
		end++;
	}
	return end - start;
}

int main()
{
	char arr[] = "hello world";
	int len = my_strlen(arr);
	printf("%d\n", len);
	printf("%d\n", strlen("hello world"));
	return 0;
}

结果果然相同

指针的关系运算

for ( vp = & values [ N_VALUES ]; vp > & values [ 0 ];)
{
    *-- vp = 0 ;
}
注意标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与
指向第一个元素之前的那个内存位置的指针进行比较。

5. 指针和数组

printf("%p\n",数组名);  这里的地址是首元素的地址,地址可以放在指针变量中

printf("%p",&数组名[0]);   与上边两者相同

&数组名,不是首元素的地址,数组名表示整个数组,&数组名 取出的是整个数组的地址

同理,sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小

#include<stdio.h>
int main()
{
int arr[10]={0};
int *p=arr;
int i=0;
int sz=sizeof(arr)/sizeof(arr[0]);
for(i=0;i<sz;i++)
{
 *p++=i+1;
}
p=arr;
for(i=0;i<sz;i++)
{
printf("%d",*p);
p++;
}
return 0;
}

当然也可以写为:

for ( i=0 ; i<sz ; i++)

{

   *(p+1) = i +1;

}

 指针非常灵活,只要找到一个地址,它后面的地址都可以访问

arr [ i ] 与  *(i+arr)等价,也可以写为*(i+arr), i [arr],但一般不用

6. 二级指针

例如:int * pa = &a;   一级指针

那么,int *  *p pa = &pa;  二级指针

第一个*表示类型 ,第二个*表示指针,ppa就是一个二级指针变量 ( 用来存放一级指针变量地址)

同理, int * * * p p paa =& p pa;     三级指针……

与一级指针相同,我们可以将二级指针解引用,可以用代码进行演示

//二级指针解引用
#include<stdio.h>
int main()
{
	int a = 5;
	int* pa = &a;
	int** ppa = &pa;
	**ppa = 27;
	printf("%d\n", **ppa);
	printf("%d\n", a);
	return 0;
}

证明二级指针可以解引用

 7. 指针数组(本质是数组)与 数组指针(本质是指针)

     指针数组:存放指针的数组

#include<stdio.h>

int main()

{

   int  a=10;

   int  b=20;

   int  c=30;

   int  d=40;

  // int *pa=&a;

  //int *pb=&b;

  //int *pc=&c;

  // int *pd=&d

//一个一个罗列很麻烦,它们都是指针,可以用一个数组来存放指针

//有整型数组(存放整形),字符数组(存放字符)也有指针数组(存放指针)

int *arr[4]={&a,&b,&c,&d};   //指针数组:存放指针的数组

   int i=0;

    for(i=0;i<4;i++)

{

    printf("%d ", *( arr[i] ) );

}

return 0;

}

练习:利用指针使用一维数组模拟二维数组

 如果每个元素都是指针,数组名表示首元素地址

int a[]={1,2,3,4};

int b[]={2,3,4,5};

int c[]={3,4,5,6};

int *arr[3] = {a,b,c};

#include<stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	int b[] = { 2,3,4,5 };
	int c[] = { 3,4,5,6 };
	int* arr[3] = { a,b,c };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

看着不爽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值