C语言指针入门

指针是C语言中的一大难题,这篇文章将带大家简单了解运用指针。

目录
1. 指针是什么
2. 指针和指针类型
3. 野指针
4. 指针运算
5. 指针和数组
6. 二级指针
7. 指针数组
8.总结
1.指针是什么
指针理解的2个要点:
1. 指针是内存中一个最小单元的编号,也就是地址。
2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。
    首先我们要了解一个名词,叫作内存空间,我们在写C语言程序的时候,创建的变量、数组等都要在内存上开辟空间。而为了方便管理内存空间,我们将内存分为很多内存单元,每个内存单元都有一个唯一的编号,这个编号也被称为地址。
    所以我们可以这样理解:编号==地址==指针,也就是说指针就是地址,口语中说的指针通常指的是指针变量。
    
我们可以通过&(取地址操作符)取出变量的内存起始地址,把地址可以存放到一个变量中,这个
变量就是指针变量,我们写一个例子进行理解:
int main()
{
	int a = 10;//在内存中开辟一块空间
	int* p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
	            //这里的int表示a的类型是整型、这里的*表示p是指针。
	            //a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量中,p就是 
                //一个之指针变量。
	return 0;
}

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

那下面又来了两个问题:1.一个小的单元到底是多大?2.如何编址?

首先我们回答第一个问题,一个小的单元是1个字节。

下面我们看二个问题,先要了解原理:

对于 32 位的机器,假设有 32 根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压和低电平(低电压)就是(1 或者 0),这是因为电信号转化为了数字信号,所以32根地址线产生的地址就会从00000000000000000000000000000000——11111111111111111111111111111111,共有2的32次方个地址,每个地址标识一个字节,那一共会给
2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G 的空间进行编址。同样的方法,我们可以计算出64位机器可以编址8个字节的空间。
指针的大小在32 位平台是 4 个字节,在 64 位平台是 8 个字节。(指针类型不同,但是大小都一样,)
2. 指针和指针类型
我们都知道,变量有不同的类型:整型、浮点型等。指针也是有类型的。
1.指针类型决定了指针解引用操作的权限。
    int * 的指针解引用访问4个字节、char * 的指针解引用访问一个字节。我们举个例子:
char* pc = NULL;//存放char类型变量的字节,存放的数据大小为1字节
int* pi = NULL;//存放int类型变量的字节,存放的数据大小为4字节
float* pf = NULL;//存放float类型变量的字节,存放的数据大小为4字节
指针类型可以决定指针解引用的时候访问多少个字节(指针的权限)。
2.指针的类型决定了指针向前或者向后走一步有多大(距离)
我们写一个代码来看一下地址:

我们发现pa和pa+1的地址相差4个字节,pc和pc+1的地址却只相差1个字节,也就是说整型;指针+1跳过4个字节,而字符指针+1只跳过1个字节,我们得到结论:指针的类型决定了指针向前或者向后走一步有多大(距离),跳过的是n(+n)* sizeof(type)个字节。

 3. 野指针 

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

    野指针的成因有两个:1.指针未初始化  2.指针越界访问

1. 指针未初始化

#include <stdio.h>
int main()
{ 
 int *p;//局部变量指针未初始化,默认为随机值
 *p = 20;
 return 0;
}

    这就是一个典型的指针未初始化,它未初始化局部变量“p”,我们定义指针的时候: type * p;这里*说明p是指针变量,type说明p指向的对象的类型,同时也代表p解引用的时候访问的大小是sizeof(type)。但是我们必须在定义的时候给他右边一个确切的要取出的地址,否则就会导致编译器报错。

2.指针越界访问

指针的越界访问是很常见的错误,也有几种错误类型,我们举例子来说明一下:

这个代码运行的时候,就会出现程序报错问题,这就是因为:arr的下标只从0—9,而我们在访问下标的时候,给到范围全达到了i<=11,这就导致i=10和i=11的时候,是找不到数组的,所以就会出现指针越界访问,从而报错。

下一个例子:

     这个代码我们看的第一眼像是对的,其实它是有问题的代码,a的空间是进入函数创建的,那么它出函数返还给操作系统的时候,它的空间就已经没有了,但是它的地址传出来了,所以主函数其实运行的时候就已经越界调用访问了test() . 所以虽然运行成功了,但是这个代码其实是有问题的,p还是一个野指针。

 那我们如何避免野指针呢?我们需要注意以下几点:   

1. 指针初始化

2. 小心指针越界
3. 指针指向空间释放,及时置 NULL
int * ptr=NULL;这里ptr是一个空指针,没有任何有效的空间,这个指针不能直接使用。
4. 避免返回局部变量的地址
避免如指针越界例二的问题。
5. 指针使用之前检查有效性
4. 指针运算
1.指针+-整数
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
     *vp++ = 0;
}

这里也可以写成:arr[i] == *(arr+i) == *(i+arr) == i[arr]

2.指针-指针

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

这也是有前提条件的:指针和指针(两个指针)指向同一块空间。

指针-指针得到的数值的绝对值是指针之前的元素个数。

3.指针的关系运算

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

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

有的人会简化以上代码:

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

    其实这样子写是不符合标准规定的,标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。所以如果这样子写,会导致出现指针指向第一个元素之前的那个元素位置的指针进行对比,会出现指针越界。

实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证 它可行。

5. 指针和数组

    很多人可能会混淆数组和指针。

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

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

    但是指针和数组也是有很多联系的:数组中,数组名其实是数组首元素的地址,数组名==地址==指针,当我们知道数组首元素的地址的时候,因为数组又是连续存放的,所以通过指针就可以遍历访问数组,数组是可以通过指针来访问的。

我们看一个例子:

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

所以我们也可以直接通过指针来访问数组:

 6. 二级指针

指针变量也有变量,是变量就有地址,那指针变量的地址存放在哪里?

int a = 10;
int* p = &a;//p是一级指针,指针变量也是变量,变量是在内存中开辟空间的,是变量就有地址
int** pp = &p;//pp是二级指针,二级指针变量就是用来存放一级指针变量的地址

 二级指针了解即可,一般用途少。

7. 指针数组

指针数组是指针还是数组?
答案:是数组。是存放指针的数组。
数组有很多种:整型数组——存放整型的数组、字符数组——存放字符的数组、指针数组——那肯定就是存放指针的数组了。
比如arr是一个数组,他有五个元素,每个元素是一个整型指针,那它就是一个指针数组。

    这就是指针数组的一个例子,其中parr[i][j],其实就是下标为i的那个数组,下标为j的数字,这也是模拟二级数组。 

8.总结

    综上所述,我们已经基本学习完了关于指针的基础知识,希望大家能学习透彻,进而学习更深层次的指针教学。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值