首页 > 其他分享 >9.【C语言详解】指针

9.【C语言详解】指针

时间:2022-12-04 16:59:27浏览次数:49  
标签:arr 变量 int C语言 地址 详解 数组 指针

指针是什么

指针是什么?

指针理解的2个要点:

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

指针就是地址,指向某一块内存空间。

image-20220518225213164

image-20220518225220643

我们有一个变量在栈中被创建,如果我们想找到它有几种方式呢?

  1. 通过变量名去访问
  2. 通过地址访问

这一个个编号就是虚拟内存地址,相当于将内存分为以一个字节为基本单位的很多单元体,通过对这些单元体的编号,我们就可以在任何位置找到一个对应的编号。

像不像我们现实中的地址门牌号?

指针变量

我们可以通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量。

int main()
{
	int a = 10;//在内存栈中开辟一块空间
	int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
  	//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
	//中,p就是一个之指针变量。
	return 0;
}

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

那这里的问题是:

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

经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。

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

这样每个地址就可以用32个二进制位表示。每个二进制位有两种情况。

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

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

这里我们就明白:

  • 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以

  • 一个指针变量的大小就应该是4个字节。

  • 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。

    总结:

  • 指针是用来存放地址的,地址是唯一标示一块地址空间的。

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

指针和指针类型

先看看基本类型:

整形
int
char
short
long
longlong

浮点型
double
float

那么指针指向不同的类型,是不是就代表着指针类型的不同呢?

可以确定的说:当然。

如下代码:

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

p就是一个指针变量,指向一个int类型的数据。

根据a的类型的不同,p的类型也要做出相应的调正。

我们知道p就是一个指针变量,那它的类型是怎样的呢?

我们给指针变量相应的类型。

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 类型变量的地址。

指针可以根据指向不同的类型来分类,那么它们作用上有什么不同?

通常来说,想通过地址访问变量,不同变量的存储方式,大小都不同,那么指针去访问的时候的权限以及解引用访问方式是不是应该也不同。

int i;
double d;
//假如它们的首地址相同
int* p1 = &i;
double *p2 = &d;
p1 == p2;

int 和 double的类型长度不同,那么通过解引用方式取得 i 和 d的值的时候,所访问的字节长度也不同。

//演示实例
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;
}

输出:

image-20220518225230690

可以看到指针类型决定了指针加一或者减一时跳过几个字节。

指针的解引用

int main()
{
	int n = 0x11223344;
	char* pc = (char*)&n;
	int* pi = &n;
	*pc = 0; (1)
	*pi = 0; (2)
    return 0;
}

image-20220518225240905

image-20220518225249369

上图为(1)被执行

下图为(2)被执行

image-20220518225257294

image-20220518225304232

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

野指针

成因

  1. 指针未初始化
int main()
{
	int* p;
	int a = *p;
	return 0;
}

会报错:使用了未初始化的局部变量。

  1. 指针越界访问
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for (i = 0; i <= 11; i++)
	{		
		*(p++) = i;//当指针指向的范围超出数组arr的范围时,p就是野指针
	}
	return 0;
}

如何规避野指针的使用

  1. 指针初始化;
  2. 注意指针的越界;
  3. 指针使用后及时置NULL;
  4. 函数体中不应返回局部变量的地址;
  5. 检查指针的有效性;
#include <stdio.h>
int main()
{
  int *p = NULL;
  //....
  int a = 10;
  p = &a;
  if(p != NULL)
  {
    *p = 20;
  }
  return 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;
}

image-20220518225314458

数组名和数组首元素的地址的值是一样的。

image-20220518225320933

但是注意类型是不同的,只是在数值上相等。

我们既然可以知道数组元素的指针,那么自然可以实现对其元素地址的逐一访问。

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;
}

输出:

image-20220518225327076

可以看到&arr[i] == (arr + i )。

既然连地址都可以逐一访问了,那么地址上所存储的数据自然可以轻松获得:

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	int *p = arr; //指针存放数组首元素的地址
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i<sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

二级指针

指针是存储变量地址的变量,那么自然指针变量也是需要一块内存空间来存储它的值。

如果我们想得到指针变量的地址,只能对指针变量&操作。

如果我们也想找一个变量用来存储指针变量的值,那么这个变量的类型是什么?

这就是二级指针。

int main()
{
	int num = 10;
	int* pa = &num;
	int** ppa = &pa;
	return 0;
}

那么可以通过二级指针获取int变量的值吗?

是的,我们既然二级指针记录了一级指针的值,就可以取得一级指针值,而一级指针的值又是int变量的地址,层层递进解引用,便可以获取int变量的值。

*ppa = pa;
**ppa = *pa = num

指针数组

元素类型为整形集合的数组叫什么?

答案是整形数组,那么理所应当,元素类型为指针的数组就叫指针数组了。

我们创建一个整型数组或者字符数组如下:

int arr1[10];
char arr2[10];

数组名可以看作一个变量名,[10]代表这是个数组中有十个元素,那么剩下的就是元素类型。

我们创建一个有是个元素,每个元素为int*指针的数组:

我们创建一个int*变量是如何创建的?

  1. 确定变量名
  2. 确定变量的类型
  3. 在变量名前加上类型名

那么数组也是差不多的。

首先写出想要的数组名
arr;

arr是个数组,并且有十个元素,我们加上[10];
arr[10];

元素类型是什么? int*
int* arr[10];

整形数组在内存中的存储

image-20220518225336391 image-20220518225342798

指针数组呢?

image-20220518225349700

可以看到,每个元素都是一个指针,指向一块特定的内存空间。

思考:指针数组的元素可以是数组名吗?

答案是的,除了对数组名进行sizeof和&操作的两种情况外,其他时候的数组名,一律当作指针来使用。

标签:arr,变量,int,C语言,地址,详解,数组,指针
From: https://www.cnblogs.com/ncphoton/p/16950152.html

相关文章

  • 10.【C语言详解】结构体
    结构体的声明什么是结构结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。结构体的声明structtag{ member-list;}variable-list;......
  • 11.C语言实现【N子棋】
    C语言实现一个大家小时候都玩过的小游戏的进阶版本,不止是三子棋,可以根据玩家需要设定棋盘大小。的可读性,我将源码分为了三个部分,分别是源文件test.c、game.c、game.h。tes......
  • 16.【C语言进阶】动态内存管理
    为什么存在动态内存分配栈区上的内存开辟intval=20;//在栈空间上开辟四个字节chararr[10]={0};//在栈空间上开辟10个字节的连续空间这样直接在函数体中开辟内存......
  • 15.【C语言进阶】自定义类型
    结构体的声明常规的结构的声明太过简单常见,不再过多阐述。特殊声明在声明结构体的时候可以不完全的声明struct{ inti; doubled; charc;}x;struct{ inti;......
  • 13.【C语言进阶】数据的存储
    数据基本类型为什么会有不同的类型,这些类型有内存大小上的差异,那么他们还有什么差异呢?在内存中开辟空间的大小读取内存空间方式的差异 char//字符类型short//......
  • 14.【C语言进阶】指针
    简介指针的概念指针是个变量,用来存储地址。指针的大小只与是64位平台还是32位平台有关,与指针类型无关。指针类型决定了指针的解引用权限和读取方式。指针+-正数与指......
  • 19.C语言实现【通讯录】
    简单功能展示增加联系人功能。按照姓名排序功能。保存文件,重新启动重新加载功能。头文件contact.h//文件保存版#include<stdio.h>#include<string.h>#include<s......
  • 18.【C语言进阶】程序的编译
    程序的翻译环境和执行环境在ANSIC的任何一种实现中,存在两个不同的环境。第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。第2种是执行环境,它用于实际执行......
  • 初识C语言(结构)
    前言:C语言是一种结构化的程序设计语言。对于新手来说要想学好一门编程语言,除了要了解编程的相关知识,更要了解其结构。“万层高楼平地起”结构就是地基中到比较重要的一环,所......
  • c语言第一个程序及编译运行过程中问题相关说明
    #define_CRT_SECURE_NO_WARNINGS1#include<stdio.h>intmain(){printf("helloworld");return0;}1.当打开visualstudio,创建新项目后将如下所示页面不小心关闭则......