C语言--指针详解(上)--指针基本概念

C语言--指针详解(上)--指针基本概念

  • 1. 内存和地址
    • 1.1 内存
    • 1.2 地址
  • 2. 指针变量和地址
    • 2.1 取地址操作符(&)
    • 2.2 指针变量和解引⽤操作符(*)
    • 2.3 指针变量
    • 2.4 指针变量的大小
  • 3. 指针变量类型的意义
    • 3.1 指针的解引⽤
    • 3.2 指针+-整数
  • 4. const 修饰指针
  • 5. 指针运算
    • 5.1 指针 + - 整数
    • 5.2 指针 - 指针
  • 6. 野指针
    • 6.1 类型
    • 6.2 避免方法
  • 7. 总结

1. 内存和地址

1.1 内存

内存是计算机中用于存储数据和程序的设备。它是一个由许多存储单元组成的集合,每个存储单元都可以存储一个固定大小的数据。这些存储单元可以被计算机读取和写入,以便在程序运行时存储和检索数据。

内存就好比一家旅馆,里面的房间就好比存储空间,往内存里面存放数据就好比旅客入住,取出数据就好比旅客退房,当所有房间都住满的时候,内存也就存储满了。

在计算机操作中所做的每一步操作都是会产生大小不一的数据的,这些数据都会存储到内存中。

1.2 地址

当有客人来住旅馆的时候,如何准确的告诉该旅客应该去哪个房间?或者要寻找以为旅客时,如何知道他在哪一间房间中?答案是找房间号,只要找到了房间号,也就能精准找到需要找到的旅客。

计算机定位数据也是如此。

在计算机中,每个内存单元都有一个唯一的地址,用于标识该单元,也相当于每个房间有一个独无二的房间号。地址是一个数字,用于访问内存中的数据。计算机使用这些地址来读取和写入内存中的数据。也就是通过房间号来找到里面的人。

地址通常是一个整数,它表示内存中存储单元的位置。例如,在一个32位计算机上,每个地址可以表示2^ 32个不同的内存单元。相当于房间号由4位数字组成,如 0000 代表第一间房间,0001代表第二间房间,那么一共就可以代表 10000 (10 ^ 4)间房间,也就是代表有这么多空间可以用来存储数据。(计算机使用的是二进制表示法,因而 32 位只能表示 2 ^ 32个地址,生活中用的是十进制,所以4 位数字可以表示 10000个房间,即10 ^4个房间)。

当程序需要读取或写入内存中的数据时,它会使用地址来指示要读取或写入的内存单元。程序员可以使用编程语言中的指针来访问内存地址,以便直接读取或写入内存中的数据。指针是一个变量,它存储一个地址,可以用于访问内存中的数据

2. 指针变量和地址

2.1 取地址操作符(&)

在C语⾔中,创建变量其实就是向内存申请空间,⽐如下面这段代码:

#include 

int main()
{
 int a = 10;
 return 0;
}

C语言--指针详解(上)--指针基本概念_第1张图片
创建变量会消耗内存,它消耗的方式在于向内存中申请4个字节,⽤于存放整数10,其中每个字节都有地址,上图中4个字节的地址分别是:

1. 0x006FFD702 
2. 0x006FFD713 
3. 0x006FFD724 
4. 0x006FFD73

那我们如何能得到a的地址呢?
这⾥就得学习⼀个操作符(&) - - 取地址操作符,操作如下:

#include 
int main()
{	int a = 10;
    &a;//取出a的地址 
    printf("%p\n", &a);
    return 0;
}

C语言--指针详解(上)--指针基本概念_第2张图片
按照我画图的例⼦,会打印处理:006FFD70,&a取出的是a所占4个字节中地址较⼩的字节的地址。

虽然整型变量占⽤4个字节,我们只要知道了第⼀个字节地址,顺藤摸⽠访问到4个字节的数据也是可⾏的。

2.2 指针变量和解引⽤操作符(*)

2.3 指针变量

那我们通过取地址操作符(&)拿到的地址是⼀个数值,⽐如0x006FFD70,这个数值有时候也是需要存储起来,⽅便后期再使⽤的,那我们把这样的地址值存放在哪⾥呢?答案是:指针变量中。
比如:

#include 

int main()
{
 int a = 10;
 int* pa = &a;//取出a的地址并存储到指针变量pa中 
 
 return 0;
}

指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。

我们看到pa的类型是 int* ,我们该如何理解指针的类型呢?

int a = 10;
int * pa = &a;

这⾥pa左边写的是 int* , * 是在说明pa是指针变量,⽽前⾯的 int 是在说明pa指向的是整型(int)类型的对象。

那如果有⼀个char类型的变量ch,ch的地址,要放在什么类型的指针变量中呢?答案如下:

char ch = 'w';
char* pc = &ch;

指针解引用:
在现实⽣活中,我们使⽤地址要找到⼀个房间,在房间⾥可以拿去或者存放物品。

C语⾔中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)。

#include 
{
int main()
{
 int a = 100;
 int* pa = &a;
 *pa = 0;//此时a 变成了0;
 return 0;
}

上⾯代码中第7⾏就使⽤了解引⽤操作符, *pa 的意思就是通过pa中存放的地址,找到指向的空间,pa其实就是a变量了;所以pa = 0,这个操作符是把 a 改成了0.

这⾥如果⽬的就是把a改成0的话,写成 a = 0; 不就完了,为啥⾮要使⽤指针呢?

其实这⾥是把a的修改交给了pa来操作,这样对a的修改,就多了⼀种的途径,写代码就会更加灵活.

2.4 指针变量的大小

• 32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
• 64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
• 注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。

指针变量的大小可以用 sizeof 关键字来计算。如:

#include 
int main()
{
	int a = 1;
	int* p = &a;
	printf("%zd",sizeof(p));
	return 0;
}

代码会在屏幕上打印出指针变量的大小。

3. 指针变量类型的意义

3.1 指针的解引⽤

在解引用时,指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。

⽐如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。可以在编译器中进行调试查看。

3.2 指针±整数

先看⼀段代码,调试观察地址的变化。

#include 

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

代码运⾏的结果如下:
C语言--指针详解(上)--指针基本概念_第3张图片
我们可以看出, char类型的指针变量+1跳过1个字节,int类型的指针变量+1跳过了4个字节。
**结论:**指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。

4. const 修饰指针

const 修饰的是其后面的内容,保证其后面的内容不能够被修改

例如 const int n = 0,此时的 n 是不可修改的。

当const 修饰指针变量的时候,分为两种情况:

  1. const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本⾝的内容可变。

例如:int a=10; int b=1; const int* p = &a
或者int a=10; int b=1; int const* p=&a
此时p=&b是被允许的,但是*p = 2,这种通过对指针来改变指针指向的内容是不被允许的。

  1. const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不.能修改,但是指针指向的内容,可以通过指针改变。

例如:int a=10; int b=1; int* const p = &a
或者int a=10; int b=1; int* const p=&a
此时p=&b是不被允许的,但是*p = 2,这种通过对指针来改变指针指向的内容是被允许的。

5. 指针运算

指针的基本运算有三种,分别是:
•指针 + - 整数
•指针 - 指针
•指针的关系运算

5.1 指针 + - 整数

指针 + - 整数的结果是指针往后或往前跳过指针类型个数据。

例如:char* p; p + 1 表示往后跳过一个字节的地址,p - 1 表示往前跳一个字节地址
int* p;p + 1 表示往后跳过四个字节的地址,p - 1 表示往前跳过四个字节的地址
short* p; p + 1 表示往后跳过2个字节的地址, p + 1 表示往前跳过2个字节的地址

5.2 指针 - 指针

指针减指针的结果为两个指针间相差的地址位置个数的绝对值
例如:0x0011fdf2 - 0x0011fdf1 = 1;
0x0011fdf1 - 0x0011fdf2 = -1;

#include 
int my_strlen(char *s)
{
	char *p = s;
	while(*p != '\0' )
	{
		p++;
		return p-s;
	} 
int main()
{
    printf("%d\n", my_strlen("abc"));
    return 0;
}

上面代码的结果为 3.

6. 野指针

6.1 类型

1. 指针未初始化

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

2. 指针越界访问

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

3. 指针指向的空间释放

#include 
int* test()
{
	int n = 100;  
 	return &n;
}
int main()
{
	int*p = test();
    printf("%d\n", *p);
    return 0;
}

6.2 避免方法

1. 指针初始化

如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL.

NULL是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址会报错。

2. 当心指针越界

⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。

3. 指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性

当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使⽤这个指针访问空间的时候,我们可以把该指针置为NULL。因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问,同时使⽤指针之前可以判断指针是否为NULL。

4. 避免返回局部变量的地址

7. 总结

指针是C语言当中最重要的部分之一,熟练掌握指针之后,能加深对于C语言的理解。

你可能感兴趣的:(c语言,学习,开发语言)