初阶C语言-指针

初阶C语言-指针_第1张图片
. ◡̈ ᴴᴬᵛᴱ ᴬ ᴳᴼᴼᴰ ᵀᴵᴹᴱ我们终会上岸,无论去到哪里都是阳光万里,鲜花灿烂 ₍ᐢ •⌄• ᐢ₎ 今天我们一起学习一下指针的相关内容吧!

指针

  • 1.指针是什么
  • 2.指针和指针类型
    • 2.1指针的解引用
    • 2.2指针+-整数
  • 3.野指针
    • 3.1野指针成因
    • 3.2如何规避野指针
  • 4.指针运算
    • 4.1指针+-整数
    • 4.2指针的关系运算
    • 4.3指针-指针
  • 5.指针和数组
  • 6.二级指针
  • 7.指针数组

1.指针是什么

指针是什么呢?
1.指针是内存中的一个最小单元的编号,也就是地址。
2.平时口语中说的指针就是指针变量,是用来存放内存地址的变量。

总结:指针就是地址,口语中说的指针通常指的是指针变量❗

我们可以这样理解:内存
初阶C语言-指针_第2张图片
指针变量
我们可以通过&(取地址操作符)取出变量的内存起始地址,把地址可以存放到一个变量中,这个变量就是指针变量。

#define _CRT_SECURE_NO_WARNINGS 1
#include 
int main()
{
	int a = 2;//是向内存中的栈区空间申请了4个字节的空间,这4个字节用来存放2这个数值。
	int* pa = &a;//这里我们对变量a,取出它的地址,可以用&操作符。
	//a变量占有4个字节的空间,这里将a的4个字节的第一个字节的地址存放在p变量中,p就是一个指针变量
	return 0;
}

总结:1.指针变量是用来存放地址的变量。(存放在指针中的值会被当作地址处理。)
2.一个小的单元的大小是一个字节。

那么,这是如何编址的呢?
经过仔细的计算和权衡,我们发现一个字节给一个对应的地址比较合适。对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平和低电平就是0或者1,那么32根地址线就会产生232个地址。
初阶C语言-指针_第3张图片

这里,我们就明白了:

  • 在32位的机器上,地址是32个0或者1组成的二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
  • 那么同样的,在64位的机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。

总结:1.内存被划分成一个个的内存单元,每个内存单元的大小是一个字节。
2.每个字节的内存单元都有一个编号,这个编号就是地址,地址在C语言中被称为指针。
3.地址要储存的话,要存放在指针变量中。
4.每个内存单元都有一个唯一的地址来标识。
5.在32位机器上,地址的大小是4个字节,所以指针变量的大小也是4个字节。
同理,在64位机器上地址的大小是8个字节,所以指针变量的大小也是8个字节。

#define _CRT_SECURE_NO_WARNINGS 1
#include 
int main()
{
	int a = 10;
	int* pa = &a;
	*pa = 20;
	printf("%d\n", a);
	return 0;
}

初阶C语言-指针_第4张图片

2.指针和指针类型

前面我们知道了变量有不同的类型:整型、浮点型等。那么指针有类型吗?
通过上面的示例:我们可以知道指针的定义方式是:type + *
那么:

  • char*类型的指针是为了存放char类型变量的地址。
  • short*类型的指针是为了存放short类型变量的地址。
  • int*类型的指针是为了存放int类型变量的地址。

我们也计算一下不同的指针类型的大小是多少呢?

#define _CRT_SECURE_NO_WARNINGS 1
#include 
int main()
{
	printf("%d\n", sizeof(int*));
	printf("%d\n", sizeof(short*));
	printf("%d\n", sizeof(long*));
	printf("%d\n", sizeof(float*));
	printf("%d\n", sizeof(double*));
	printf("%d\n", sizeof(long long*));
	return 0;
}

初阶C语言-指针_第5张图片

初阶C语言-指针_第6张图片
我们发现:在32位的机器上,指针的大小是4个字节,在64位的机器上,指针的大小是8个字节。那么,指针类型的意义是什么呢?

2.1指针的解引用

#define _CRT_SECURE_NO_WARNINGS 1
#include 
int main()
{
	/*int a = 0x11223344;
	int* p = &a;
	*p = 0;*/
	int a = 0x11223344;
	char* p = &a;
	*p = 0;
	return 0;
}

初阶C语言-指针_第7张图片
初阶C语言-指针_第8张图片
初阶C语言-指针_第9张图片
初阶C语言-指针_第10张图片
总结:指针的类型决定了对指针解引用的时候有多大的权限❗
比如:char*的指针解引用就指针访问1个字节,而int*的指针解引用可以访问4个字节。

2.2指针±整数

#define _CRT_SECURE_NO_WARNINGS 1
#include 
int main()
{
	int a = 1;
	char* pc = &a;
	int* pi = &a;
	printf("pc = %p\n", pc);
	printf("pc+1 = %p\n", pc+1);
	printf("pi = %p\n", pi);
	printf("pi+1 = %p\n", pi+1);
	return 0;
}

初阶C语言-指针_第11张图片
总结:指针的类型决定了指针+1/-1跳过几个字节❗
比如:char*的指针+1,跳过1个字节,int*的指针+1,跳过4个字节。double*的指针+1,跳过8个字节等等。

3.野指针

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

3.1野指针成因

1.指针未初始化

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

2.指针越界访问

#include 
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for (i = 0; i <= 10; i++)
	{
		*p = -1;//循环了11次,越界访问
		p++;
	}
	return 0;
}

初阶C语言-指针_第12张图片
3.指针指向的空间释放

#include 
int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int* p = test();//p是野指针
	printf("%d\n", *p);
	return 0;
}

3.2如何规避野指针

如何规避野指针:

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

1.如果明确知道指针指向哪里,就初始化正确的地址。
例如:int a = 20;int* p = &a;
2.如果指针不知道初始化为什么值,就初始化为NULL .
例如:int* p = NULL;
3.空指针NULL值为0,0作为地址时,这个地址用户不能直接访问!

4.指针运算

4.1指针±整数

#include 
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//使用指针打印数组内容
	int* p = arr;
	//arr == p
	//arr + i == p + i
	// *(arr + i) == *(p + i) == arr[i]
	//p指向的是数组首元素
	//p + i是数组中下标为i的元素的地址
	//p + i起始时跳过了i*sizeof(int)个字节
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
		//printf("%d ", *(arr + i));
		//printf("%d ", arr[i]);
		//printf("%d ", i[arr]);
	}
	return 0;
}

初阶C语言-指针_第13张图片

4.2指针的关系运算

#include 
#define Count 5
float values[Count];
float* p;
int main()
{
	for (p = &values[Count]; p > &values[0];)
	{
		*--p = 0;
	}
	//简写为:(这种写法不推荐,因为标准不保证它可行)
	/*for (p = &values[Count - 1]; p >= &values[0]; p--)
	{
		*p = 0;
	}*/
}

标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许和指向第一个元素之前的那个内存位置的指针进行比较。

4.3指针-指针

接下来,我们看看指针-指针是什么样的结果呢?

#include 
int main()
{
	int arr[10] = { 0 };
	//指针-指针的前提:两个指针指向同一块区域,指针类型是相同的
	//指针-指针的差值的绝对值是指针和指针之间的元素个数
	printf("%d\n", &arr[0] - &arr[9]);
	printf("%d\n", &arr[9] - &arr[0]);
	return 0;
}

初阶C语言-指针_第14张图片
✅我们来利用指针-指针实现strlen函数

 #include 
int str(char* arr)
{
	char* p = arr;
	while (*p != '\0')
	{
		p++;
	}
	return p - arr;//指针-指针的差值的绝对值是指针和指针之间的元素个数
}
int main()
{
	char arr[] = "abcdefghigk";
	int ret = str(arr);
	printf("%d\n", ret);
	return 0;
}

初阶C语言-指针_第15张图片

5.指针和数组

1.指针就是指针,指针变量是一个变量,用来存放地址,指针变量的大小是4个字节或者8个字节。
2.数组就是数组,可以存放一组数,数组的大小是取决于元素的类型和个数。
3.数组的数组名是数组首元素的地址,地址是可以放在指针变量中的。通过指针可以访问一个数组的元素。

#include 
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	return 0;
}

初阶C语言-指针_第16张图片
✅数组名通常是表示数组首元素的地址,但是有两个例外:
1.sizeof(数组名),数组名单独放在sizeof内部,数组名表示整个数组,计算的是数组的大小,单位是字节。
2.&数组名,数组名表示整个数组,取出的是数组的地址,数组的地址和数组首元素的地址是一样的,但是类型和意义是不一样的。

#include 
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%p\n", arr);
	printf("%p\n", arr + 1);//跳过4个字节
	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0]+1);//跳过4个字节
	//例外:
	printf("%p\n", &arr);
	printf("%p\n", &arr+1);//跳过40个字节
	printf("%d\n", sizeof(arr));
	return 0;
}

初阶C语言-指针_第17张图片

6.二级指针

#include 
int main()
{
	int a = 10;
	int* p = &a;//p是指针变量,一级指针变量
	int** pp = &p;//pp是指针变量,二级指针变量
	//int*** ppp = &pp;//ppp是指针变量,三级指针变量
	**pp = 20;//二级指针解引用一次得到一级指针变量的地址,再解引用一次得到一级指针所指向的变量
	printf("%d\n", a);
	return 0;
}

初阶C语言-指针_第18张图片

初阶C语言-指针_第19张图片

7.指针数组

指针数组是存放指针的数组。
前面对于数组,我们已经了解过了整型数组int arr1[3];以及字符数组char arr2[3];
那么同样的,char* arr3[3];就是存放字符指针的数组。

#include 
int main()
{
	//使用指针数组模拟二维数组
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	//指针数组
	int* arr[] = { arr1,arr2};
	int i = 0;
	for (i = 0; i < 2; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

初阶C语言-指针_第20张图片
注:这里只是模拟二维数组,并不是真正的二维数组,注意区分!

好啦,关于指针的知识点到这里就结束啦,后期会继续更新C语言的相关知识,欢迎大家持续关注、点赞和评论!❤️❤️❤️

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