首页 > 其他分享 >C语言指针(进阶内容)

C语言指针(进阶内容)

时间:2024-09-02 16:50:51浏览次数:11  
标签:ps arr 进阶 int C语言 地址 数组 指针

目录

1. 指针的基本概念:

2. 不同类型的指针的意义是什么?

3. 野指针是什么?

4.指针加减整数 

5.指针运算 (指针 - 指针)

6.一级指针和二级指针以及多级指针是什么意思?

7.指针数组

8.数组指针

9.函数指针


1. 指针的基本概念:

1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的 4/8 个字节(32位平台/64位平台)。
3. 指针是有类型,指针的类型决定了指针的 + - 整数的步长,指针解引用操作的时候的权限。
4. 指针的运算。


2. 不同类型的指针的意义是什么?

指针本身在内存中都是4/8(32位平台/64位平台)个字节大小,区别就在于不同指针的步长是不相同的。在于被解引用时改变内存空间的多少个字节,不同的指针改变不同的内存空间字节大小。

int main()    
{	
	int a = 1111111;
	char* ps = (char*)&a; // 强制类型转换成char*
	printf("%d",*ps ); // 71
	return 0;
}

int和char指针控制指针偏移量不同 所以修过地址时会发生数据截断,整形空间存储的是4个字节大小,而字符串指针的步长是一个字节大小,这里强制类型转换会截断数据,ps存放的是一个字节的数据,而删除了高位的三个地址(字节)。


3. 野指针是什么?

指向未知、无效或已释放的内存位置的指针被称为野指针。

    int* p;
    *p = 10; // 非法访问内存 p就是野指针

p没有初始化就意味着没有明确指向,一个局部变量没有初始化 放的就是随机值 0xcccccc。

int *test()
{
	int a = 10;
	return &a;
}

int main()
{
  // *p可以获取a的值 前提是没有其他变量占领a的空间否则就是乱码
	int*p = test();
	return 0;
}

即使这里p获取到了a的地址,但是也无法通过解引用去改变a的值,可以获取a的值为10,不过依然无法改变,a变量的地址已经被销毁了。

 这里涉及到函数栈帧的概念,可以去查看这篇文章,很好的解释了什么是栈帧。【详解】函数栈帧——多图(c语言)_指针与函数值传递-CSDN博客


4.指针加减整数 

指针的类型决定了指针的 + - 整数的步长,指针加n,即跳过n * 指针步长的字节,就是指向同一指针类型的第n + 1个元素。

int main()
{
	// 用指针的方式初始化数组
	int arr[5];
	int* ps;
	for (ps = arr; ps < &arr[5];)
	{
		*ps++ = 1;
		// *ps = 1;
		// ps++
	}
	return 0;
}

这里通过指针++的方式,初始化数组。&arr[5]不存在数组越界问题,因为没有访问他的地址,只是用于判断。


5.指针运算 (指针 - 指针)

指针 - 指针 得到的是指针之间的元素个数
不是所以指针都可以相减 指向同一内存空间的两个指针才能相减

int my_strlen3(char* str)
{
	char *start = str; // 存储首字符地址
	while (*str != '\0')
	{
		str++; // 向后寻找 直到寻找到\0
	}
	return str - start; // 指针 - 指针
}

int main()
{
	int len = my_strlen3("abcdef"); // 第六个指针 - 第0个指针
	printf("%d", len);// 6
	return 0;
}

这是一个使用指针运算的方式重写Strlen内置函数的案例,my_strlen3函数最后return返回的是两个指针相减的结果,在同一内存空间中第6个指针减去第0个指针,得出的是元素的个数也就是6。


6.一级指针和二级指针以及多级指针是什么意思?

一级指针指向一块内存空间,其变量值即为该地址,通过“ * ”运算可以获取该地址存储的值。

二级指针是指向指针的指针,其变量值为一级指针的地址。

以此类推三级指针就是指向指针的指针,其变量值为二级指针的地址....

    int a = 10;
    int* pa = &a;	// 一级指针
	int** paa = &pa; // 二级指针
	**paa = 20;

二级指针通过解引用 *paa的方式可以访问到a的内存空间,从而修改a的值。


7.指针数组

指针数组是一个数组,其中的每个元素都是指向某种数据类型的指针。

指针数组存储了一组指针,每个指针可以指向不同的数据对象。

    int arr1[4] = { 1,2,3,4 };
	int arr2[4] = { 2,2,3,4 };
	int arr3[4] = { 3,2,3,4 };
    // 将arr1 arr2 arr3的数组首元素地址存储到数组中
	int* parr[] = { arr1,arr2,arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", parr[i][j]);
		}
		printf("\n");
	}

这里模拟了一个二维数组的结构,将三个数组以地址的形式存放到parr指针数组中,再通过双重for循环遍历parr来获取每个数组中元素的值。


8.数组指针

数组指针本质是一个指针,指向了一个数组。

数组指针常用于接收二维或多维数组的函数传参。

int main()
{
	int* p1 = arr;	// 存放的是数组的首元素地址
	int(*p2)[10] = &arr; // 存放的是整个数组的地址

	return 0;
}

简单演示数组指针的使用,数组指针存放的是整个数组的地址。

  •  那么如何理解数组指针的写法和意思呢? 
int main()
{
	char* arr[10] = { 0 };
	//  (*ps)代表这是一个指针变量名叫ps  
	//  char* 代表存放的数组中的元素是char* 
	//  [] 中必须和存放数组的元素个数一样
	char* (*ps)[10] = &arr;  

	return 0;
}

初始化一个字符串指针类型的数组arr,再用指针数组来存放arr,而这个数组指针存放的arr数组中元素是char *类型,千万不要误以为是二级指针!

  • 如何使用指针数组呢? 
 //数组指针常用于 二维数组甚至三维数组!
void print2(int(*ps)[5], int r, int c)  // 实参用 int (*)[5] 这个类型来接受 是第一行数组的大小
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
            // 这两种写法的结果都相同
            // 因为 *(ps + i) == ps[i]
			//printf("%d ", *(*(ps + i) + j));
			printf("%d ", ps[i][j]);
		}
		printf("\n");
	}
}

int main()
{
	int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
	// 这里arr 传的是第一行的地址
	print2(arr, 3, 5);
	return 0;
}

我们初始化一个二维数组arr,再传入print2函数的第一个参数中,此时arr实际传入的是第一行的数组,而我们函数中第一个形参类型就是数组指针 int(*ps)[5] ,于是我们接收到了二维数组传入进来的第一行数组的地址。

1. 那我们解释一下第一中写法,ps+i 等同于第一行数组向下移动 i 行,*(ps + i )就获取了第 i 行数组的地址,数组的地址也就是首元素地址又等同于数组名。第 i 行的数组名 + j就是数组首元素地址加上 j ,此时二维数组的全部元素都被遍历了一遍。

2. 第二种写法就很好理解了,*(ps + i )等同于ps[ i ],因为传进函数的是arr,所以arr[ i ]就是数组的第 i 行。最后ps[ i ]加上[ j ]就是完整的二维数组写法,也就能将二维数组全部遍历一次了。

  •  一维数组和二维数组的传参时的规范和易错点
// 一维数组传参
void test(int arr[]); //ture
void test(int* arr); //ture
void test(int arr[10]); //ture

void test2(int* arr[20]); //ture
void test2(int** arr); //ture

int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = {0};
	test(arr);
	test2(arr2);
	return 0;
}

 一维数组

 

// 二维数组传参
void test(int arr[3][5]); // ture
void test(int arr[][]); // false
void test(int arr[][5]); // ture
void test(int*arr); // false  因为是第一行数组地址 所以不能用整形指针接收
void test(int*arr[5]); // false 
void test(int(*arr)[5]); // ture 要用数组指针这个类型来接收第一行数组的地址
void test(int**arr); // ture

int main()
{
	int arr[3][5] = { 0 };
	test(arr);
	return 0;
}

 二维数组


9.函数指针

函数指针是指向函数的指针变量。

通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。

  • 函数指针的基本写法和调用方法
int Add(int x, int y)
{
	return x + y;
}

int main()
{
	// &函数名等于函数名 都可以获取函数的地址
	//printf("%p", &Add);  ==  printf("%p", Add);
	
	// 函数指针写法
	int (*ps)(int, int) = &Add;
	int ret = (*ps)(3, 2); // 通过指针的方式调用函数 也可以这样写 ps(3, 2)
	printf("%d", ret);
	return 0;
}

1.&函数名等于函数名 都可以获取函数的地址,只是写法上的不同。

2.函数指针就是获取函数的地址,int (*ps)(int, int) 括号中写的是函数形参的数据类型。

3.可以通过函数指针调用函数,可以省略(*)不写,效果也是一样

  • 函数指针数组也被称为转移表
int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

void munu()
{
	printf("*********************\n");
	printf("********1. 相加*************\n");
	printf("********2. 相减*************\n");
	printf("********3. 退出*************\n");
	printf("*********************\n");
	printf("*********************\n");
}

int main()
{
	// 函数指针数组 - 存放函数指针的数组
	// 转移表
	int (*arr[2])(int, int) = { Add,Sub };
	int x = 0;
	int y = 0;
	int input = 0;
	
	do
	{
		munu();
		scanf("%d", &input);
		if (input == 3)
		{
			printf("退出游戏\n");
		}
		else if (input >= 1 && input <= 2)
		{
			printf("请输入坐标:>");
			scanf("%d %d", &x,&y);
			int ret = arr[input-1](x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("输入错误的选项\n");
		}

	} while (input);

	return 0;

}	

 这个案例简单实现了加减数的功能。我们将加法函数和减法函数的地址存储到函数指针数组中,函数指针数组是可以存放多个函数地址的数组,但函数指针数组中的形参类型都要相同。再通过用户输入和简单的判断调用指定的函数。

  • 指向【函数指针数组】的指针 
int main()
{	
	// 函数指针数组
	int (*arr[2])(int, int) = { Add,Sub };
	// 指向【函数指针数组】的指针
	int (*(arr)[2])(int, int) = &arr[2];
	return 0;
}

 可以无限套娃,但是不建议

  • 无具体类型指针 
int main()
{
	int a = 10;
	void* b = &a; // void*是无具体类型的指针,可以接收
	// void*是无具体类型指针 不可以进行指针+-和引用操作
	return 0;
}

 无具体类型指针void * 通常用于函数形参,不确定传入的是什么数据类型时使用。


好了这就是C语言指针进阶部分的内容,如有哪里错误或者混淆的地方请指出或者留言。

标签:ps,arr,进阶,int,C语言,地址,数组,指针
From: https://blog.csdn.net/m0_64574726/article/details/141782183

相关文章

  • 【树莓派开发】使用树莓派在Linux环境下编写C语言代码
    文章目录前言1.创建test.c文件2.编译运行该文件3.编译并链接两个源文件结语前言如何使用树莓派编译C语言代码呢?21年暑假的时候,学习编程的劲头高涨,然后冲动消费买了个树莓派4B……结果压根不会用,吃灰了半年不过现在已经学完了C语言,也接触了一丢丢Linux系统下的gcc指令,可以尝试用它......
  • 【C语言】文件操作(详解)
    文章目录1.为什么需要文件?2.什么是文件?2.1文件分类2.2文件名3.文件的使用3.1文件指针3.2打开和关闭文件3.2.1文件使用方式3.2.2标准输入输出流3.3文件输入输出函数3.3.1字符输入输出实现文件拷贝3.3.2文本行输入输出3.3.3格式化输入输出3.3.4二进制输入输出3.3.5sscanf/sprintf函......
  • 【C语言】顺序表详解,灵活运用所学知识
    文章目录前言1.什么是顺序表?1.1线性表2.编写你的顺序表!2.0赛前准备2.1初始化2.2容量检查2.3打印顺序表2.4尾插和尾删2.5头插和头删2.6插入和删除2.7查找和更改3.菜单一些err总结前言顺序表是我们学习数据结构第一阶段的必经之路什么是顺序表,且听我慢慢道来本篇博客用到的......
  • 【C语言】数据结构-栈(顺序表实现)
    文章目录前言1.什么是栈2.栈的实现3.敲代码!3.1头文件3.2函数实现4.知识巩固,来道OJ!结语前言在之前的数据结构学习中,我们学习了顺序表、链表这两种结构顺序表:博客链接1单链表:博客链接2链表OJ:博客链接3除了单链表以外,还有一个结构,是双向带头循环链表。这个链表的形式如下头节点的......
  • 【方法分享】准大学生如何自学C语言?
    准大学生如何自学C语言文章目录准大学生如何自学C语言一、找寻一个优质教学视频二、使用优质编译器三、安排一个电子笔记需不需要一个ipad来辅助学习?四、除工具以外的学习方法1.专心致志,不要让其他东西分心2.多练习,多练习,多练习3.复习!结语虽说现在已经接近我们最长的且无忧无虑的......
  • 33. 指针和函数
    1函数形参改变实参的值#include<stdio.h>voidswap1(intx,inty){ inttmp; tmp=x; x=y; y=tmp; printf("x=%d,y=%d\n",x,y);}voidswap2(int*x,int*y){ inttmp; tmp=*x; *x=*y; *y=tmp;}intmain(){ inta=3; intb=5......
  • 52. C高级-指针强化
    1指针是一种数据类型1.1指针变量指针是一种数据类型,占用内存空间,用来保存内存地址。voidtest01(){ int*p1=0x1234; int***p2=0x1111; printf("p1size:%d\n",sizeof(p1)); printf("p2size:%d\n",sizeof(p2)); //指针是变量,指针本身也占内存空间,指针也可以被赋......
  • SQL进阶技巧:如何取时间序列最新完成状态的前一个状态并将完成状态的过程进行合并?
    目录0问题描述1数据准备2问题分析问题1:取最新完成状态的前一个状态 方法1:分析函数求解方法2:关联求解问题2:如何将完成状态的过程合并 方法1:分析函数作为辅助变量 方法2:自关联形式获取全量结果集 3小结0问题描述表status字段及内容如下:date_id为连续自增......
  • 让AI学会打光,从此利好电商(Stable Diffusion进阶篇:Imposing Consistent Light)
    IC-Light的下载安装有两个不同的节点包可以在ComfyUI中安装IC-Light,一个是kijai大佬的节点包:https://github.com/kijai/ComfyUI-IC-Light没有魔法的小伙伴可以扫描下面二维码获取相关整合资料!另一个是huchenlei大佬的节点包:https://github.com/huchenlei/ComfyUI-IC......
  • C语言函数递归(含扫雷进阶思路)
    文章目录一、什么是递归二、递归的使用思路和限制条件1.递归的使用思路2.递归的限制条件三、递归的举例举例1:求n的阶乘2.举例2:顺序打印⼀个整数的每⼀位四、递归与迭代对比五、递归与迭代对比举例七、扫雷进阶思路一、什么是递归  递归是学习C语⾔函数绕不开的......