C语言基础(9)之指针(1)

目录

1. 指针的概念

1.1 内存

1.2 指针是什么        

1.3 指针变量的大小

2. 指针类型

2.1 指针类型的意义

2.2 指针类型意义的疑问

3. 野指针

3.1 野指针的概念

3.2 野指针的成因

3.3 如何规避野指针

4. 指针运算

4.1 指针 + 或 - 整数

4.2 指针 - 指针

4.3 指针 -  指针的用途


        在前面的文章中我详细的讲解了各种操作符的知识,不知道小伙伴们在了解完后有没有自己去练习呀!那么今天这篇文章小编就带大家来了解指针的相关知识。包括什么是指针、指针类型、指针运算等知识点。

1. 指针的概念

        在学习指针之前,我们首先得理解内存的概念,因为指针是用来访问内存的。

1.1 内存

        那么什么是内存呢?内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的。
        所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
        而为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址,且每一个内存单元都有一个唯一的编号。如下图:

1.2 指针是什么        

        那么指针到底是什么呢?在C语言中,把 这些编号 或 这些地址 也叫做 指针。因此 编号 == 地址 == 指针。

        但我们口头语中常说的指针,基本上都是指针变量。(指针变量  —>  变量  —>  存放地址的变量

        需要注意的是:我们在写C语言程序的时候,创建的变量、数组等都是创建在内存中的(即在内存中分配空间的),而每个内存单元都有地址,所以变量也是有地址的

1.3 指针变量的大小

        在谈及指针变量的大小之前,小编先来给大家讲讲 地址或者编号是如何产生的呢?  ——>      在计算机上,有地址线(物理的电线),通过产生高低电平的信号,转换为数字信号:1/0。在32位的机器中,上面就会有32根地址线;在64位的机器中,上面就会有64根地址线。

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

        如果在64位机器上,地址是64个0或者1组成二进制序列,那内存中就得用64个bit位的空间来存储,那一个指针变量的大小是8个字节,才能存放一个地址。

        大家可以这样记忆:地址可以类比为身份证,并不会因为身份的不同(存储不同的数据类型时)而改变。而指针变量的大小可以类比为身份证的位数,也不会因为一个人身份的改变(数据类型的不同)而改变大小。

总结:

        1. 指针是内存中一个最小单元的编号,也就是地址。

        2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。

        3. 指针变量是用来存放地址的,地址是唯一标识一个内存单元的。

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

2. 指针类型

        我们都知道,变量有不同的类型,整形,浮点型等。那指针有没有类型呢?答案当然是有的!

指针的类型:type* p


其中:
* 说明p是指向的 type类型 的指针变量


type说明:1. p指向的对象的类型
                  2. p解引用的时候访问的对象大小是sizeof(type)


例如:
char* pc = NULL;
short* ps = NULL;
int* pi = NULL;
char* 类型的指针是为了存放 char 类型变量的地址。
short* 类型的指针是为了存放 short 类型变量的地址。
int* 类型的指针是为了存放 int 类型变量的地址。

2.1 指针类型的意义

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

#include<stdio.h>
int main()
{
	char a = 'x';
	char* pc = &a;

	int i = 10;
	int* pi = &i;

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

	printf("\n");

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

	return 0;
}

        如上所示例子,当我们对不同类型的指针进行 +1 操作时,其在内存中跳过的字节数是不一样的,char类型的指针pc 进行 +1 操作只跳过了一个字节,而int型的指针进行 +1 操作跳过了4个字节。

        也就是说,指针类型决定了指针 +/- 1 操作时的步长,整型指针+1跳过4个字节,字符指针+1跳过1个字节。 +/- n 对于type* p 来说跳过的是:n*sizeof(type)这么多个字节。

int main()
{
	int n = 0x11223344;

	char* pc = (char*)&n;

	*pc = 0;

	printf("0x%x\n", n);

	return 0;
}

        在上述的这个例子中,"0x11223344" 是16进制的数字,我们将整型n的地址 强制类型转换 并赋值给了字符类型的指针pc,当指针pc解引用并修改值时,可以看出 n 的值并没有完全被修改,而是只改变了一个字节的值。

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

总结指针类型的意义:

        1. 指针的类型决定了指针向前或者向后走一步有多大(距离)
        2. 指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。

2.2 指针类型意义的疑问

        这里也许有一些小伙伴会对第二点有疑问:不是说指针不是4个字节就是8个字节吗?怎么这里又能根据类型来操作字节的个数了? 下面举例来说明:

        如上例子,我们前面说的指针不是4字节就是8字节指的是指针本身自己的大小,如上无论是字符指针pc还是整型指针pi,它们在64位的机器上大小都是8字节。

        而我们所说的指针解引用能操作几个字节,可以这么理解:对于整型指针pi来说,它解引用后,即 *pi 等同于 变量 i而变量 i 是整型类型的,整型(int)的大小是4个字节,因此变量 i 能操作4个字节,即 *pi也只能操作4个字节。

3. 野指针

3.1 野指针的概念

        何为野指针呢?野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

3.2 野指针的成因

        那为什么会有野指针呢?野指针是怎么来的呢?主要有以下三点原因!

                1. 指针未初始化。

        如上例子,当局部变量不初始化时,内容会是随机值,此时 p 就称为野指针。

                2. 指针越界访问。

        我们设定数组的长度为10,但当我们用指针访问数组时,访问时却超出了数组大小的范围。换句话说,当指针p指向的范围超出数组arr的范围时,p就是野指针。

                 3. 指针指向的空间释放 (这点小编会在后续讲解动态内存开辟的时候跟老铁们探讨,这里就不先进行讲解了)

3.3 如何规避野指针

既然有野指针,那么我们该如何规避野指针的生成呢?小编在这里给大家总结了几点:

                1. 指针初始化。
                        1.1 明确知道指针应该初始化为谁的地址,就直接初始化。
                        1.2 不知道初始化为什么值,暂时初始化为NULL。
                2. 小心指针越界。
                3. 指针指向的空间释放,及时置为NULL。
                4. 避免返回局部变量的地址。
                5. 指针使用之前检查有效性。

4. 指针运算

        指针运算又可分为:1. 指针 +或- 整数                2. 指针 - 指针。

4.1 指针 + 或 - 整数

        假定这样一个场景:我们设置一个长度大小为10的整型数组arr,要求不使用数组下标的方式访问数组,即不采用arr[0]、arr[1]...这种形式来访问数组。

        那该采用什么样的方式呢?毫无疑问肯定是 指针 + 或 - 整数 的方式。代码如下:

#include<stdio.h>

int main()
{
	int arr[10] = { 0 };
	
	int* p = &arr[0];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for ( i = 0; i < sz; i++)
	{
		*p = i;
		p++;
	}

	for (i = 0; i < sz; i++)
		printf("%d ", arr[i]);

	printf("\n\n");

	p = arr;
	for (i = 0; i < sz; i++)
		printf("%d ", *(p + i));  

	printf("\n");

	return 0;
}

        如结果所示,采用 指针 + 或 - 整数 的方式也能得到正确的结果。

        需要注意的是:

                1. arr是数组名,数组名是首元素的地址,所以可以把arr理解为指针,指向数组首元素,故可以写成p = arr。

                 2. 因为p = arr,所以arr[0] = *p = *(p+0) = p[0]。类推:若p = arr,则arr[i] = *(p+i) = p[i]。

                3. arr[i] == *(arr+i) == *(i+arr) == i[arr] (注:[]仅仅是个操作符)

4.2 指针 - 指针

        大家先来看看下面两个例子:

        上面这个例子说明指针 - 指针结果的绝对值是指针与指针之间的元素个数。

        而这第二个例子出错了,是因为数组ch和数组arr并没有执行同一块内存空间,所以两者之间不能相减。

总结:

        1. 指针 - 指针得到的数值的绝对值:是指针和指针之间的元素个数。
        2. 指针 - 指针运算的前提条件是:指针和指针指向了同一块空间。

4.3 指针 -  指针的用途

        我们都知道strlen库函数是用来求字符串的长度的。那当我们自己模拟实现strlen函数时,大多数的小伙伴可能都是设置一个int的变量count来统计字符个数,最终求得字符串的长度。

        下面小编就来教大家怎么用指针 - 指针的方式来模拟实现strlen函数,代码如下:

#include<stdio.h>

int my_strlen(char* s)
{
	char* start = s;

	while (*s) // *s != '\0'
	{
		s++;
	}

	return s - start;

}

int main()
{
	char ch[] = "abcdef";
	int len = my_strlen(ch);

	printf("%d\n", len);

	return 0;
}

        我们只要找到字符串的末尾的地址,再减去字符串的首地址,即可得到字符串的长度。

        好了,小编今天就先讲解到这里啦,各位老铁们我们下期再见吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值