指针初阶讲解

目录

指针是什么?

指针和指针类型

指针+-整数

指针的解引用

野指针

指针运算

指针+-整数

指针-指针

指针的关系运算

指针和数组

二级指针

指针数组


指针是什么?

指针理解的2个要点:

  1. 指针是内存中一个最小单元的编号,也就是地址;
  2. 平时口语中说的指针,通常指的指针变量,是用来存放内存地址的变量;

总结:指针就是地址,口语中的说的指针通常就是指针变量;

每个内存都有一个唯一的编号,这个编号也被称为地址,地址在C语言中成为指针;

每个内存单元都有一个唯一的地址来标识;

编号==地址==指针;

内存:

写C语言程序的时候,创建的变量、数组等都要在内存上开辟空间;

内存被划分成一个个的内存单元,每个内存单元的大小是1个字节;

指针变量

地址如果要存储的话,存放在指针变量中;

我们可以通过&(取地址操作符)取出变量的内存起始地址,把地址可以存放到一个变量中,这个
变量就是指针变量;
代码:
#include <stdio.h>
int main()
{
 int a = 10;//在内存中开辟一块空间
 int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
    //a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量中,p就是一个之指针变量。
 return 0;
}

总结:指针变量,用来存放地址的变量(存放在指针中的值都被当成地址处理);

  • 一个小的单元到底是多大?(1个字节);
  • 如何编制?

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是(1或者0);

那么32根地址线产生的地址就会是:

0000~0000(32位);

0000~0001(32位);

0000~0011(32位);

1111~1111(32位);

这里就有2的32次方个地址;

每个地址标识一个字节,那我们就可以给(2^32Byte==2^32/1024KB==2^31/1024/1024MB==2^32/1024/1024/1024GB==4GB)4G的空间进行编址;

这样我能就明白:

  • 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节(一个字节就是一个ASCll码,一个ASCll码就是对应的一个8位2进制表示的2进制数字);
  •  那如果在64位机器上,如果右64个地址线,那一个指针变量的大小就是8个字节,才能存放一个地址;

 总结:

  • 指针变量是用来存放地址的;
  • 指针的大小在32位平台是4个字节,在64位平台是8个字节;

指针和指针类型

变量有不同的类型,整形,浮点型等,指针同样也是;

代码:

int num = 10;
p = &num;
&num num 的地址)保存到 p 中, p 就是一个指针变量;
char* 类型的指针是为了存放 char 类型变量的地址。
short* 类型的指针是为了存放 short 类型变量的地址。
int* 类型的指针是为了存放 int 类型变量的地址。

0x开头的是16进制数字;

指针+-整数

代码:

#include<stdio.h>
int main()
{
 int n = 10;
 char *pc = (char*)&n;
 int *pi = &n;
 
 printf("%p\n", &n);
 printf("%p\n", pc);
 printf("%p\n", pc+1);
 printf("%p\n", pi);
 printf("%p\n", pi+1);
 return 0;
}

指针类型是有意义的;

指针类型决定了指针+1/-1跳过几个字节:

  • int * 的指针+1,跳过4个字节;
  • char * 的指针+1,跳过1个字节;
  • short * 的指针+1,跳过2个字节;
  • double * 的指针+1,跳过8个字节;

指针的解引用

代码;

#include <stdio.h>
int main()
{
 int n = 0x11223344;
 char *pc = (char *)&n;
 int *pi = &n;
 *pc = 0;   
 *pi = 0;  
 return 0;
}

通过*pc和*pi就可得到n;

指针类型是有意义的;

指针类型决定了指针进行解引用操作的时候,访问了几个字节(指针的权限);

  • int * 的指针解引用访问4个字节;
  • char * 的指针解引用访问1个字节;

野指针

局部变量不初始化的时候,内容是随机值;

概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的);

如何规范避免野指针:

  • 指针初始化——明确知道指针应该初始化为谁的地址,就直接初始化;不知道指针初始化为什么值,暂时初始化为NULL(!=NULL)NULL就是0;
#include <stdio.h>
int main()
{ 
 int *p;//局部变量指针未初始化,默认为随机值
    *p = 20;
 return 0;
}
  • 小心指针越界;
#include <stdio.h>
int main()
{
    int arr[10] = {0};
    int *p = arr;
    int i = 0;
    for(i=0; i<=11; i++)
   {
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
   }
    return 0;
}
  • 指针指向的空间释放,及时设置NULL;
  • 避免返回局部变量的地址;
  • 指针使用之前检查有效性
#include <stdio.h>
int main()
{
    int *p = NULL;
    //....
    int a = 10;
    p = &a;
    if(p != NULL)
   {
        *p = 20;
   }
    return 0;
}

指针运算

  • 指针+- 整数
  • 指针-指针
  • 指针的关系运算

指针+-整数

#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
     *vp++ = 0;
}

指针-指针

int my_strlen(char *s)
{
       char *p = s;
       while(*p != '\0' )
              p++;
       return p-s;
}

指针和指针相减的前提是:两个指针都指向了同一块空间;

指针-指针差值得到的绝对值:是指针和指针之间的元素个数;

指针的关系运算

地址是有大小的,指针的关系运算就是比较指针的大小;

代码:

for(vp = &values[N_VALUES]; vp > &values[0];)
{
    *--vp = 0;
}

简化:

for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
    *vp = 0;
}
绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证
它可行。
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位指针比较,但是不允许与指向
第一个元素之前的那个内存位置的指针进行比较;

指针和数组

#include <stdio.h>
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);
    return 0;
}

指针变量就是指针变量,不是数组,指针变量的大小是4/8个字节,专门用来存放地址的;

数组就是数组,不是指针,数组是一块连续的空间,可以存放1个或者多类型相同的数据;

联系:

数组中,数组名其实就是首元素的地址,数组名==地址==指针;

当我们知道数组首元素的地址的时候,因为数组又是连续存放的,所以通过指针就可以遍历访问数组,数组可以通过指针来访问,通过指针可以访问一个数组的元素;

数组名是首元素的地址,但是有两个例外:

  1. sizeof(数组名),数组名单独存放在sizeof内部,数组名表示整个数组,计算的是数组的大小,单位是字节;
  2. &(数组名),数组名表示整个数组的地址,数组的地址和数组首元素的地址,值是一样的,但是类型和意义是不一样的;
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int *p = arr;//p存放的是数组首元素的地址
既然可以把数组名当成地址存放到一个指针中,我们使用指针来访问一个就成为可能。
代码:
#include <stdio.h>
int main()
{
    int arr[] = {1,2,3,4,5,6,7,8,9,0};
    int *p = arr; //指针存放数组首元素的地址
    int sz = sizeof(arr)/sizeof(arr[0]);
    for(i=0; i<sz; i++)
   {
        printf("&arr[%d] = %p   <====> p+%d = %p\n", i, &arr[i], i, p+i);
   }
    return 0;
}

所以 p+i 其实计算的是数组 arr 下标为 i 的地址。
那我们就可以直接通过指针来访问数组。
代码:
#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	//使用指针打印数组的内同
	int* p = arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d", *(p + i));
		//p指向的是数组首元素
		//p+i是数组中下标为i的元素的地址
		//p+i起始时跳过了i*sizeof(int)个字节
	}
}

二级指针

指针变量也是变量,是变量就有地址;
对于二级指针的运算有:
  • *ppa 通过对ppa中的地址进行解引用,这样找到的是 pa *ppa 其实访问的就是 pa 
int b = 20;
*ppa = &b;//等价于 pa = &b;
  • **ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;

指针数组

字符数组——存放字符的数组;

整型数组——存放整型的数组;

指针数组——存放指针(地址)的数组;

例如:

char* arr[5];//存放字符指针的数组
doulbe* arr2[6];存放字符指针的数组
int* arr3[7];//存放整型指针的数组

int arr1[5];
char arr2[6];

int* arr3[5];//arr3是一个数组,有五个元素,每个元素是一个整形指针

持续更新中.........
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值