【C进阶】指针的进阶

news2024/11/24 7:33:31

家人们欢迎来到小姜的世界,<<点此>>传送门 这里有详细的关于C/C++/Linux等的解析博客,家人们赶紧冲鸭!!!

客官,码字不易,来个三连支持一下吧!!!关注我不迷路!!!

指针的进阶

  • 前言
  • 一、 字符指针
    • (一)概念
    • (二)笔试题
  • 二、指针数组
    • (一)字符指针的数组
    • (二)整型数组
  • 三、 数组指针
    • (一)数组指针的定义
    • (二)&数组名VS数组名
    • (三)数组指针的使用
      • 1.不常用但好理解的代码
      • 2.常规代码
      • 3.小知识
        • (1)如何强转
        • (2)类型
    • (四)回顾代码
  • 四、数组参数、指针参数
    • (一)一维数组传参
    • (二)二维数组传参
    • (三)一级指针传参
    • (四)二级指针传参
  • 五、函数指针
    • (一)概念
    • (二)两个有趣代码
      • 1.代码1
      • 2.代码2
  • 六、函数指针数组
    • (一)基本概念
    • (二)例子(计算器)
  • 七、指向函数指针数组的指针
    • (一)代码
    • (二)解释
  • 八、回调函数
    • (一)概念
    • (二)引例
  • 九、指针综合练习笔试题
  • 总结


前言

指针居然有进阶,我一直以为指针是简简单单的找地址,可是整理了一遍发现指针的进阶居然那么细节那么难,最主要的是指针进阶是把前面学习的所有知识,例如数组、函数相结合起来进行操作,是很复杂的,所以大家要多看几遍此篇博客!!!我们今天要讲解的是字符指针、数组指针、指针数组、数组传参和指针传参、函数指针、函数指针数组、指向函数指针数组的指针、回调函数、指针和数组面试题的解析。
指针的基本概念:

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

一、 字符指针

(一)概念

在这里插入图片描述
在这里插入图片描述

(二)笔试题

#include <stdio.h>
int main(){
	char str1[] = "hello world";
	char str2[] = "hello world";
	const char* str3 = "hello world";
	const char* str4 = "hello world";
	//are not same
	if (str1 == str2)
		printf("str1 and str2 are same\n");
	else
		printf("str1 and str2 are not same\n");
	//are same
	if (str3 == str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are not same\n");

	return 0;
}

在这里插入图片描述


二、指针数组

大家可以先看一看之前的指针初阶,可以去看一下关于指针数组的知识。
【C初阶】详解指针

(一)字符指针的数组

在这里插入图片描述
如图,当我们进行打印常量数组的时候,是找到首元素的地址然后用%s打印,如下代码:

//整型数组 - 存放整型的数组
//字符数组 - 存放字符的数组
//指针数组 - 存放指针(地址)的数组
 
int main() {
	//存放字符指针的数组
	const char* arr[4] = { "abcdef","qwer","yes","no" };
	int i = 0;
	for (i = 0; i < 4; i++) {
		printf("%s\n", arr[i]);
	}

	return 0;
}

在这里插入图片描述

(二)整型数组

在这里插入图片描述
如图,当我们找到数组的首元素地址的时候,只要根据j作为下标往后找元素。

int main() {
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	int arr4[5] = { 4,5,6,7,8 };
	//指针数组
	int* arr[4] = { arr1,arr2,arr3,arr4 };
	int i = 0;
	for (i = 0; i < 4; i++) {
		int j = 0;
		for (j = 0; j < 5; j++) {
			//printf("%d ", arr[i][j]);
			printf("%d ", *(arr[i] + j));//找到j下标元素的地址
		}
		printf("\n");
	}
	return 0;
}

在这里插入图片描述
在这里插入图片描述


三、 数组指针

(一)数组指针的定义

数组指针是指针?还是数组?
答案是:指针。
我们已经熟悉:
整形指针: int * pint; 能够指向整形数据的指针。
浮点型指针: float * pf; 能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针。
在这里插入图片描述
大家可以做一个类比,取出一个字符的地址并存起来,是用字符指针存起来的;整型指针存放的是一个整型数的地址;&数组名是取出的整个数组的地址,再存放到数组指针中,因为[]的优先级大于*,因为pa需要和*先结合,毕竟它是一个指针,所以需要加一个()来保证p和*先结合。

#include<stdio.h>
int main() {
	char ch = 'w';
	char* pc = &ch;//字符指针

	int num = 10;
	int* pi = &num;//整型指针

	int arr[10];
	int(*pa)[10] = &arr;//数组指针
	//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。
	//所以p是一个指针,指向一个数组,叫数组指针。
	//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

	return 0;
}

(二)&数组名VS数组名

我们知道arr是数组名,数组名表示数组首元素的地址。
那&arr数组名到底是什么?
那各位如果想很详细了解可以去看一下下面的链接:
【C初阶】数组详解
在这里插入图片描述

#include<stdio.h>
int main() {
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//&arr取出的是数组的地址,只有数组的地址才需要数组来接收
	int(*p)[10] = &arr;//数组指针
	
	//数组名 - 数组首元素的地址
	//&数组名 - 是整个数组的地址
	//数组首元素的地址和数组的地址从值的角度来看是一样的,但是意义不一样

	printf("%p\n", arr);//类型:int*
	printf("%p\n", arr + 1);//4

	printf("%p\n", &arr[0]);//类型:int*
	printf("%p\n", &arr[0] + 1);//4

	printf("%p\n", &arr);//类型:int(*)[10]
	printf("%p\n", &arr + 1);//40

	return 0;
}

(三)数组指针的使用

既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。

1.不常用但好理解的代码

//不常用但好理解的代码
#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int(*p)[10] = &arr;
	int i = 0;
	for (i = 0; i < 10; i++) {
		printf("%d ", (*p)[i]);//p解引用再加下标找到位置
	}

	return 0;
}

找到整个数组的地址然后解引用以后通过下标去访问数组内的元素。

2.常规代码

一般用在二维数组,那我们就看看二维数组是怎么进行传参的吧!
先来一个大家耳熟能详的传参方式:

#include <stdio.h>
void print1(int arr[3][4], int r, int c) {//形参接收的是二维数组
	int i = 0;
	for (i = 0; i < r; i++) {
		int j = 0;
		for (j = 0; j < c; j++) {
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}

int main() {
	int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
	print1(arr, 3, 4);//二维数组传参

	//print2(arr, 3, 4);
	return 0;
}

那可是今天我们要讲的是指针啊,这传参仅仅是二维数组,那指针怎么传参呢?其实还是很简单的,二维数组的首地址就是这个二维数组的第0行,因为二维数组在计算机内部也是连续存放的,那不就是四个元素存放在一个大元素当中,成为一个首地址元素,那再用指针去指向这个数组不就好了吗,那我们试一试吧!
在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
//void print1(int arr[3][4], int r, int c) {//形参接收的是二维数组
//	int i = 0;
//	for (i = 0; i < r; i++) {
//		int j = 0;
//		for (j = 0; j < c; j++) {
//			printf("%d ", arr[i][j]);
//		}
//		printf("\n");
//	}
//}

void print2(int(*p)[4],int r,int c) {
	int i = 0;
	for (i = 0; i < r; i++) {
		int j = 0;
		for (j = 0; j < c; j++) {
			printf("%d ", (*(p + i))[j]);//类型本来就是int [j]所以固定格式不变,再p+偏移量是找到后面的地址再解引用访问
			                             //[]的优先级大于*,所以要再加一个()来先解引用操作找到地址
		}
		printf("\n");
	}
}

int main() {
	int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
	//print1(arr, 3, 4);//二维数组传参

	print2(arr, 3, 4);
	return 0;
}

3.小知识

(1)如何强转

int (*p)[10]=arr;如何强转?
在这里插入图片描述

(2)类型

在这里插入图片描述

(四)回顾代码

在这里插入图片描述


四、数组参数、指针参数

(一)一维数组传参

在这里插入图片描述

(二)二维数组传参

在这里插入图片描述

(三)一级指针传参

形参直接用一级指针接收就好了。
在这里插入图片描述

(四)二级指针传参

形参直接用二级指针接收传参就好了。
在这里插入图片描述

当函数的参数为二级指针的时候,可以接收什么参数?
在这里插入图片描述


五、函数指针

(一)概念

函数也有地址!?原来函数也是在栈区创建的一片空间,也是有地址的。
在这里插入图片描述
在这里插入图片描述

int Add(int x, int y) {
	return x + y;
}

int main() {
	//int arr[10];
	//int(*pa)[10] = &arr;//类比,函数指针与数组指针非常相似
	//printf("%p\n", &Add);

	int(*pf)(int, int) = &Add;
	int ret = (*pf)(2, 3);//调用Add函数,通过pf调用函数
	int ret = Add(2, 3);//我们之前写的
	//而Add是pf调用的,所以Add和pf是一回事
	//所以等价于int ret = pf(2, 3);
	//(*pf)中的*是一颗摆设,写几颗都无所谓,但是一旦写上一定要加上括号,因为不加()是解引用pf(2,3)的结果
	printf("%d\n", ret);
	//pf就是一个存放函数地址的指针变量,被称为函数指针
	//&函数名和函数名都是函数的地址
	//上式等价于int (*pf)(int, int) = Add;

	return 0;
}

关于*的问题,大家想一想,当调用函数的时候,我们用一个pf来接收,那如何接收呢?先要声明,这个pf是个指针,所以星号p,而大家想,这个是不是可以与一维数组指针进行类比,那我们看,这个函数指针的类型是什么,是后面加上(),圆括号里面是int,int两个类型的,而这一整个函数指针的类型又是int,所以就有了int(*pf)(int, int) = &Add;其次大家想,那我想把这个指针存起来该怎么办?那么我们看,先将pf解引用找到这个指针的地址去看里面的内容,那再通过里面的实参去进行进入形参当中,那不就是int ret = (*pf)(2, 3);那其实大家觉得这样很好理解,那就这么用。但是还有更简练的方法是:Add与&Add是一回事,这与数组是不一样的,而我们想之前我们写调用函数是int ret = Add(2, 3);这么方便,现在我哼哧哼哧写了那么多难以理解的东西,怎么经过简化呢?大家看,Add是由pf去进行调用的,那这两个就是等价的,既然等价,那就直接用pf来代替Add好了,那最后的结果就是int ret = pf(2, 3);所以说那颗星星是没用的,加几颗都没事,但是一旦加上了那颗星,就跟我们之前说的一样了,是要先解引用呀,解引用才能找到这个地址,然后再去访问这里面的元素的呀,所以需要加括号。

何时用的问题:经常用于函数调用函数的情况,调用地址不是更加方便吗?

(二)两个有趣代码

出自《C的陷阱和缺陷》。

1.代码1

在这里插入图片描述
想到的需要是0是一个地址

int main() {
	//该代码是一次函数调用
	//调用0地址处的一个函数
	(*(void (*)())0)();
	//void (*)()是函数指针类型
	//首先是把0(函数的地址)强制类型转换为void (*)()的函数指针
	//然后去调用0地址处的函数


	return 0;
}

在这里插入图片描述

2.代码2

在这里插入图片描述
关键需要一步一步剖析,它是大的函数指针包着一个小的函数指针。

int main() {
	void (*signal(int, void(*)(int)))(int);
	//该代码是一次函数的声明
	//声明的函数的名字叫signal
	//signal函数的第一个参数是一个整型int类型
	//signal函数的第二个参数是函数指针类型
	//该函数指针指向的那个函数的参数是int,返回类型是void
	//signal函数的返回类型是一个函数指针:void (*)(int);
	//该函数指针参数是int,返回类型是void

	//简化
	typedef void(*pf_t)(int);//pf_t就是这个函数指针类型为void(*)(int)
	pf_t signal(int pf_t);

	return 0;
}

在这里插入图片描述


六、函数指针数组

(一)基本概念

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组。

#include<stdio.h>

int my_strlen(const char* str) {
	return 0;
}

int main() {
	//函数指针数组
	int(*pf[5])(const char*) = { &my_strlen };//数组内部存放的是函数的地址

	return 0;
}

(二)例子(计算器)

这是我们能写出来最简单的计算器,但是不是太长了……

//写一个计算器实现加减乘除
#include<stdio.h>

void menu(void) {
	printf("************************\n");
	printf("****1.add     2.sub*****\n");
	printf("****3.mul     4.div*****\n");
	printf("******   0.exit   ******\n");
	printf("************************\n");
}

int Add(int x, int y) {
	return x + y;
}
int Sub(int x, int y) {
	return x - y;
}
int Mul(int x, int y) {
	return x * y;
}
int Div(int x, int y) {
	return x / y;
}

int main() {
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do {
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input) {
		case 1:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);

	return 0;
}

我们进行一个优化吧!
我们发现返回类型和参数是一模一样的!
那我们用函数指针数组来简化一下上式的switch吧!

//写一个计算器实现加减乘除
#include<stdio.h>

void menu(void) {
	printf("************************\n");
	printf("****1.add     2.sub*****\n");
	printf("****3.mul     4.div*****\n");
	printf("******   0.exit   ******\n");
	printf("************************\n");
}

int Add(int x, int y) {
	return x + y;
}
int Sub(int x, int y) {
	return x - y;
}
int Mul(int x, int y) {
	return x * y;
}
int Div(int x, int y) {
	return x / y;
}

//函数指针数组存放上述函数的地址
//转移表
int (*pf[5])(int, int) = { NULL,&Add,&Sub,&Mul,&Div };//因为我们的1是Add函数,所以第0位空出来
//只能操作整数的双目操作
int main() {
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do {
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		if (input == 0) {
			printf("退出计算器\n");
			break;
		}
		else if (input >= 1 && input <= 4) {
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			int ret = pf[input](x, y);
			printf("%d\n", ret);
		}
		else {
			printf("选择错误\n");
		}
	} while (input);

	return 0;
}

七、指向函数指针数组的指针

(一)代码

#include<stdio.h>

int main() {
	//函数指针数组
	int (*pf[5])(int, int);

	//ppf是指向函数指针数组的指针
	int (*(*ppf)[5])(int, int) = &pf;//&pf就是对数组名取地址,取的是整个数组的地址,存放到数组指针中去
	//但pf为一个函数指针数组,所以需要放到函数指针当中,所以加了函数指针的框架

	return 0;
}

(二)解释

在这里插入图片描述
在这里插入图片描述


八、回调函数

回调函数是本课最难的也是最复杂的,要理解起来非常麻烦,所以需要大家细细去品味,或许会很枯燥,但也需要好好品味去学习。

(一)概念

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
即:用函数指针调用函数。

(二)引例

在这里插入图片描述
case里面都有相同的参数,我们之前已经使用了将Add,Sub,Mul和Div这四个函数进行统一的分装到一个函数指针数组当中,这样只要用到哪个函数就直接利用函数指针去进行访问函数指针数组即可,但是,我们现在想用case语句,那可是发现四个case内部除了调用函数不同,其他全部相同,那我们就想了,能不能找一个共同的函数,直接通过这个函数去进行访问其他函数的地址进而进入函数内部进行计算呢?那我们就来一个比较简单的函数就是calc();如下代码:

//写一个计算器实现加减乘除
#include<stdio.h>

void menu(void) {
	printf("************************\n");
	printf("****1.add     2.sub*****\n");
	printf("****3.mul     4.div*****\n");
	printf("******   0.exit   ******\n");
	printf("************************\n");
}

int Add(int x, int y) {
	return x + y;
}
int Sub(int x, int y) {
	return x - y;
}
int Mul(int x, int y) {
	return x * y;
}
int Div(int x, int y) {
	return x / y;
}

void calc(int(*pf)(int, int)) {
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数:>");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("%d\n", ret);
}
int main() {
	int input = 0;
	do {
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input) {
		case 1:
			calc(&Add);
			break;
		case 2:
			calc(&Sub);
			break;
		case 3:
			calc(&Mul);
			break;
		case 4:
			calc(&Div);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);

	return 0;
}

在这里插入图片描述
所以这就是很好的解释了回调函数是什么,当我们发现有四个case操作是一样的,但是就是所需要调用的函数不一样,那就很好办了,找一个函数中介去进行调用,就相当于大家都在一个小姜中介所看中了这一套房子,但出于价格稍微有点贵都相等房价下跌,所以你们告诉了我电话和家庭住址,我有了你们的地址了,存起来但还没用,突然有一天,小徐打电话给我说:“小姜中介,我想直接全款拿下这套房子!”,那此时小徐是已经告诉了我他的地址和电话,此时我是知道他的地址的,所以我的指针是指向他的,但此时还没用到他的地址,在后续的操作中,当我需要去找他并带他一起去看房的时候,那我就需要他的地址并带他去看房了,也就是说,calc函数是一个中介函数,而真正的回调函数是Sub函数。

来个传送门,传送大家到qsort函数那里去,模拟实现qsort函数就是一个非常经典的回调函数的例子,那可是干货满满的!!
传送门【<<点此>>进行传送】

九、指针综合练习笔试题

这里需要给两个传送门,直接跳转到我们的笔试题解析当中,将这几年所有的笔试题精妙绝伦地演示出来,供大家学习一下:

传送门<<点此>>【C进阶】指针笔试题汇总
传送门<<点此>>【C进阶】指针和数组综合题


总结

此篇博客干货满满,先是以很长的篇幅讲解了关于各类复合指针,后续主要介绍了回调函数中的qsort函数,可谓是十分巧妙,以及讲解了关于指针和各类数组的配合使用,是非常详细的,干货满满,读完这里大概已经读了20分钟,但仔细学这篇博客需要一天的时间,所以大家需要耐心阅读与学习总结。


客官,码字不易,来个三连支持一下吧!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/158171.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

祝大家兔年 新春快乐Happy new year

春节&#xff0c;也被称为农历新年。对于中国人来说&#xff0c;这是规模最大&#xff0c;最重要的传统节日。The Spring Festival is also called Chinese Lunar New Year. Being one of the traditional Chinese festivals, it is the grandest and most important festival …

Ue4 Insights的使用

1.运行UnrealInsights.exe 2.执行独立进程或者打包exe 这时会发现Insights自动创建并开始运行了一个Trace Sessions&#xff0c;持续记录到.utrace文件中 .utrace文件路径 3.也可以通过连接IP地址&#xff0c;获取到该计算机的UE程序。状态为LIVE实时 4.点击右下角Open按钮…

Redis客户端命令基础操作一

查看所有key 语法: keys * 是否存在key 语法: exists [key] 获取包含指定字符串的key 语法&#xff1a; keys *[字符串]* 设置key 语法&#xff1a;set [key] [value] 设置key 语法&#xff1a; setex 【key】【过期时间&#xff08;单位秒&#xff09;】【value】 key重…

时隔 20 年,这个编程语言再次“称王”

近日&#xff0c;全球知名的编程语言流行度排行榜网站 TIOBE 公布了 1 月编程指数信息。前三的编程语言是Python、C 和C&#xff0c;第四为Java&#xff0c;第五是C#。 TIOBE 的 2022 年度编程语言最终花落 C&#xff0c;也是它时隔 20 年后第二次赢得这一称号。 “年度编程语…

行云创新受邀出席2023中国(深圳)阿联酋(迪拜)经贸合作交流会

1月10日&#xff0c;2023中国&#xff08;深圳&#xff09;-阿联酋&#xff08;迪拜&#xff09;经贸合作交流会成功举办。本次交流会充分展示了深圳和迪拜两地城市营商环境和政策优势&#xff0c;并围绕科技创新、数字经济、港口物流等领域发展经验展开分享&#xff0c;来自两…

Linux网络常用命令(ifconfig/ethtool/nmon+n)

Linux网络常用命令ifconfig可设置网络设备的状态&#xff0c;或是显示目前的设置ethtool 是用于查询及设置网卡参数的命令服务端监控工具&#xff1a;nmonnmon可监控的数据类型ifconfig可设置网络设备的状态&#xff0c;或是显示目前的设置 显示linux系统中当前服务器中的全部…

【并发】并发锁机制-深入理解synchronized(二)

【并发】并发锁机制-深入理解synchronized&#xff08;二&#xff09; synchronized 高级篇&#xff08;底层原理&#xff09; synchronized是JVM内置锁&#xff0c;基于Monitor机制实现。 这个Monitor就是管程的意思&#xff0c;它可以控制线程&#xff0c;让其陷入等待&am…

想去看更大的世界,社科院与杜兰大学金融管理硕士项目给予你前行的勇气

当我们的工作生活趋于稳定&#xff0c;我们那颗不安分的心就按捺不住的跳动&#xff0c;想要去看更美的风景&#xff0c;探索更大的世界。所谓遥不可及的梦想才是你见过更大世界的证明&#xff0c;社科院与杜兰大学金融管理硕士项目给予你前行的勇气。一定要不断提高自己的认知…

浅谈函数栈帧(Stack Frame)

&#x1f499;作者&#xff1a;阿润菜菜 &#x1f4d6;专栏&#xff1a;C 本文目录 什么是栈帧 在调试中观察 总结 什么是栈帧 那我们先来看看什么是栈&#xff1a; 栈(stack)是限定仅在表尾进行插入或者删除的线性表。栈是一种数据结构&#xff0c;它按照后进先出的原则存储…

C进阶:字符函数和内存函数

字符串函数和内存函数字符函数和内存函数字符函数求字符串长度strlen长度不受限制的字符串函数strcpystrcatstrcmp长度受限制的字符串函数strncpystrncatstrncmp字符串查找strstrstrtok错误信息报告strerror字符函数&#xff1a;内存函数memcpymemmovememcmpmemset库函数的模拟…

2023年网络安全比赛--跨站脚本攻击中职组(超详细)

一、竞赛时间 180分钟 共计3小时 二、竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 1.访问服务器网站目录1,根据页面信息完成条件,将获取到弹框信息作为flag提交; 2.访问服务器网站目录2,根据页面信息完成条件,将获取到弹框信息作为flag提交; 3.访问服务器网站目录…

javaWeb jsp

概念&#xff1a; Java Server Pages&#xff0c;Java服务端页面。 其中既可以定义 HTML、JS、CSS等静态内容&#xff0c;还可以定义 Java代码的动态内容 JSP HTML Java。最终解析为一个servlet输出给前端。 jsp实践 <dependency> <groupId>javax.servlet…

ASP.NET Core 3.1系列(24)——依赖注入框架之Autofac

1、前言 前面的博客已经介绍过ASP.NET Core中内置IoC容器的使用方法。对于规模较小的项目来说&#xff0c;内置容器完全够用。但在实际开发业务中&#xff0c;一般更推荐开发者使用Autofac作为系统的IoC容器。相较于微软提供的内置容器&#xff0c;Autofac无论是在功能性还是灵…

Python Socket联机自动匹配双人五子棋(含登录注册系统与界面,数据库连接,可作结课作业,可用于学习)

1、前言 首先&#xff0c;关于源码的获取&#xff0c;本人提供了三种方式&#xff1a; 直接从文章里面CtrlC&#xff0c;CtrlV&#xff0c;然后按照我已给的文件结构搞一下即可&#xff1b;通过积分下载上传到CSDN的资源&#xff1b;点开本人的主页&#xff0c;点击“查看详细…

C语言-字符串+内存函数介绍与模拟实现(10)

目录 思维导图&#xff1a; 字符串与内存函数 求字符串长度 strlen 长度不受限制的字符串函数 strcpy strcat strcmp 长度受限制的字符串函数介绍 strncpy strncat strncmp 字符串查找 strstr strtok 错误信息报告 strerror perror 字符操作 内存操作函数 …

Linux之select、poll、epoll讲解

文章目录1 select、poll、epoll1.1 引言1.2 IO和Linux内核发展1.2.1 整体概述1.2.2 阻塞IO1.2.3 非阻塞IO1.2.4 select1.2.5 共享空间1.2.6 零拷贝1.3 select1.3.1 简介1.3.2 select缺点1.4 poll介绍1.4.1 与select差别1.4.2 poll缺点1.5 epoll1.5.1 epoll相关函数1.5.2 epoll优…

详解floor函数、ceil函数和round函数

1.floor函数 功能&#xff1a;把一个小数向下取整 即就是如果数是2.2 &#xff0c;那向下取整的结果就为2.000000 原型&#xff1a;double floor(doube x); 参数解释&#xff1a; x:是需要计算的数 返回值&#xff1a; 成功&#xff1a;返回一个double类型的数&#xff0c;此数…

6-星际密码

题目 星际战争开展了100年之后&#xff0c;NowCoder终于破译了外星人的密码&#xff01;他们的密码是一串整数&#xff0c;通过一张表里的信息映射成最终4位密码。表的规则是&#xff1a;n对应的值是矩阵X的n次方的左上角&#xff0c;如果这个数不足4位则用0填充&#xff0c;如…

C语言-自定义类型-结构体(11.1)

目录 思维导图&#xff1a; 1.结构体类型的基础知识 1.1结构体的声明 1.2特殊的声明 2.结构的自引用 3.结构体变量的定义和初始化 4.结构体内存对齐 4.1如何计算 4.2如何修改内对齐数 5.结构体传参 写在最后&#xff1a; 思维导图&#xff1a; 1.结构体类型的基础知…

Leetcode:98. 验证二叉搜索树(C++)

目录 问题描述&#xff1a; 实现代码与解析&#xff1a; 递归&#xff1a; 原理思路&#xff1a; 迭代&#xff08;中序&#xff09;&#xff1a; 思路原理&#xff1a; 问题描述&#xff1a; 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。…