C语言——指针详解——初阶篇

1.1指针是什么?

指针是内存中一个最小单元的编号,也就是地址;平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。
注解:
1、把内存划分为一个个的内存单元,这个内存单元的大小是一个字节
2、每个字节都给一个唯一的编号,这个编号我们称为地址,地址在C语言中也叫“指针”。
即“编号=地址=指针”

int main()
{
	int a = 10;//a是整型,占用4个字节的内存空间,每个字节都有对应的地址
	
	int* pa = &a;
	//&a--得到的是a的地址(指针),其实得到的是a所占内存中4个字节中第一个字节的地址。pa是指针变量,pa的类型是int*
	return 0;
}

指针变量:可以通过&(取地址操作符)取出变量的内存起始地址,把地址可以存放到一个变量中,这个变量就是指针变量。
指针变量——用来存放地址的变量。(存放在指针中的值都被当成地址处理)
如何编址?
经计算和权衡发现一个字节给一个对应的地址是比较合适的。
对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)或低电平(低电压)就是1(或者0);
那么32根地址线产生的地址就会是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001

11111111 11111111 11111111 11111111
就会有2的32次方个地址。
每个地址标识一个字节,就可以给4GB的空间进行编址。
于是可知:
1、在32位的机器上,地址是32个0或1组成的二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
2、在64位的机器上,地址是64个0或1组成的二进制序列,那地址就得用8个字节的空间来存储,所以一个指针变量的大小就应该是8个字节。
总结:

  • 指针变量是用来存放地址的,地址是唯一标示一个内存单元的
  • 指针的大小在32位平台(x86)是4个字节,在64位平台(x64)是8个字节

1.2指针和指针类型

指针类型有:

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

可知:指针的定义方式为:“ type + * ”。
其实:
“char* ”类型的指针是为了存放char 类型变量的地址。
“ short* ”类型的指针是为了存放short 类型变量的地址。
“ int* ”类型的指针是为了存放int 类型变量的地址。
“ long* ”类型的指针是为了存放long 类型变量的地址。
“ float* ”类型的指针是为了存放float 类型变量的地址。
“ double* ”类型的指针是为了存放double 类型变量的地址。
指针类型的两个意义:
(1)指针类型决定了,在解引用指针的时候能访问几个字节。
(2)指针类型决定了,指针进行+1、-1的时候,一步走多远
例证(1):
(一)当int类型的变量a的指针类型为int *类型时

在这里插入图片描述
(二)当int类型的变量a的指针类型为char *类型时
在这里插入图片描述
在这里插入图片描述
例证(1)结束。
例证(2):
在这里插入图片描述
例证(2)结束。
总结:指针的类型决定了指针向前或者向后走一步有多大(距离)。
【步长取决于类型大小,而指针大小为4个(或8个)字节】

1.3指针的解引用

指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
比如:“ char * ”的指针解引用就只能访问一个字节,而“ int * ” 指针的解引用能访问四个字节。
例:
在这里插入图片描述

1.4野指针

概念:野指针就是指针指向的位置是不可知的(随机的、不正确的,没有明确限制的)
(一)野指针的成因
1、指针未初始化
例:

int main()
{
int* p;
//错误原因:使用了未初始化的局部变量“p”	
//指针变量p是局部变量,它的值没有初始化,那么它的值此时是随机的(地址),没有明确的指向空间,(没有有效的指向)
*p = 20;
return 0;
}

2、指针越界访问
例:

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

3、指针指向的空间释放

int* test()
{
	int a = 10;//在函数内部定义a使用a
	return &a;
}//出函数后a就不在了,销毁了,但是把地址返回了放在p中了
//这个函数就是返回了局部变量的地址
int main()
{
	int* p = test();//test()调用完之后,还可以通过地址找到那一块儿空间,但a已经不在了,该块空间没有所属
	*p = 100;//解引用访问,修改值的操作就变得危险了
	//此时p就为野指针
	return 0;
}

如何规避野指针:
1、指针初始化
2、小心指针越界
3、指针指向空间释放,及时置NULL
4、避免返回局部变量的地址
5、指针使用之前检查有效性
例:

int main()
{
   int* p=NULL;
   if(p!=NULL)
   {
   }
   return 0;
}

1.5指针运算

  • 指针±整数
  • 指针-指针
  • 指针的关系运算
    1.5.1 指针±整数
    例一:
    在这里插入图片描述
    例二:
#define N_VALUES  5
float values[N_NALUES];
float *vp;
//指针+-整数:指针的关系运算
for(vp = &values[0]; vp < &values[N_VALUES];)
{
     *vp++ = 0;
}

在这里插入图片描述
1.5.2指针-指针
指针-指针——地址-地址
1、两个指针指向同一块空间,指针的类型是一致的
2、指针-指针得到的是指针和指针之间的元素个数(有正负之分)

在这里插入图片描述
得到的是元素个数。
推理:指针加整数等于指针,指针减指针等于整数
在这里插入图片描述
在这里插入图片描述
1.5.3指针的关系运算
“ >” “ < ” “ = ”

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

代码简化,可写为:

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

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

1.6 指针和数组

在这里插入图片描述
程序实例:
在这里插入图片描述

1.7二级指针

二级指针用来存放一级指针的地址
在这里插入图片描述

1.8指针数组

指针数组是数组,是存放指针的数组。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注:该二维数组内存空间不一定连续,因为arr1、arr2、arr3不连续,但arr1内的数据连续存放

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值