C语言初阶——6指针

1. 指针是什么?

指针是指编程语言中的一个对象,利用地址,它的值指向存在电脑存储器中的另一个值。由于通过地址能找到所需的变量单元,即地址指向变量单元,所以称为“指针”。通过它能找到以它为地址的内存单元。

1.1 指针

  • 指针是个变量,存放内存单元的地址。
  • 存放在指针的值,被当做地址处理

1、那一个单元是多大?(一个字节)
2、如何编址?

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的是产生一个电信号正电/负电(1或者0)
那么32根地址线产生的地址就会是:

00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001

11111111 11111111 11111111 11111111

这里就有2的32次方个地址。
每个地址标识一个字节,那我们就可以给
(2^ 32Byte = 2^ 32/1024KB = 2 ^ 32/1024/1024MB = 2 ^ 32/1024/1024/1024GB = 4GB) 4G的空闲进行编址。

1、 指针是用来存放地址的,地址是唯一标示一块地址空间的。
2、指针的大小在32位平台是4个字节(一个字节8个比特位,4×8=32位),在64位平台是8个字节(同理8×8=64位)。

1.2 指针和指针类型

指针变量的类型:

char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;

这里可以看到,指针的定义方式是: type + * 。 其实: char* 类型的指针是为了存放 char 类型变量的地址。 short* 类型的指针是为了存放 short 类型变量的地址。 int* 类型的指针是为了存放int 类型变量的地址。

指针类型的意义是什么?
1、指针±整数
2、指针的解引用

  1. 指针加减整数
#include"stdio.h"


int main()
{
	int a = 10;
	int* b = &a;
	char* c = (char*)&a;
	printf("%p\n", b);
	printf("%p\n", c);
	printf("%p\n", b+1);
	printf("%p\n", c+1);

	return 0;
}

在这里插入图片描述

int*+1 地址从0x005BE928到0x005BE92C 走了4个字节
char*+1地址从0x005BE928到0x005BE929 走了1个字节
总结:指针的类型决定了指针向前或者向后走一步有多大(距离)

  1. 指针的解引用

总结:指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。 比如: char* 的指针解引用就只能访问一个字节,而int* 的指针的解引用就能访问四个字节。

2 野指针

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

2.1 野指针成因

1. 指针未初始化

#include"stdio.h"

int main()
{
	int* p;
	//printf("%p\n", p);
	*p = 1;
	printf("%p\n", p);

	return 0;
}



在这里插入图片描述
在这里插入图片描述

2. 指针越界访问

#include"stdio.h"

int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for (i = 1; i < 12; i++)
	{
		*(p++) = i;
		printf("%p\n", arr[i]);
	}

	return 0;
}

在这里插入图片描述

3. 指针指向的空间释放

2.2 如何规避野指针

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放及时置NULL
  4. 避免返回局部变量的地址
  5. 指针使用之前检查有效性
#include"stdio.h"
#include "stddef.h"



int main()
{
	int* p = NULL;//指针初始化
	printf("%p\n", p);
	int a = 10;
	p = &a;
	printf("%p\n", p);
	if (p != NULL)
	{
		*p = 20;
	}
	printf("%p\n", p);
	return 0;
}


在这里插入图片描述

3. 指针运算

3.1 指针± 整数

示例1

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

在这里插入图片描述
vp指针一个一个往后遍历。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

3.2 指针-指针

前提:两个指针要指向同一块空间
指针 - 指针的绝对值指的是两个指针之间的元素个数。

#include<stdio.h>
#include <stddef.h>
#define N_VALUES 5
int mystrlen(char *s)
{
	char* p = s;
	while (*p != '\0')
	{
		p++;
	}
	return p - s;
}
int main()
{
	char* q = "abcdef";
	char* q1 = "dbcdef";
	int ret = mystrlen(q);
	printf("%d\n", ret);

	return 0;
}

在这里插入图片描述

3.3 指针关系运算

示例1

#include<stdio.h>
int main()
{
	#define N_VALUES 5
	float values[N_VALUES];
	float* vp;
	for (vp = &values[N_VALUES]; vp > &values[0];) 
	//这里&values[5]我们取出了数组最后一个下标的紧跟着的下一位的地址,
	 // 但是我们没有进行访问操作所以不是数组越界。
	{
		*--vp = 0;
	}
	int i = 0;
	for (i = 0; i < N_VALUES; i++)
	{
		printf("%d ",values[i]);
	}
}

在这里插入图片描述
在这里插入图片描述

示例2 是对示例1的改进

#include<stdio.h>
int main()
{
#define N_VALUES 5
	float values[N_VALUES];
	float* vp;

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

	int i = 0;
	for (i = 0; i < N_VALUES; i++)
	{
		printf("%d ", values[i]);
	}

}

在这里插入图片描述

在这里插入图片描述

实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

4. 指针和数组

  1. 数组和指针的关系

(1)指针和数组是不同的对象
指针是一种变量,存放地址的,大小4/8字节的。
数组是一组相同类型元素的集合,是可以放多个元素的,大小是取决于元素个数和元素的类型的
(2)数组的数组名是数组首元素的地址,地址是可以放在指针变量中
可以通过指针访问数组

#include"stdio.h"


int main()
{

	int arr[10] = { 2,4,3,1,5,8,6,9,1 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	return 0;
}

在这里插入图片描述

结论:数组名表示的是数组首元素的地址。

  1. 示例
#include"stdio.h"


int main()
{

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

	int length = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < length; i++)
	{
		printf("&arr[%d] = %p   <======> p+%d = %p\n", i, &arr[i], i,p+i);
	}
	//printf("%p\n", p);
	//printf("%p\n", arr);
	//printf("%p\n", &arr[0]);
	return 0;
}

在这里插入图片描述

在这里插入图片描述

5 二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里? 这就是二级指针。
在这里插入图片描述

6 指针数组

指针数组是指针还是数组?
答案:是数组。是存放指针的数组。

  1. 整型数组、字符型数组
int arr1[5];
char arr2[6];

在这里插入图片描述
2.指针数组

int* arr3[5];

arr3是一个数组,有五个元素,每个元素是一个整形指针。

在这里插入图片描述

7. void*

  • void*不是"空指针",而是一种特殊的指针,这个指针只有地址,没有长度~
  • 针对void*不能 * 操作,也不能±整数,也不能指针相减~

8. const和指针

const 修饰指针变量

1.const放在*的左边,const修饰的是指针指向的内容,表示指针指向的内容,不能通过指针来改变了;但是指针变量本身可以修改const int* p = &num;*p = 20;//err
p = &n;//ok
2.const放在*的右边,const修饰的指针变量本身,表示指针变量本身的内容不能被修改,但是指针指向的内容,可以通过指针来改变。int* const p= &num;
*p = 20;//ok
p= &n;//err

```c
const int* p = &a;

在这里插入图片描述

int* const  p = &a;

在这里插入图片描述

const int* const p = &a;

在这里插入图片描述

9.习题

9.1习题1

#include <stdio.h>
int main()
{
  int arr[] = {1,2,3,4,5};
  short *p = (short*)arr;
  int i = 0;
  for(i=0; i<4; i++)
  {
    *(p+i) = 0;
  }
   
  for(i=0; i<5; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

在这里插入图片描述

9.2 写一个函数打印arr数组的内容,不使用数组下标,使用指针。

  • 方式1
#include<stdio.h>

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	int* p = arr;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

在这里插入图片描述

  • 方式2
#include<stdio.h>

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	int* p = arr;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *( i+p));
	}
	return 0;
}

在这里插入图片描述

  • 这里有一个很有意思的写法,没想到这样也可以打印数组。
    在这里插入图片描述在这里插入图片描述
#include<stdio.h>

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	int* p = arr;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", i[arr]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值