C语言指针全解

news2024/11/14 11:01:14

C语言指针全解

      • 指针的定义
      • 指针的大小
      • 指针类型
        • 指针类型意义
      • 野指针
      • 二级指针
      • 字符指针
      • 指针数组
      • 数组指针
      • &数组名 VS 数组名
        • 应用
      • 数组参数、指针参数
      • 函数指针
      • 函数指针数组
        • 函数指针数组的使用 - 模拟计算器
      • 指向函数指针数组的指针
      • 回调函数
        • 回调函数的使用 - qsort函数

指针的定义

在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的内存单元,可以说地址指向该内存单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。

  • 指针变量是用于存放地址的变量。(存放在指针中的值都将被当作地址处理)
#include<stdio.h>
int main()
{
	int a = 10;//在内存中开辟一块空间
	int* p = &a;//将a的地址取出,放到指针变量p中
	return 0;
}

指针的大小

对于32位的机器,即有32根地址线,因为每根地址线能产生正电(1)或负电(0),所以在32位的机器上能够产生的地址信号就是32个0/1组成的二进制序列:一共 232 个地址。
同样的算法,在64位的机器上一共能产生 264 个不同的地址。

232 可以用32个bit位进行存储,而8个bit位等价于1个字节,所以在32位的平台下指针的大小为4个字节。
264 可以用64个bit位进行存储,所以在64位的平台下指针的大小为8个字节。

在32位平台下指针的大小为4个字节,在64位平台下指针的大小为8个字节。

指针类型

指针有哪些类型?

指针的定义方式是type + *
char * 类型的指针存放的是char类型的变量地址;
int * 类型的指针存放的是int类型的变量地址;
float * 类型的指针存放的是float类型的变量地址等。

指针类型意义

1.指针±整数

  • 若指针类型为int * 的指针+1,那么它将跳过4个字节的大小指向4个字节以后的内容。

2.指针解引用

指针的类型决定了指针解引用的时候能够访问几个字节的内容。

  • 若指针类型为int *,那么将它进行解引用操作,它将可以访问从指向位置开始向后4个字节的内容。

总结:

  • 指针的类型决定了指针向前或向后走一步有多大距离。
  • 指针的类型决定了指针在进行解引用操作时,能向后访问的空间大小。

野指针

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

野指针的成因

1.指针未初始化
2.指针越界访问
3.指针指向的空间被释放

二级指针

我们知道,指针变量是用于存放地址的变量。但是指针变量也是变量,是变量就有地址,那么存放指针变量的地址的变量是什么呢?

其实,存放普通变量的地址的指针叫一级指针,存放一级指针变量的地址的指针叫二级指针,存放二级指针变量地址的指针叫三级指针,以此类推。

#include<stdio.h>
int main()
{
	int a = 10;
	int* p1 = &a;
	int** p2 = &p1;
	return 0;
}

在这里,我们用一级指针p1存放了普通常量a的地址,用二级指针p2存放了一级指针p1的地址。
这时如果我们要得到a的值,就有两种方法:
方法一:对一级指针p1进行一次解引用操作即可得到a的值,即*p1。
方法二:对二级指针p2进行一次解引用操作即可得到p1的值,而p1的值就是a的地址,所以再对p2进行一次解引用操作即可得到a的值,也就是对二级指针p2进行两次解引用操作即可得到a的值,即**p2。

字符指针

例:

#include<stdio.h>
int main()
{
	const char* p = "hello csdn.";
	printf("%c\n", *p);//打印字符'h'
	printf("%s\n", p);//打印字符串"hello csdn."
	return 0;
}
  • 代码中,字符指针p中存放的并非字符串"hello csdn.",字符指针p中存放的是字符串"hello csdn.“的首元素地址,即字符’h’的地址。
  • 代码中的字符串"hello csdn."是一个常量字符串,存放在常量区,不可以被修改,我们顺便可以加上const修饰
  • 我们对字符指针p进行解引用操作并以字符的形式打印时只能打印字符’h’。我们知道,打印一个字符串只需要提供字符串的首元素地址即可,既然字符指针p中存放的是字符串的首元素地址,那么我们只要提供p(字符串首地址)并以字符串的形式打印,便可以打印字符串"hello csdn.”。

注意:常量字符串与普通字符串最大的区别是,常量字符串是不可被修改的字符串,既然不能被修改,那么在内存中没有必要存放两个一模一样的字符串,所以在内存中相同的常量字符串只有一个。

指针数组

指针数组也是数组,是用于存放指针的数组。

int* arr3[5];//数组arr3包含5个元素,每个元素是一个一级整型指针。

在这里插入图片描述
举例:

#include<iostream>
using namespace std;
int main()
{
	char a = 'm';
	char* p =&a;
	char* arr[1];
	arr[0] = p;
	cout << *arr[0] << endl;
	return 0;
}

我们定义了一个p指针指向字符a,将p存到了指针数组arr[0]中,然后对arr[0]解引用取到了字符’m’。

数组指针

数组指针就是指向数组的指针

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int(*p)[10] = &arr;
	//()优先级高,说明表示指针,是一个数组指针
	return 0;
}

&数组名 VS 数组名

对于一个数组的数组名,它什么时候代表数组首元素的地址,什么时候又代表整个数组的地址呢?

数组名代表整个数组的地址的情况其实只有两种:

  • &数组名。
  • 数组名单独放在sizeof内部,即sizeof(数组名)。

除此之外,所有的数组名都是数组首元素地址。

比如:

int arr[5] = { 1, 2, 3, 4, 5 };

对于该数组arr,只有以下两种情况数组名代表整个数组的地址:

&arr;
sizeof(arr);//arr单独放在sizeof内部

应用

#include<stdio.h>
int main()
{
	int arr[5] = { 1, 2, 3, 4, 5 };
	int* p1 = arr;//数组首元素的地址
	int(*p2)[5] = &arr;//数组的地址
	printf("%p\n", p1);
	printf("%p\n", p2);

	printf("%p\n", p1+1);
	printf("%p\n", p2+1);
	return 0;
}
  • 因为代码中的arr是数组首元素地址,所以要用int * 的指针接收。而&arr是整个数组的地址,所以要用数组指针进行接收。
  • 虽然一个是数组首元素地址,一个是数组的地址,但是它们存放的都是数组的起始位置的地址,所以将p1和p2以地址的形式打印出来发现它们的值一样。
  • 数组首元素地址和数组的地址的区别在于,数组首元素地址+1只能跳过一个元素指向下一个元素,而数组的地址+1能跳过整个数组指向数组后面的内存空间

在这里插入图片描述

数组参数、指针参数

一维数组传参

#include<stdio.h>
void test1(int arr[10])//数组接收
{}
void test1(int *arr)//指针接收
{}
void test2(int *arr[20])//数组接收
{}
void test2(int **arr)//指针接收
{}
int main()
{
	int arr1[10] = { 0 };//整型数组
	int *arr2[20] = { 0 };//整型指针数组
	test1(arr1);
	test2(arr2);
}

整型数组:
当向函数传入整型数组的数组名时,我们有以下几种参数可供接收:

  • 数组传参数组接收,我们传入的是整型数组,那我们就用整型数组接收。
  • 传入的数组名本质上是数组首元素地址,所以我们可以用指针接收。数组的元素类型是整型,我们接收整型元素的地址用int * 的指针即可。

整型指针数组:
当向函数传入整型指针数组的数组名时,我们有以下几种参数可供接收:

  • 数组传参数组接收,我们传入的是整型指针数组,那我们就用整型指针数组接收。
  • 指针接收,数组的元素是int * 类型的,我们接收int * 类型元素的地址用二级指针int ** 即可。

注意:一维数组传参,函数形参设计时[ ]内的数字可省略。

二维数组传参

#include<stdio.h>
void test(int arr[][5])//数组接收
{}
void test(int(*arr)[5])//指针接收
{}
int main()
{
	int arr[3][5] = { 0 };//二维数组
	test(arr);
}

当向函数传入二维数组的数组名时,我们有以下几种参数可供接收:

  • 二维数组传参二维数组接收。
  • 指针接收,二维数组的首元素是二维数组第一行的地址,即一维数组的地址,我们用数组指针接收即可。

注意:二维数组传参,函数形参的设计只能省略第一个[ ]内的数字。

一级指针传参

#include<stdio.h>
void print(int* p, int sz)//一级指针接收
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
}
int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;//一级指针
	print(p, sz);
	return 0;
}

二级指针传参

#include<stdio.h>
void test(int** p)//二级指针接收
{}
int main()
{
	int a = 10;
	int* pa = &a;
	int** paa = &pa;
	test(paa);//二级指针
	return 0;
}

当我们传入的参数为二级指针时,我们可以用二级指针的形参对其进行接收,那么当函数形参为二级指针的时候,我们可以传入什么样的参数呢?

#include<stdio.h>
void test(int** p)
{}
int main()
{
	int a = 10;
	int* pa = &a;
	test(&pa);//可以传入一级指针的地址
	int** paa = &pa;
	test(paa);//可以传入二级指针
	int* arr[10];
	test(arr);//可以传入一级指针数组的数组名
	//...
	return 0;
}

总而言之,只要传入的表达式最终的类型是二级指针类型即可传入。

函数指针

函数指针的定义

函数指针就是指向函数的指针。

和学习数组指针一样,学习函数指针我们也需要知道三点:

  • ( )的优先级要高于 * 。
  • 一个变量除去了变量名,便是它的变量类型。
  • 一个指针变量除去了变量名和 * ,便是指针指向的内容的类型。

例子:

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int(*p)(int, int) = &Add;//取出函数的地址放在函数指针p中
	return 0;
}

那么,函数指针p的类型我们是如何创建的呢?

  • 首先,p是一个指针,所以必须先与 * 结合,而( )的优先级高于 * ,所以我们要把 * 和p用括号括起来,让它们先结合。
  • 指针p指向的内容,即函数Add的类型是int (int,int),所以函数指针p就变成了int(*p)(int,int)。
  • 去掉变量名p后,便是该函数指针的变量类型int( * )(int,int)。

函数指针的使用

知道了如何创建函数指针,那么函数指针应该如何使用呢?
1.函数指针的赋值

  • 对于数组来说,数组名和&数组名它们代表的意义不同,数组名代表的是数组首元素地址,而&数组名代表的是整个数组的地址。
  • 但是对于函数来说,函数名和&函数名它们代表的意义却是相同的,它们都代表函数的地址(毕竟你也没有听说过函数有首元素这个说法吧)。
    所以,当我们对函数指针赋值时可以赋值为&函数名,也可以赋值为函数名。
int(*p)(int, int) = &Add;
int(*p)(int, int) = Add;

2.通过函数指针调用函数

方法一:我们知道,函数指针存放的是函数的地址,那么我们将函数指针进行解引用操作,便能找到该函数了,于是就可以通过函数指针调用该函数。

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 10;
	int b = 20;
	int(*p)(int, int) = &Add;
	int ret = (*p)(a, b);//解引用找到该函数
	printf("%d\n", ret);
	return 0;
}

方法二:我们在函数指针赋值中说到,函数名和&函数名都代表函数的地址,我们可以赋值时直接赋值函数名,那么通过函数指针调用函数的时候我们就可以不用解引用操作符就能找到函数了。

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 10;
	int b = 20;
	int(*p)(int, int) = Add;
	int ret = p(a, b);//不用解引用
	printf("%d\n", ret);
	return 0;
}

函数指针数组

函数指针数组的定义
我们知道,数组是一个存放相同类型数据的空间,我们已经认识了指针数组,比如:

int* arr[10];//数组arr有10个元素,每个元素的类型是int*

那如果要将一系列相同类型的函数指针存放到一个数组中,那么这个数组就叫做函数指针数组,比如:

int(*pArr[10])(int, int);
//数组pArr有10个元素,每个元素的类型是int(*)(int,int)

函数指针数组的创建只需在函数指针创建的基础上加上[ ]即可。

  • 比如,你要创建一个函数指针数组,这个数组中存放的函数指针的类型均为int(*)(int,int),如果你要创建一个函数指针为该类型,那么该函数指针的写法为int(*p)(int,int),现在你要创建一个存放该指针类型的数组,只需在变量名的后面加上[]即可,int(*pArr[10])(int,int)。

函数指针数组的使用 - 模拟计算器

函数指针数组一个很好的运用场景,就是计算机的模拟实现:

#include<stdio.h>
void menu()
{
	printf("|-----------------------|\n");
	printf("|     1.Add   2.Sub     |\n");
	printf("|     3.Mul   4.Div     |\n");
	printf("|        0.exit         |\n");
	printf("|-----------------------|\n");
}//菜单
double Add(double x, double y)
{
	return x + y;
}//加法函数
double Sub(double x, double y)
{
	return x - y;
}//减法函数
double Mul(double x, double y)
{
	return x*y;
}//乘法函数
double Div(double x, double y)
{
	return x / y;
}//除法函数
int main()
{
	int input = 0;
	double x = 0;//第一个操作数
	double y = 0;//第二个操作数
	double ret = 0;//运算结果
	double(*pArr[])(double, double) = { 0, Add, Sub, Mul, Div };
	//函数指针数组-转移表
	int sz = sizeof(pArr) / sizeof(pArr[0]);//计算数组的大小
	do
	{
		menu();
		printf("请输入:>");
		scanf("%d", &input);
		if (input == 0)
			printf("退出程序\n");
		else if (input > 0 && input < sz)
		{
			printf("请输入两个操作数:>");
			scanf("%lf %lf", &x, &y);
			ret = pArr[input](x, y);
			printf("ret=%lf\n", ret);
		}
		else
			printf("选择错误,请重新选择!\n");
	} while (input);//当input不为0时循环继续
	return 0;
}

代码中,函数指针数组存放的是一系列参数和返回类型相同的函数名,即函数指针。将0放在该函数指针数组的第一位是为了让用户输入的数字input与对应的函数指针下标相对应。
该代码若不使用函数指针数组,而选择使用一系列的switch分支语句当然也能达到想要的效果,但会使代码出现许多重复内容,而且当以后需要增加该计算机功能时又需要增加一个case语句,而使用函数指针数组,当你想要增加计算机功能时只需在数组中加入一个函数名即可。

指向函数指针数组的指针

既然存在函数指针数组,那么必然存在指向函数指针数组的指针。

int(*p)(int, int);
//函数指针
int(*pArr[5])(int, int);
//函数指针数组
int(*(*pa)[5])(int, int) = &pArr;
//指向函数指针数组的指针

pa就是一个指向函数指针数组的指针,该函数指针数组中每个元素类型是int(*)(int, int)。

回调函数

回调函数的定义
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

举个简单的例子:

void test1()
{
	printf("hello\n");
}
void test2(void(*p)())
{
	p(); //指针p被用来调用其所指向的函数
}
int main()
{
	test2(test1);//将test1函数的地址传递给test2
	return 0;
}

在该代码中test1函数不是由该函数的实现方直接调用,而是将其地址传递给test2函数,在test2函数中通过函数指针间接调用了test1函数,那么函数test1就被称为回调函数。

回调函数的使用 - qsort函数

其实回调函数并不是很难见到,在用于快速排序的库函数qsort中便运用了回调函数。

void qsort(void*base,size_t num,size_t width,int(*compare)(const void*e1,const void*e2));
  • qsort函数的第一个参数是待排序的内容的起始位置;第二个参数是从起始位置开始,待排序的元素个数;第三个参数是待排序的每个元素的大小,单位是字节;第四个参数是一个函数指针。qsort函数的返回类型为void。
  • qsort函数的第四个参数是一个函数指针,该函数指针指向的函数的两个参数的参数类型均为const void*,返回类型为int。当参数e1小于参数e2时返回小于0的数;当参数e1大于参数e2时返回大于0的数;当参数e1等于参数e2时返回0。

列如,我们要排一个整型数组:

#include<stdio.h>
int compare(const void* e1, const void* e2)
{
	return *((int*)e1) - *((int*)e2);
}//自定义的比较函数
int main()
{
	int arr[] = { 2, 5, 1, 8, 6, 10, 9, 3, 5, 4 };
	int sz = sizeof(arr) / sizeof(arr[0]);//元素个数
	qsort(arr, sz, 4, compare);//用qsort函数将arr数组排序
	return 0;
}

最终arr数组将被排为升序。

注意:qsort函数默认将待排序的内容排为升序,如果我们要排为降序可将自定义的比较函数的两个形参的位置互换一下即可。

在qsort函数中我们传入了一个函数指针,最终qsort函数会在其内部通过该函数指针调用该函数,那么我们的这个自定义比较函数就被称为回调函数。

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

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

相关文章

澳亚集团在港交所上市:IPO首日破发,毛利率走低,盈利能力下滑

12月30日&#xff0c;澳亚集团有限公司&#xff08;下称“澳亚集团”&#xff0c;HK:02425&#xff09;在港交所上市。本次上市&#xff0c;澳亚集团的发行价为6.40港元/股&#xff0c;发行数量为3064万股&#xff0c;募资总额约为1.96亿港元&#xff0c;募资净额约为1.01亿港元…

DoIP协议从入门到精通——DoIP企业规范常见疑问解答

基于自己在做DoIP测试过程中遇到的一些让自己困惑的点,汇总后与你分享,期望有所帮助。 具体内容如下: 1、IP地址 背景信息: 在通常Tester与车辆进行诊断通信时,Tester端设置好自己的IP地址信息,而对于待测ECU IP地址信息通常不设置,或者说不知道怎么设置。 穿拖鞋的…

ext4 extent详解1之示意图演示

本文将从内核源码、实例演示等角度详细ext4 extent B树的前世今生&#xff0c;希望看过本文的读者从理解ext4 extent的工作原理。内核版本3.10.96&#xff0c;详细内核详细源码注释见GitHub - dongzhiyan-stack/kernel-code-comment: 3.10.96 内核源代码注释。 1 ext4 extent由…

题库自定义配置,满足各种使用习惯,专治强迫症

目录 自定义题型 自定义选项 章节、难度增加排序属性 下一个版本预告 根据吐槽社区的反馈情况&#xff0c;没想到居然有台湾同胞使用土著刷题微信小程序&#xff0c;鉴于与内地的试题题目有所不同&#xff0c;所以土著刷题微信小程序v1.9主要迭代了题库自定义相关配置。 自…

架构师课程笔记day03——单体应用开发过程中常用知识点及注意事项

1.自定义异常捕获处理 定义如下类 可捕获并处理相关异常 package com.imooc.exception;import com.imooc.utils.IMOOCJSONResult; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice;…

第十二篇 1+X考证 Web前端测试题PHP篇(新)

单选题 1、以下关于PHP面向对象的说法错误的是&#xff08; A &#xff09; A、PHP可以多重继承&#xff0c;一个类可以继承多个父类 B、PHP使用new运算符来获取一个实例对象 C、一个类可以在声明中用extends关键字继承另一个类的方法和属性 D、PHP默认将var关键字解释为pu…

数据源支持

数据源支持目录概述需求&#xff1a;设计思路实现思路分析1.AT模式2.TCC模式3.Saga模式4.XA模式参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait for ch…

【ROS】—— ROS快速上手

文章目录前言1. ROS-melodic 安装2. ROS基本操作2.1 创建工作空间2.2 创建功能包2.3 HelloWorld(C版)2.4 HelloWorld(Python版)3. Vscode ROS 插件4. vscode 使用基本配置4.1 启动 vscode4.2 vscode 中编译 ros5. launch文件演示6. ROS文件系统7. ROS文件系统相关命令前言 &…

沸腾过程气泡成核OVITO渲染

《Lammps空间划分——识别气泡、三维裂隙》 根据博文对纳米气泡的筛选&#xff08;获得select&#xff09;&#xff0c;本案例练习如何渲染气泡。 文章目录一、选择气泡&#xff08;select&#xff09;二、删除液体三、渲染液体四、巧妙利用原子半径大小五、气泡渲染并获得体积…

elasticsearch 7.9.3知识归纳整理(一)之es,kibana,ik的下载安装

es&#xff0c;kibana&#xff0c;ik的下载安装 下载地址 es下载地址&#xff1a;https://www.elastic.co/cn/downloads/elasticsearch kibana下载地址&#xff1a; https://www.elastic.co/cn/downloads/kibana ik中文分词器下载地址&#xff1a;https://github.com/medcl/el…

我的周刊(第072期)

我的信息周刊&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。&#x1f3af; 项目duplicati[1]Duplicati 是一个免费的开源备份客户端…

力扣(LeetCode)364. 加权嵌套序列和 II(2022.12.31)

给你一个整数嵌套列表 nestedList &#xff0c;每一个元素要么是一个整数&#xff0c;要么是一个列表&#xff08;这个列表中的每个元素也同样是整数或列表&#xff09;。 整数的 深度 取决于它位于多少个列表内部。例如&#xff0c;嵌套列表 [1,[2,2],[[3],2],1] 的每个整数的…

回归原型网络代码episode数据加载

一般PyTorchPyTorchPyTorch加载数据的固定格式是: dataset MyDataset() : 构建DatasetDatasetDataset对象 dataLoader DataLoader(dataset) #通过DataLoaderDataLoaderDataLoader来构造迭代对象. num_epoches 100 for epoch in range(num_epoches): #逐步迭代数据 for img,l…

元宇宙产业委评选2022全球元宇宙十大事件(含国外元宇宙五大事件)

中国移动通信联合会元宇宙产业工作委员会&#xff08;简称为&#xff1a;元宇宙产业委&#xff09; 评选2022全球元宇宙十大事件&#xff08;含国外元宇宙五大事件&#xff09; 1、1月5日&#xff0c;CES 2022上&#xff0c;英伟达&#xff08;NVIDIA&#xff09;宣布旗下元宇…

【django】HttpRequest对象的属性和路由补充

文章目录一、HttpRequest对象的常用属性1、request.GET&#xff1a;获取查询字符串参数案例:特别注意&#xff1a;2、request.POST&#xff1a;post请求数据&#xff0c;只能获取表单参数3、request.body&#xff1a;请求body&#xff0c;响应结果为字节类型4、request.method&…

一文搞懂G1垃圾回收器

G1是从JDK9之后的默认垃圾回收器&#xff0c;其功能强大&#xff0c;性能优异&#xff0c;不过目前市面的材料不算多&#xff0c;很多都是抄来抄去&#xff0c;讲得也不太清楚。经过仔细阅读oracle官网以及相关的材料&#xff0c;从整体上梳理了G1的过程&#xff0c;希望这一文…

数据库设计规范详解

对于后端开发人员&#xff0c;建表是个基础活&#xff0c;是地基&#xff0c;如果地基不大牢固&#xff0c;后面在程序开发过程中会带来很多麻烦&#xff0c;在建表的时候不注意细节&#xff0c;等后面系统上线之后&#xff0c;表的维护成本变得非常高&#xff0c;而且很容易踩…

基数排序分析

&#x1f954; 原理介绍&#xff1a; [排序算法] 基数排序 (C) - Amαdeus - 博客园 前述的各类排序方法都是建立在关键字比较的基础上&#xff0c;而基数排序是一种非比较型整数排序算法。它的基本思想是将整数按位数切割成不同的数字&#xff0c;然后按每个位数分别比较。 …

单片机基础知识之定时计数器和寄存器

目录 一、定时计数器 二、什么是寄存器 三、定时器如何定时10毫秒 四、定时器编程前寄存器配置计划 五、编程定时器控制LED每隔一秒亮灭 一、定时计数器 1、定时计数器的概念引入 定时器和计数器&#xff0c;电路一样 定时或者计数的本质就是让单片机某个部件数数 当定…

Linux基础------高级IO

文章目录阻塞IO非阻塞IO信号驱动异步IO多路转接&#xff08;核心终点&#xff09;实际上 IO “等” 拷贝 等什么呢&#xff1f; -----> 等待的是内核将数据准备好。 拷贝-------> 数据从内核考到用户 IO话题&#xff1a; 无非就是 1 &#xff0c; 改变等的方式 2 &…