学习笔记:C语言指针的学习——初学指针

一、初始指针

内存是电脑上特别重要的东西,计算机中所有的程序的运行都是在内存中进行的。

所以为了有效的使用内存,就把内存划分为一个个小的内存单元,每个内存单元的大小是1字节。

为了能够有效访问到内存的每个单元,就给内存单元进行了编号。这些编号被称为该内存单元的地址。

指针是什么?

在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,该地址指向该变量单元。因此,将地址形象化的称为:指针。意思是,通过它能找到以它为地址的内存单元。
 

1、 编号如何产生?

(1) 电脑上有地址线,地址线一旦通电,就能产生电信号,把电信号转为数字信号,数字信号产生的二进制序列(1或0),把数字信号作为二进制的编号,可作为内存单元的一个编号,即内存单元的地址。十六进制显得方便。

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

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

00000000 0000000 00000000 00000000

这里就有二的三十二次方个地址。

每个地址标识一个字节,那我们就可以给(2^32byte==2^32/1024kb==2^32/1024/1024mb==2^32/1024/1024/1024gb==4gb)4G的空闲进行编址。

同样的方法,64位机器,如果给64根地址线,能编址多大空间,自己计算。

这里我们明白:

1) 在32位机器上,地址是32个0或1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。

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

2、 一个内存单元是多大?

一个字节比较合适。

#include <stdio.h>
int main()
{
	int a =10;
	int *p = &a;//指针是用来存放地址的,p为指针变量 
	return 0;
}

P里面存的是地址,地址也叫指针。p变量是用来存放地址的,所以叫它指针变量,类型是int*。

存放地址的,就称为指针变量,而这个指针变量存放的又是地址。

指针就是地址,地址就是指针。

指针其实就是个变量,变量里面存放的是内存单元的地址,也就可以说:指针就是地址。(存放在指针中的值都被当成地址处理)
 

#include <stdio.h>
int main()
{
	int a =10;//在内存中开辟一个空间 
	int *p = &a;//我们对变量a,取出它的地址,可以使用&操作符
	//	将a的地址存放在p的变量中,指针是用来存放地址的,p为指针变量 
	return 0;
}

总结:

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

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

二、指针和指针的类型

       指针的类型和意义

我们来看一下,各种指针类型的大小:

#include<stdio.h>
#define _CRT_SECURE_NO_WARNINGS
int main()
{
	printf("%d\n",sizeof(char*));
	printf("%d\n",sizeof(short*));
	printf("%d\n",sizeof(int*));
	printf("%d\n",sizeof(double*));
	
	return 0;
}

运行结果:8 8 8 8 

当前默认是64位平台,所以结果都是8个字节。

1)指针的解引用

①问题抛出

❓ 问:它们指针大小都是4个字节,那为什么要区分?

那指针类型究竟有什么样的意义?

先定义一个整型变量a。

int a = 0x11223344;

将十六进制数0x11223344存入a中。

可以放进去吗?

可以的哦,两个十六进制位占一个字节(1个十六进制是4个二进制位,2个十六进制位就是8个二进制位,即1个字节)。

这样的话,11占一个字节,22占一个字节,33占一个字节,44占一个字节。总共4个字节。

a是整型变量,4个字节的空间。所以是可以存放的。

再将a的地址取出来(&a)。

放在变量pa里面,pa的是指针类型的变量,即int*类型。

如下:

int* pa=&a;

既然在探讨指针类型,那么我们将a的地址放入char*类型的变量pc里面行吗?

如下:

char* pc=&a;

pa和pc都能存放好a的地址吗?

来输出看一下:

int* pa=&a;//用int类型指针来存放a的地址 
printf("%p\n",pa);
char* pc = &a;//用char类型指针来存放a的地址 
printf("%p\n",pc);

运行结果:    000000000022FE44          000000000022FE44(分开运行)

由上图可见,两次输出地址一样。

可见,无论是什么类型的,都能存好a的地址。

为啥都能存放?因为pa和pc都是指针变量,都是4字节(32平台)/8字节(64平台)大小。

类型不匹配,只会报警告,类型不兼容。但还是可以存放进去的。

既然无论什么类型都能存放,那指针类型还有什么意义了呢?

一起往下继续探讨吧!

②探讨

接下来,我们分别看一下不同类型存储a地址的内存。

<1>当我们用Int类型来存储时

现在我们将a变量的地址存入一个整型指针pa中。

当用来存储的变量的类型发生变化,我们解引用操作的时候,结果不一样。

以上结果我们看到,用Int类型来存储时,当进行数据改变,原来的都改变了;而用char类型来存储时,只改变了前两个数(1个字节)。

🍰 指针在存储数据的时候,类型无差,都能够存放。但当我们对它进行解引用操作的时候,整型指针可以改4个字节;而字符指针,确实可以从指针访问相应空间,但*pc去访问空间的时候,只能改1个字节

③总结

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

不同类型的指针能够访问的空间大小不同:

  int* p;    p能够访问4个字节
  char*  p;    p能够访问1个字节
  double*  p;   p能够访问8个字节

那么学这个类型有什么意义?

指针类型既然决定了指针变量解引用能够一次访问几个字节,那当我们给指针赋值的时候,应该赋一个合理的指针。

比如我们希望从这个位置向后访问一个字节,那我们应该把它交给一个char指针;我们希望一次访问两个字节,那我们应该把它交给一个short类型的指针。

2)指针+整数

上面我们讨论了第一个意义,下面来讨论第二个意义。

如下代码:

int a=0x11223344;
int* pa=&a;
char* pc=&a;
printf("%p\n",pa);
printf("%p\n",pa+1);

printf("%p\n",pc);
printf("%p\n",pc+1);

看一下最终结果:

000000000022FE44和000000000022FE48

000000000022FE47和000000000022FE48

我们可以看到,当指针类型是int类型时,pa+1向后访问了4个字节;

而指针类型是char类型时,pc+1向后访问了1个字节。

即指针类型决定了指针加一向后跳几个字节,指针走一步走多远(步长)。

int* p;    p+1  -->  4
char* p;   p+1  -->  1 
double* p;  p+1  -->  8

❓ 指针向前向后一次走多大?

向后走的是指针所指向对象的类型的大小,整型指针向后加的是4,因为向后加一是跳过一个整型(4个字节);字符指针向后加一加的是1个字节,因为向后跳的是1个字符。

3)总结

🍰 总结

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

  • 指针的类型决

4)举例

那我们知道指针类型的意义有什么用?

我们来举个例子:定了指针向前或者向后走一步走多大(距离),单位是字节。

将arr数组的地址拿出来,赋值给指针变量p:

int* p=arr;	//arr为数组名,即首元素地址

🚗 需求:把arr数组里面的元素全部改成1。

<1> 用int类型的指针

改变数组第一个元素:*(p+0)=1

p指针指向数组第一个元素地址,解引用之后就是第一个元素,再将1赋值给它即可。

改变数组第二个元素:*(p+1)=1

p指针向后移动一位,指向数组第二个元素地址,解引用之后就是第二个元素,再将1赋值给它。

后面的规律就找到了。

改变数组里面元素:*(p+i)=1。(i=0,1,…)
 

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

运行结果:1 1 1 1 1 1 1 1 1

<2> 用char类型的指针

刚才我们使用了int类型的指针,那么使用char类型的话,会怎么样呢?

int arr[10] = { 0 }; 
char* p=arr;//arr为数组名,即首元素地址 
int i = 0; 
for (i=0;i<10;i++) { 
   *(p + i) = 1; 
} 

对于*(p+1),指针向后偏移了一个字节。

循环10次,就访问了10个字节。

可数组里面有40个字节!

一个整型4个字节,按照逻辑,只会初始化两个半整型的空间。

4)介绍reinterpret_cast(强制类型转换符)的使用

初学指针我们可能会遇到如下代码:

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

但是有的vs版本输入这段代码会报错,运行不了,报错的原因是:无法从int*转化到char*。所以这时候就需要我们将int类型强制转化为char类型,那我们就可以用reinterpret_cast(强制类型转换符)。

所以将以上代码修改为如下代码段:

#include<stdio.h>
int main()
{
 int a = 0x11223344;
 int* pa = &a;
 char* pc = reinterpret_cast<char*>(&a);
 printf("%p\n",pa);
 printf("%p\n",pc);
 
 return 0;
}

reinterpret_cast的用法:

reinterpret_cast<type-name>(expression)

reinterpret_cast后面的<>中的type-name类型必须是一个指针、引用、算术类型、函数指针或者指针成员,它可以把一个指针类型转换成一个整数,也可以把一个整数转换成一个指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值