C语言学习之路(基础篇)—— 指针(上)

news2024/11/20 0:49:11

说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!

概述

1) 内存

内存含义:

  • 存储器: 计算机的组成中,用来存储程序和数据,辅助CPU进行运算处理的重要部分。
  • 内存: 内部存贮器,暂存程序/数据——掉电丢失 SRAM、DRAM、DDR、DDR2、DDR3
  • 外存: 外部存储器,长时间保存程序/数据—掉电不丢ROM、ERRROM、FLASH(NAND、NOR)、硬盘、光盘

内存是沟通CPU与硬盘的桥梁:

  • 暂存放CPU中的运算数据
  • 暂存与硬盘等外部存储器交换的数据

2) 物理存储器和存储地址空间

有关内存的两个概念:物理存储器 和 存储地址空间。

物理存储器:实际存在的具体存储器芯片。

  • 主板上装插的内存条
  • 显示卡上的显示RAM芯片
  • 各种适配卡上的RAM芯片和ROM芯片

存储地址空间:对存储器编码的范围。我们在软件上常说的内存是指这一层含义。

  • 编码:对每个物理存储单元(一个字节)分配一个号码
  • 寻址:可以根据分配的号码找到相应的存储单元,完成数据的读写

3) 内存地址

  • 将内存抽象成一个很大的一维字符数组。
  • 编码就是对内存的每一个字节分配一个32位或64位的编号(与32位或者64位处理器相关)。
  • 这个内存编号我们称之为内存地址。

内存中的每一个数据都会分配相应的地址:

  • char:占一个字节分配一个地址
  • int: 占四个字节分配四个地址
  • float、struct、函数、数组等
    在这里插入图片描述

4) 指针和指针变量

指针: 指针 === 地址 === 编号
指针变量: 存放指针(地址)的变量

  • 内存区的每一个字节都有一个编号,这就是“地址”。
  • 如果在程序中定义了一个变量,在对程序进行编译或运行时,系统就会给这个变量分配内存单元,并确定它的内存地址(编号)
  • 指针的实质就是内存“地址”。指针就是地址,地址就是指针。
  • 指针是内存单元的编号,指针变量是存放地址的变量。
  • 通常我们叙述时会把指针变量简称为指针,实际他们含义并不一样。

在这里插入图片描述

指针基础知识

1) 指针变量的定义和使用

  • 指针也是一种数据类型,指针变量也是一种变量
  • 指针变量指向谁,就把谁的地址赋值给指针变量
  • *”操作符操作的是指针变量指向的内存空间

定义指针的三步骤:
1、 *与符号结合代表是一个指针变量
2、 要保存谁的地址,将他的定义形式放在此处
3、 用*p替换掉定义的变量

int a = 10;
// 1、定义*p指针变量
// 2、int a ---->  
// 3、int a ----> int *p  // 指针变量p

分析:
1、与*结合代表这个一个指针变量
2、p是变量,p的类型是将变量p本身拖黑,剩下的类型就是指针变量的类型 int *
3、指针变量p用来保存什么类型数据的地址 ,将指针变量p和指针变量p最近的*一起拖黑,剩下什么类型就保存什么类型数据的地址

int a = 10;
int *p = &a;

示例:

#include <stdio.h>

int main()
{
	int a = 10;
	char b = 97;
	printf("%p, %p\n", &a, &b); //打印a, b的地址

	//int *代表是一种数据类型,int*指针类型,p才是变量名
	//定义了一个指针类型的变量,可以指向一个int类型变量的地址
	int* p;
	p = &a; //将a的地址赋值给变量p,p也是一个变量,值是一个内存地址编号
	printf("%d\n", *p); //p指向了a的地址,*p就是a的值

	char* p1 = &b;
	printf("%c\n", *p1); //*p1指向了b的地址,*p1就是b的值,%c打印字符

	return 0;
}

在这里插入图片描述

在这里插入图片描述

注意:&可以取得一个变量在内存中的地址。但是,不能取寄存器变量,因为寄存器变量不在内存里,而在CPU里面,所以是没有地址的。

2) 通过指针间接修改变量的值

#include <stdio.h>


int main() {

	int a = 10;
	char b = 97;

	int* p = &a;
	char* p1 = &b;

	printf("a=%d, *p=%d\n", a, *p);  // a=10, *p=10
	printf("b=%c, *p1=%c\n", b, *p1);  // b=a, *p1=a
	// 指针变量保存谁的地址就指向了谁
	*p = 100; // 在使用时,*与p结合代表,取p指针所指向那块空间的内容
	*p1 = 65;

	printf("a=%d, *p=%d\n", a, *p);  // a=100, *p=100
	printf("b=%c, *p1=%c\n", b, *p1);  // b=A, *p1=A
	return 0;
}

在这里插入图片描述

重点:星花*与取地址& ,在给变量赋值时,等号两边的表达式类型应当要匹配一致;int* p = &a;亦是如此。

在使用时,对一个表达式取*,就会对表达式减一级*,如果对表达式取&,就会加一级*

int a = 10;
//在使用时,对一个表达式取`*`,就会对表达式减一级`*`,如果对表达式取`&`,就会加一级`*`

// 左边表达式p为int*类型;右边的表达式a为int类型那么加上`&`,对表达式取`&`,就会加一级`*,即int *类型;左右两边类型匹配
int* p;
p = &a;

// 左边表达式p为int*类型,取`*`,则减一级`*`即int类型;右边100为int类型;左右两边类型匹配
*p = 100;

int* t;
int** g;
// 左边表达式g为int**类型;右边表达式t为int*类型,取`&`就会加一级`*`即为int**类型;左右两边类型匹配
g = &t;
// 左边表达式g为int**类型,取`*`,则减一级`*`即int*类型;右边表达式t为int*类型;左右两边类型匹配
*g = t;

3) 指针大小

  • 使用sizeof()测量指针的大小,得到的总是:48
  • sizeof()测的是指针变量指向存储地址的大小
  • 32位平台,所有的指针(地址)都是32位(4字节)
  • 64位平台,所有的指针(地址)都是64位(8字节)
#include <stdio.h>

int main() {
	// 不管什么类型的指针,大小只和系统编译器有关系
	char* p1;
	char** p2;
	short* p3;
	int* p4;
	int** p5;
	float* p6;
	double* p7;
	printf("%d\n", sizeof(p1));
	printf("%d\n", sizeof(p2));
	printf("%d\n", sizeof(p3));
	printf("%d\n", sizeof(p4));
	printf("%d\n", sizeof(p5));
	printf("%d\n", sizeof(p6));
	printf("%d\n", sizeof(p7));
	printf("%d\n", sizeof(long*));

	return 0;
}

编译器X86运行结果:

在这里插入图片描述

编译器X64运行结果:

在这里插入图片描述

因为32位编译器内存地址编号范围是0x0000 0000 - 0xffff ffff如此,所以我们的指针变量占4个字节就可以存下;而64位编译器内存地址编号范围是0x0000 0000 0000 0000 - 0xffff ffff ffff ffff 这样的编号,需要8个字节才能存下,所以指针变量也需要8个字节

4) 指针的宽度和步长

  • 不同类型的指针变量,取指针指向的内容的宽度
  • 指针的宽度 = sizeof(将指针变量与指针变量最近的*拖黑,剩下的类型;如char *p; sizeof(char);占一个字节; int **p; sizeof(int*);占4个字节)
  • 宽度也叫做步长
  • 步长:指针加1跨过多少个字节

示例1:

#include <stdio.h>

int main(){

	int num = 0x01020304;  // 刚好四个字节的数据
	char* p1 = (char*)&num;  // 类型不匹配强转
	short* p2 = (short*)&num; // 类型不匹配强转
	int* p3 = &num;

	//通过*取指针变量所指向那块空间内容时,取的内存的宽度和指针变量本身的类型有关
	printf("p1=%x\n", *p1);  // 04  1个字节
	printf("p2=%x\n", *p2);  // 0304  2个字节 
	printf("p3=%x\n", *p3);  // 01020304  4个字节
	return 0;
}

在这里插入图片描述

在这里插入图片描述

示例2:

#include <stdio.h>

int main() {

	int num = 0x01020304;  
	char* p1 = (char*)&num;  
	short* p2 = (short*)&num; 
	int* p3 = &num;

	printf("p1=%u\n", p1);  // p1=7601028
	printf("p2=%u\n", p2);  // p2=7601028
	printf("p3=%u\n", p3);  // p3=7601028
	printf("\n");
	printf("p1=%u\n", p1+1);  // p1=7601029       p1是char类型的指针,+1跨过1个字节
	printf("p2=%u\n", p2+1);  // p1=7601030       p2是short类型的指针,+1跨过2个字节
	printf("p3=%u\n", p3+1);  // p1=7601032       p3是int类型的指针,+1跨过4个字节


	return 0;
}

在这里插入图片描述

5) 野指针和空指针

5.1 野指针

指针变量也是变量,是变量就可以任意赋值,不要越界即可(32位为4字节,64位为8字节),但是任意数值赋值给指针变量没有意义,因为这样的指针就成了野指针,此指针指向的区域是未知(操作系统不允许操作此指针指向的内存区域)。所以,野指针不会直接引发错误,操作野指针指向的内存区域才会出问题

错误示例:

	int a = 100;
	int* p;
	p = a; //把a的值赋值给指针变量p,p为野指针, ok,不会有问题,但没有意义
	int* p;
	p = 0x12345678; //给指针变量p赋值,p为野指针, ok,不会有问题,但没有意义
	int* p;
	*p = 1000;  //操作野指针指向未知区域,内存出问题,err

野指针就是没有初始化的指针,指针的指向是随机的,不可以操作野指针。

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

正确示例:
指针p保存的地址一定是定义过的(向系统申请过的)。

	int a = 100;
	int* p = &a;
	*p = 1000; //p指向了a的地址,*p就是a的值,给*p赋值1000,那么a的值也是1000

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

5.2 空指针

野指针和有效指针变量保存的都是数值,为了标志此指针变量没有指向任何变量(空闲可用),C语言中,可以把NULL赋值给此指针,这样就标志此指针为空指针,没有任何指针。

#include <stdio.h>


int main() {
	int a = 100; // 整型变量的初始化
	// 将指针的值赋值为0 即0x00000000 =  NULL
	int* p = NULL; // 因为p保存了0x0000的地址,这个地址是程序初始地址不可以使用的,非法

	*p = 1000;
	printf("%d", *p);

	return 0;
}

在这里插入图片描述

在这里插入图片描述

既然不能使用,为什么还要初始化为NULL呢,赋值为NULL主要用于标记,来判断该指针是否被使用,避免它成为一个野指针。

#include <stdio.h>


int main() {
	int a = 100; // 整型变量的初始化
	// 将指针的值赋值为0 即0x00000000 =  NULL
	int* p = NULL; // 因为p保存了0x0000的地址,这个地址是程序初始地址不可以使用的,非法
	// 如果p等于NULL,说明没有被使用,那么就可以进行赋值操作
	if (p == NULL)
	{
		p = &a;
		*p = 1000;
	}
	else
	{
		// 说明p有指向,被使用
		// 养成好的习惯,每次使用完指针,就给赋值为NULL
		// int* p = NULL;
	}
	printf("%d %d", *p, a);

	return 0;
}

在这里插入图片描述

6) 万能指针void *

void*万能指针可以指向任意变量的内存空间:

错误示例:

#include <stdio.h>

int main() {

	int a = 10;
	void* p = (void*)&a;  // a为int类型,取&,加一级*,所以为int*类型,int*类型不匹配void*类型,所以要进行强转
	printf("%d", *p);

	return 0;
}

运行以上代码,提示错误

在这里插入图片描述

导致以上错误原因是:我们不知道*p应该取多少个字节的数据

在这里插入图片描述

就好比定义void类型的变量一个道理,因为编译器不知道该给此变量类型分配多大的空间;但是定义void*类型没有问题,因为指针类型数据要么4个字节要么8个字节(取决于编译器)

在这里插入图片描述

目前程序上是知道*p指针变量应该取哪里的地址,只是不知道应该取多少个字节数据而已,那么我们可以通过转换类型的方式去获取地址数据。(如:*p 中的p指向a的地址,p的类型是void*,你要取多少个字节数据,就转为什么类型即可;我要取4个字节的数据,那么就将p转为int*类型即可)

#include <stdio.h>

int main() {

	int a = 10;
	//void b = 20; // error 不可以定义void类型的变量,因为编译器不知道给变量分配多大的空间
	// 但是可以定义void* 类型, 因为指针类型数据要么4个字节要么8个字节(取决于编译器)
	void* p = (void*)&a;  // a为int类型,取&,加一级*,所以为int*类型,int*类型不匹配void*类型,所以要进行强转

	//printf("%d\n", *p); //error p是void*类型,编译器不知道取几个字节的大小

	// *p 中的p指向a的地址,p的类型是void* 类型,你要取4个字节数据,那么将p转为int*类型即可解决
	printf("%d\n", *(int*)p);
	// 同理q的类型是void* 类型,我要取2个字节的数据,那么我就将q转为short*类型即可
	printf("%d", *(short*)p);

	return 0;
}

在这里插入图片描述

7) const修饰的指针变量

const是一个C语言的关键字,具有着举足轻重的地位。它限定一个变量不允许被改变,产生静态作用。使用const在一定程度上可以提高程序的安全性和可靠性。另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一定帮助。

const修饰变量a后,不能再通过变量a去修改a所指向内存空间里面的内容

	// const 修饰变量a
	const int a = 10;
	a = 100;  // error 修饰变量a后,不能再通过变量a去修改a所指向空间里面的内容

但可以通过指针变量*p去修改a地址的内容

	// const 修饰变量a
	const int a = 10;
	int* p = &a;
	*p = 100;

在这里插入图片描述

const修饰指针变量*p后,不能再通过*p去修改变量p所指向a空间里面的内容

	int a = 10;
	// 这里const修饰的是*,不能通过*p去修改p所指向空间的内容
	const int* p = &a;
	*p = 100;  // error 不能通过*p去修改变量p所指向a空间里面的内容

在这里插入图片描述

const修饰变量p后,变量p本身的值不能被更改

	int a = 10;
	int b = 20;
	// const修饰的是变量p,p保存的地址不可以修改
	int* const p = &a;
	p = &b; // error 变量p本身的值不能被更改

在这里插入图片描述

const修饰的是变量p*p本身的指向不能改变,不能通过*p去修改p所指向空间的内容

	int a = 10;
	int b = 20;
	// const修饰的是变量p和*,p本身的指向不能改变,不能通过*p去修改p所指向空间的内容
	const int* const p = &a;
	p = &b; // error 变量p本身的值不能被更改
	*p = 100; // error 不能通过*p去修改变量p所指向a空间里面的内容

多级指针

  • C语言允许有多级指针存在,在实际的程序中一级指针最常用,其次是二级指针。
  • 二级指针就是指向一个一级指针变量地址的指针。
  • 三级指针基本用不着,但考试会考。
#include <stdio.h>

int main() {

	int a = 10;
	// *p > int a > int (*p) > int *p
	int* p = &a;
	// *q > int *p > int *(*q) > int **q
	//如果*和&相遇,相抵消
	// **q == *(*q) == *(p) ==  a
	// **q == *(*q) == *(&a) ==  a
	int** q = &p;
	// *k > int **q > int **(*k) > int ***k
	int*** k = &q;
	// *符号结合,代表这个k是一个指针变量
	// k是一个变量
	// k的类型,将变量k拖黑,剩下的类型
	// k用来保存谁的地址  将变量k和k最近的*一起拖黑,剩下什么类型
	// 就保存什么类型数据的地址
	
	printf("%d\n", *p); //10
	printf("%d\n", **q); //10
	printf("%d\n", ***k); //10
	return 0;
}

在这里插入图片描述

定义多级指针保存数据的地址时,定义的指针的类型只需要比要保持的数据的类型多一级*即可

	// 定义多级指针保存数据的地址时,定义的指针的类型只需要比要保持的数据的类型多一级`*`即可
	int******************* g;
	int******************** f = &g;

指针和数组

1) 数组名

数组名字是数组的首元素地址,但它是一个常量:

#include <stdio.h>

int main() {

	int a[10] = { 3,9,5,1,4,7,6,10,2,8 };
	//a = 10; //err, 数组名只是常量,不能修改
	printf("a = %p\n", a); // a = 0073F904
	printf("&a[0] = %p\n", &a[0]); // &a[0] = 0073F904

	return 0;
}

2) 指针操作数组元素

在没有学习指针之前,我们打印数组元素,是这样子打印的

#include <stdio.h>


int main() {

	int a[10] = { 3,9,5,1,4,7,6,10,2,8 };
	//a = 10; //err, 数组名只是常量,不能修改
	//printf("a = %p\n", a); // a = 0073F904
	//printf("&a[0] = %p\n", &a[0]); // &a[0] = 0073F904
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		printf("%d ", a[i]); //先打印原始元素
		a[i] = i + 1; // 后赋值
	}
	printf("\n");
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		printf("%d ", a[i]); //打印赋值后的元素
	}
	return 0;
}

在这里插入图片描述

在学习指针后,我们可以通过指针来操作数组元素

  • 指针加1,跨过一个步长 如:int *p; 的步长为sizeof(int) = 4Byte
  • 要得到内存的数据,就该先得到数据的地址
  • *(地址) 得到的是地址里面的内容
    在这里插入图片描述
#include <stdio.h>

int main() {

	int a[10] = { 3,9,5,1,4,7,6,10,2,8 };
	//a 数组名,首元素的地址
	int* p = a; // 指针p保存的是首元素的地址
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		printf("%d ", *(p+i)); //先打印原始元素
		*(p + i) = i + 1; // 后赋值
	}
	printf("\n");
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		printf("%d ", a[i]); //打印赋值后的元素
	}
	return 0;
}
输出结果
3 9 5 1 4 7 6 10 2 8
1 2 3 4 5 6 7 8 9 10

3) 指针加减运算

3.1 加法运算

  • 指针计算不是简单的整数相加
  • 如果是一个int *+1的结果是增加一个int的大小
  • 如果是一个char *+1的结果是增加一个char的大小
#include <stdio.h>

int main()
{
	int a;
	int* p = &a;
	printf("%d\n", p); // 9435892
	p += 2;//移动了2个int
	printf("%d\n", p); // 9435900

	char b = 0;
	char* p1 = &b;
	printf("%d\n", p1);	// 9435871
	p1 += 2;//移动了2个char
	printf("%d\n", p1); // 9435873

	return 0;
}

通过改变指针指向操作数组元素:

#include <stdio.h>

int main()
{
	int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	int i = 0;
	int n = sizeof(a) / sizeof(a[0]);

	int* p = a;
	for (i = 0; i < n; i++)
	{
		printf("%d, ", *p);
		p++;
	}
	printf("\n");

	return 0;
}
输出结果
1, 2, 3, 4, 5, 6, 7, 8, 9,

两指针相加没有意义:单个指针加法运算如上面开头那种p += 2;没有问题;但是两个指针相加没有任何意义,比如p指针指向第一个元素地址内容,q指针指向最后一个元素地址内容,那么结果只是一个很大的数,无其他任务实质意义。

#include <stdio.h>

int main() {

	// 整个数组的步长 = sizeof(int [10]) == sizeof(a) == 10*4 = 40;
	int a[10] = { 3,9,5,1,4,7,6,10,2,8 };
	int* p = a; // &a[0]
	int* q = (int*)(&a + 1) - 1; // 等同于int *q = &a[9]

	// 两指针相加没有意义
	printf("%d\n", p + q);


	return 0;
}

在这里插入图片描述

3.2 减法运算

示例1:通过改变指针指向操作数组元素

#include <stdio.h>
// 指针加法运算
int main()
{
	int a[10] = { 3,9,5,1,4,7,6,10,2,8 };
	int i = 0;
	int n = sizeof(a) / sizeof(a[0]);

	int* p = a + n - 1;  // p指向最后一个元素地址,*p则就是取最后一个元素的地址内容
	for (i = 0; i < n; i++)
	{
		printf("%d, ", *p);
		p--;
	}
	printf("\n");

	return 0;
}
输出结果
8, 2, 10, 6, 7, 4, 1, 5, 9, 3,

示例2:两指针(类型一致)相减,得到的是中间跨过多少个元素

#include <stdio.h>

int main() {
	// 两指针相减
	// 整个数组的步长 = sizeof(int [10]) == sizeof(a) == 10*4 = 40;
	int a[10] = { 3,9,5,1,4,7,6,10,2,8 };
	int* p = a; // &a[0]
	// 通过地址取数组最后一个元素,那么&a+1则表示横跨整个数组,就是41,取地址则需要转为int*,最后才能得到41对应的地址,最后地址-1就得到最后一个元素地址
	int* q = (int*)(&a + 1) - 1; // 等同于int *q = &a[9]
	printf("%d\n", q-p); // 9
	// 验证是q-p是否跨了9个元素,直接*取q的地址内容即可
	printf("%d\n", *(p+9)); // 8
	
	return 0;
}
输出结果
9
8

4) 方括号不是数组的专属

  • []并不是数组的专属
  • []实际上是*()的缩写

示例1:

#include <stdio.h>


int main() {
	// `[]`并不是数组的专属
	int a;
	int* p = &a;
	// [] == *()
	// p[0] == *(p+0) == *p
	p[0] = 100;

	printf("%d", a); // 100

	return 0;

}

示例2:

#include <stdio.h>

int main() {
	// `[]`并不是数组的专属
	int a;
	int* p = &a;
	// [] == *()
	// p[0] == *(p+0) == *p
	p[0] = 100;
	p[1] = 200; // error 内存污染,p[1] == *(p+1)  p+1跨过一个元素,指向的是a后面的地址,
	            //取这块地址里面的内容不能进行操作,即使显示没有问题,但是运行编译会出错

	printf("%d", a);

	return 0;
}

在这里插入图片描述

示例3:

int main() {
	// `[]`并不是数组的专属
	// [] == *()
	int a[10] = { 3,9,5,1,4,7,6,10,2,8 };
	int* p = a;
	
	for (int i = 0; i < sizeof(a)/sizeof(a[0]); i++)
	{
		// 第一种方式
		printf("%d ", a[i]);
		// 第二种方式
		printf("%d ", *(p+i));
		// 第三种方式
		printf("%d ", p[i]); // p[i] == *(p+i)
		// 第四种方式
		printf("%d ", *(a+i)); // a[i] == *(a+i)  (a+i)首元素地址阔过第i个元素,等到该元素的地址,*取该元素地址的内容

	}

	return 0;

}
输出结果
3 3 3 3 9 9 9 9 5 5 5 5 1 1 1 1 4 4 4 4 7 7 7 7 6 6 6 6 10 10 10 10 2 2 2 2 8 8 8 8

5) 指针数组

整型数组,是一个数组,数组的每一个元素都是整型。
指针数组,它也是数组,数组的每一个元素都是指针。

示例1:通过指针数组保存多个变量的地址,并打印指向变量地址的内容

#include <stdio.h>

int main() {

	int a = 10;
	int b = 20;
	int c = 30;
	// int *p1 = &a; int *p2 = &b; int *p3 = &c;
	int* num[3] = { &a, &b, &c };

	for (int i = 0; i < sizeof(num)/sizeof(num[0]); i++)
	{
		printf("%d ", *num[i]); // *num[i]  []优先级高于*
	}
	return 0;
}

在这里插入图片描述

输出结果
10 20 30

示例2:定义一个指针来保存数组num首元素的地址,并通过指针变量打印出指针数组中的所有元素

#include <stdio.h>

int main() {

	int a = 10;
	int b = 20;
	int c = 30;
	// int *p1 = &a; int *p2 = &b; int *p3 = &c;
	int* num[3] = { &a, &b, &c };
	// 定义一个指针用来保存数组num首元素的地址
	// num首元素地址 num == &num[0]
	// 定义指针的类型 int **  首先num[0]是int *类型,要保存int  *类型的地址,就需要比它多一级*
	int** k = &num[0];
	printf("%d\n", **k);
	for (int i = 0; i < sizeof(num)/sizeof(num[0]); i++)
	{
		printf("%d ", **(k+i));  //这里的括号不能去除,去除后就变成了10+i了
	}
	return 0;
}

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

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

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

相关文章

python切分TXT的句子到Excel(复制可用)——以及python切分句子遇到的问题汇总

文章目录完整代码时间转化和提取各种对象类型转换时间序列类属性数据转换完整代码 import jieba.analyseimport jieba.posseg as pseg from wordcloud import WordCloud import xlsxwriter # encodinggbk import xlsxwriterf open(E:/data/xieyangteng/review.txt, r, encodi…

波的相关参数概念整理

频率&#xff08;frequency&#xff09;&#xff0c;符号f&#xff0c;表示单位时间内完成周期性变化的次数。f1/T&#xff0c;单位s-1 角频率&#xff0c;符号ω&#xff0c;表示单位时间内变化的角弧度值。ω 2πf 2π/T,单位rad/s 波长&#xff08;wavelength&#xff0…

<SQL编程工具MySQL、SQLyog安装及环境配置教程>——《SQL》

目录 1.MySQL安装&#xff1a; 1.1 MySQL下载安装&#xff1a; 1.2 MySQL环境变量配置&#xff1a; 2.SQLyog安装&#xff1a; 2.1 SQLyog下载安装&#xff1a; 3.写在最后的话&#xff1a; 后记&#xff1a;●由于作者水平有限&#xff0c;文章难免存在谬误之处&…

力扣刷题day49|647回文子串、516最长回文子序列

文章目录647. 回文子串思路暴力解法动态规划五部曲516. 最长回文子序列思路动态规划五部曲647. 回文子串 力扣题目链接 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由…

代码随想录算法训练营第一天|LeetCode704二分查找、LeetCode27移除元素

LeetCode704二分查找 题目链接&#xff1a;704二分查找 思路&#xff1a; 以前刷过不少题&#xff0c;也看过不少题解&#xff0c;就记得区间有不少原则&#xff0c;乍一想有哪些想不起来了&#xff0c;反正我是选择了最简单易懂的左闭右闭原则。 1、区间左闭右闭原则。 2、w…

SpringBoot SpringBoot 开发实用篇 2 配置高级 2.3 常用计量单位应用

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇2 配置高级2.3 常用计量单位应用2.3.1 问题引入2.3.2 常用计量单位应…

实验2 存储器设计与实现【计算机组成原理】

实验2 存储器设计与实现【计算机组成原理】实验2 存储器设计与实现一、实验目的二、实验环境三、实验原理四、实验任务五、实验结果&#xff1a;六、心得体会&#xff1a;实验2 存储器设计与实现 一、实验目的 掌握单端口RAM和ROM原理和设计方法。掌握32位数据的读出和写入方…

【LeetCode-中等】343. 整数拆分(详解)

题目 给定一个正整数 n &#xff0c;将其拆分为 k 个 正整数 的和&#xff08; k > 2 &#xff09;&#xff0c;并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 。 力扣&#xff1a;题目链接 方法1&#xff1a;动态规划 完全不了解动态规划&#xff1f; 动态规划…

图像运算和图像增强九

图像运算和图像增强九 图像锐化之 Roberts、Prewitt 算子实现边缘检测 &#xff08;1&#xff09;图像锐化 图像锐化的目的是为了使图像的边缘、轮廓线以及图像的细节变得清晰&#xff0c;经过平滑的图像变得模糊的根本原因是图像受到了平均或积分运算&#xff0c;因此可以对…

LeetCode刷题(python版)——Topic70. 爬楼梯

一、题设 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;2 解释&#xff1a;有两种方法可以爬到楼顶。 1. 1 阶 1 阶 2. 2 阶 示例…

Flutter混编之路IOS插件0基础开发(mac环境)

本文默认你安装了Android Studio、Xcode具备flutter开发环境&#xff0c;并且会dart语言的基础使用&#xff0c;Android、ios原生开发不会也没啥关系&#xff0c;就是会很费劲啦。 1.创建插件 在Android studio 点击File-->new Flutter Project-->Flutter 取好名字&…

Web大学生网页作业成品:基于html制作中国科技发展网站设计题材【航天之路7页】HTML+CSS+JavaScript

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Celery快速使用(定时任务、Django中使用Celery、秒杀逻辑、双写一致性)

文章标题一、Celery快速使用二、Celery包结构三、Celery异步任务 延时任务 定时任务四、Django中使用Celery五、秒杀逻辑六、双写一致性1&#xff09;路飞项目接口加缓存2&#xff09;Celery定时任务实现双写一致性一、Celery快速使用 简单介绍Celery Celery官网:http://www.…

busybox的实现原理分析(C语言实现简易版的busybox)

1、linux中实现命令的两种方式 1.1、命令都是单独的可执行程序 aston:~$ ls -l /bin/ls -rwxr-xr-x 1 root root 138208 2鏈 8 2022 /bin/ls aston:~$ aston:~$ ls -l /bin/mkdir -rwxr-xr-x 1 root root 68096 2鏈 8 2022 /bin/mkdir aston:~$ aston:~$ file /bin/ls…

元数据性能大比拼:HDFS vs S3 vs JuiceFS

元数据是存储系统的核心大脑&#xff0c;元数据性能对整个大数据平台的性能和扩展能力至关重要。尤其在处理海量文件的时候。在平台任务创建、运行和结束提交阶段&#xff0c;会存在大量的元数据 create&#xff0c;open&#xff0c;rename 和 delete 操作。因此&#xff0c;在…

Sass 使用说明

CSS 样式表越来越大、 越来越复杂、越来越难以维护。这就是预处理可以提供帮助的地方。 Sass 为你提供了 CSS 中还不存在的特性&#xff0c;例如变量、 嵌套、混合、继承和其它实用的功能&#xff0c;让编写 CSS 代码变得有意思。 最直接的方式就是在命令行中调用 sass 命令。安…

java和vue的狱警管理系统监狱系统狱务管理系统

简介 狱警管理系统监狱系统狱务管理系统&#xff0c;主要是管理罪犯教育改造、劳动改造、案件管理&#xff0c;罪犯信息管理等 演示视频 https://www.bilibili.com/video/BV1VG411P7YL/?zw&vd_sourcefa4ffd66538a5ca679a754398a6fdb5f 技术&#xff1a;springbootvueel…

git的下载与安装

1. 下载地址 根据自己的电脑配置信息&#xff0c;选用合适的版本进行下载即可&#xff0c;我的电脑上64位win11&#xff0c;所以我选择了64位的widnows版本&#xff0c;下面其他内容也以此版本展开。 windows&#xff1a;Git - Downloading Package macOS&#xff1a;Git - …

Linux-进程管理

基本介绍 在Linux中&#xff0c;每个执行的程序都称为一个进程&#xff0c;每一个进程都分配一个ID号&#xff08;pid) 程序运行起来就产生了进程 ps ps命令用来查看在目前系统中&#xff0c;有哪些正在执行的进程&#xff0c;以及他们执行的状况&#xff0c;可以不加任何参…

K_A05_004 基于 STM32等单片机驱动2X2块(8X8)点阵模块(MAX7219)显示0-9与中文

目录 一、资源说明 二、基本参数 1、参数 2、引脚说明 三、通信协议说明 工作时序 对应程序: 四、部分代码说明 1、接线说明 1.1、STC89C52RC2X2块(8X8)点阵模块&#xff08;MAX7219&#xff09; 1.2、STM32F103C8T62X2块(8X8)点阵模块&#xff08;MAX7219&#xff09; 2、亮…