【C语言复习】C语言中的数组与指针

news2025/1/17 6:16:50

数组与指针复习

  • 写在前面
  • 数组和指针
    • 指针基础概念
    • 进阶知识
      • 指针的分类
      • 指针和数组笔试题

写在前面

数组和指针小节,主要分为以下关键点:

  • 常见指针分类,如指针数组、数组指针、函数指针等。
  • 什么是数组/ 指针
  • 有关数组和指针的题目
  • 数组传参

我们也会围绕这些关键点来梳理复习。

数组和指针

指针基础概念

  1. 指针是一种特殊的变量,用来存放地址,这个地址可以唯一标识一块内存空间。
  2. 指针的大小固定,32位机器4字节,64位机器8字节。
  3. 指针有类型,指针的类型决定了指针加减整数要跳过多少字节。也会影响解引用时候的权限(可以在内存中取到多少位的内容)。
  4. 指针的运算。
    • 指针 ± 整数
    • 指针 - 指针
    • 指针的关系运算
  5. 野指针的成因和规避手段:
    1. 成因:
      • 指针没有初始化,默认为随机值
      • 指针越界访问
      • 指针指向的空间释放
    2. 规避手段
      • 指针创建时初始化
      • 小心指针的越界
      • 指针指向空间释放后立即置空指针
      • 避免返回局部变量的地址(局部变量出作用域销毁)
      • 指针使用之前检查有效性
  6. 指针和数组关系
    • 数组名表示的是数组首元素的地址(两个例外)
      • sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名代表整个数组。
      • &数组名,数组名代表整个数组。
    • 直接通过指针访问数组中的元素
  7. 二级指针:指针变量也是变量,也有地址,存放指针的地址的变量就是二级指针。

进阶知识

指针的分类

  1. 字符指针 char*,使用如下:
//usage 1:
int main()
{
	char ch = 'W';
	char * pc = &ch;
	*pc = 'w';
	return 0;
}
//usage 2:
const char* pstr = "hello world.";
printf("%s\n", pstr);
//put a string into a pointer

注意第二种,是把字符串的首字符放到了pstr中,通过首字符的地址就可以找到整个字符串。注意,如果用相同的常量字符串初始化不同的字符指针,指向相同的常量字符串。因为常量字符串在创建时被储存在常量区。指针指向同一个字符串时,实际指向相同的内存。但如果用相同常量去初始化数组,不同数组开辟出不同的内存块,储存在堆区。

注意第二种,是把字符串的首字符放到了pstr中,通过首字符的地址就可以找到整个字符串。注意,如果用相同的常量字符串初始化不同的字符指针,指向相同的常量字符串。因为常量字符串在创建时被储存在常量区。指针指向同一个字符串时,实际指向相同的内存。但如果用相同常量去初始化数组,不同数组开辟出不同的内存块,储存在堆区。

  1. 指针数组,存放指针的数组。
int* arr1[10]; //整型指针数组
char** arr2[10]; //二级字符指针的数组
  1. 数组指针,存放数组的指针。
int (*p)[10];
// *p -> int[10] 指向一个整型数组的指针,数组指针
char* (*pc)[20];
// *p -> char*[20] 指向一个字符指针数组的指针,数组指针

​ &数组名 和 数组名 的对比

&数组名 和 数组名,虽然打印出来的都是相同的地址,但是意义不同。

&数组名表示的是数组的地址,而数组名是代表数组首元素的地址。

&数组名的类型是: int(*)[10],是数组指针类型。 &数组名+1条过了整个数组。

数组名代表首元素的地址,但是二维数组的首元素地址实际上是第一行的地址,即一维数组的地址。所以如果传参传二维数组,可以传数组名,并以二维数组接收,也可以传递数组名用数组指针接收。

//二维数组
int arr[4][4] = {1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4};
//接收1
void print1(int arr[4][4],int row, int col);
//传参1
print1(arr,4,4);

//接收2
void print2(int(*arr)[4], int row, int col);
//传参2
print2(arr,4,4);
//小练习
int arr[5]; //数组
int *parr1[10]; //指针数组
int (*parr2)[10]; //数组指针
int(*parr3[10])[5]; // 5个(10个元素的指针数组)的指针 (数组指针)
  1. 数组和指针传参

    • 一维数组传参

      形参设置可以传指针,可以传数组

      void test(int* arr);
      void test(int arr[10]);
      
    • 二维数组传参

      形参设置可以传二维数组,可以传数组指针

      注意:传二维数组必须要知道一行多少元素,可以不知道有多少行。否则无法解析数组。

      //传二维数组
      void test(int arr[][5]);
      void test(int arr[3][5]);
      //传数组指针
      void test(int(*parr)[5]);
      
    • 一级指针传参

      一个函数的参数部分为一级指针时,函数可以接收同类型数组,以及一级指针。

    • 二级指针传参

      一个函数的参数部分为二级指针时,函数可以接收一个一级指针的指针(&一级指针),一级指针数组的数组名。

      void test(int** ptr);
      
      int main()
      {
      	int n = 100;
      	int * pn = &n;
      	int ** ppn = &pn;
      	
      	test(ppn); //传二级指针
      	test(&pn); //传一级指针的地址
      	
      	int* arr[10];
      	test(arr); //传一个一级指针数组的数组名
      }
      
  2. 函数指针

    void test();
    //对应的函数指针为
    void(*pfunc)();
    
  3. 函数指针数组

    把函数地址存放到一个数组中去,就是函数指针数组,定义方法是

    int (*parr1[10])();
    // parr1先与[]结合,代表是一个数组。
    // 数组的类型为 int(*)();
    

    函数指针数组作用有限,一个用途是转移表。

    #include <stdio.h>
    int add(int a, int b)
    {
    		return a + b; 
    }
    int sub(int a, int b)
    {
    		return a - b; 
    }
    int mul(int a, int b)
    {
    		return a*b; 
    }
    int div(int a, int b)
    {
    		return a / b; 
    }
    int main() {
        int x, y;
        int input = 1;
        int ret = 0;
        do{
    				printf( "*************************\n" );
    				printf( " 1:add 2:sub \n" ); 
    				printf( " 3:mul 4:div \n" ); 
    				printf( "*************************\n" ); 
    				printf( "请选择:" );
            scanf( "%d", &input);
            switch (input)
            {
    						case 1:
    								printf( "输入操作数:" );
                    scanf( "%d %d", &x, &y);
                    ret = add(x, y);
                  	printf( "ret = %d\n", ret);
                  	break;
                 case 2:
                 		printf( "输入操作数:" );
                    scanf( "%d %d", &x, &y);
                    ret = sub(x, y);            
                    printf( "ret = %d\n", ret);
                    break;
                  case 3:
                  		printf( "输入操作数:" );
                      scanf( "%d %d", &x, &y);
                      ret = mul(x, y);
                      printf( "ret = %d\n", ret);
                      break;
                  case 4:
                    	printf( "输入操作数:" );
                      scanf( "%d %d", &x, &y);
                      ret = div(x, y);
                      printf( "ret = %d\n", ret);
                      break;
                  case 0:
                    	printf("退出程序\n");
                      breark;
                  default:
                  		printf( "选择错误\n" );
                  		break; }
        } while (input);
        return 0;
    }
    

    如果使用函数指针数组实现,就简单很多。

    int main()
    {
        int x, y;
        int input = 1;
        int ret = 0;
        int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表 
        while (input)
        {
        	printf( "*************************\n" );
        	printf( " 1:add 2:sub \n" ); 
        	printf( " 3:mul 4:div \n" ); 
        	printf( "*************************\n" );
        	printf( "请选择:" );
          scanf( "%d", &input);
          if ((input <= 4 && input >= 1))
          {
          		printf( "输入操作数:" ); 
          		scanf( "%d %d", &x, &y); 
          		ret = (*p[input])(x, y);
     	   	}
        	else
        			printf( "输入有误\n" ); 
        	printf( "ret = %d\n", ret);
        	}
        return 0;
    }
    

    可以发现简单很多,如果需要添加功能只需要改变数组中元素的个数,并将函数名填入其中即可。

  4. 指向函数指针数组的指针

    void gtest(const char* str)
    {
    		printf("%s\n", str);
    }
    int main()
    {
    		void(*pfun)(const char*) = test;
      	//函数指针
      	void(*pfunArr[5])(const char*);
      	pfunArr[0] = test;
      	//函数指针数组
      	void(*(*ppfunArr)[5])(const char*) = &pfunArr;
    		//定义函数指针数组的指针。
      	return 0;
      	
    }
    
  5. 回调函数

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

    qsort就是c语言中比较常用的一个回调函数。用于数组的排序。

    void qsort (void* base, size_t num, size_t size,            int (*compar)(const void*,const void*));
    //参数含义:
    // base : 需要比较的数组名
    // num  : 数组中的元素个数
    // size : 数组中元素大小
    // compar : 调用者自己实现的一个函数,用于比较大小。
    

    举个例子使用它。

    void* compareFunc(const void* p1, const void* p2)
    {
    		return (*(int*)p1 - *(int*)p2);
    }
    int main()
    {
    		int arr[] = {1,3,4,5,9,2,7,0};
    		int i = 0;
    		qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(int), compareFunc);
    		for(int i = 0; i < sizeof(arr)/sizeof(arr[0]); ++i)
    		{
    				printf(" %d ", arr[i]);
    		}
    		return 0;
    }
    

指针和数组笔试题

  1. 强化训练数组名,&数组名等内容。
//一维数组
int a[] = {1,2,3,4}; 
printf("%d\n",sizeof(a));  
//16,sizeof数组名代表整个数组的大小
printf("%d\n",sizeof(a+0)); 
//4/8,a+0代表指针指向数组的第一个元素,仍是指针大小
printf("%d\n",sizeof(*a)); 
//4,既然不在两个例外,a就代表首元素,*a代表首元素地址解引用,就是首元素的大小,4
printf("%d\n",sizeof(a+1)); 
//4/8(32位/64位), sizeof(pointer)
printf("%d\n",sizeof(a[1])); 
//4,sizeof加数组中的一个元素等于sizeof(int)
printf("%d\n",sizeof(&a)); 
//a取地址,指针大小,4/8
printf("%d\n",sizeof(*&a));
//&a代表的是整个数组取地址,后面解引用,就是整个数组的大小16
printf("%d\n",sizeof(&a+1)); 
//&a代表整个数组取地址,+1跳过整个数组,但是还是指针的大小,4/8
printf("%d\n",sizeof(&a[0])); 
//&a[0]代表首元素取地址,指针大小,4/8
printf("%d\n",sizeof(&a[0]+1));
//&a[0]+1,代表首元素取地址然后+1跳过首元素,就是a[1]的地址,4/8
//字符数组
char arr[] = {'a','b','c','d','e','f'}; 
printf("%d\n", sizeof(arr)); 
//6,整个数组的大小
printf("%d\n", sizeof(arr+0)); 
//4/8,指针大小,指向第0个元素
printf("%d\n", sizeof(*arr)); 
//1,首元素的大小
printf("%d\n", sizeof(arr[1])); 
//1,第二个元素arr[1]的大小
printf("%d\n", sizeof(&arr)); 
//4/8,指针大小,代表整个数组的地址
printf("%d\n", sizeof(&arr+1)); 
//4/8指针大小,代表这个数组后面的地址
printf("%d\n", sizeof(&arr[0]+1));
//4/8指针大小,指向第0个元素之后
printf("%d\n", strlen(arr));
//大于等于6的随机值,没有'\0'。
printf("%d\n", strlen(arr+0));
//大于等于6的随机值,没有'\0'。
printf("%d\n", strlen(*arr));
//arr是首元素的地址,*arr就是首元素,a是97,会把97当成一个地址向后查找,可能会出现野指针问题。段错误
printf("%d\n", strlen(arr[1]));
//错误,arr[1]是第二个元素,b-98,错误
printf("%d\n", strlen(&arr));
//&arr代表整个数组的地址。随机值,和strlen(arr)相同
printf("%d\n", strlen(&arr+1));
//随机值
printf("%d\n", strlen(&arr[0]+1));
//随机值
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
//7,sizeof数组名代表整个数组的大小
printf("%d\n", sizeof(arr+0));
//arr+0 代表首元素的地址,指针大小4/8
printf("%d\n", sizeof(*arr));
//*arr代表首元素地址解引用,就是a的大小,1
printf("%d\n", sizeof(arr[1]));
//b的大小,1
printf("%d\n", sizeof(&arr));
//指针大小,4/8
printf("%d\n", sizeof(&arr+1));
//指针大小,4/8
printf("%d\n", sizeof(&arr[0]+1));
//指针大小,4/8
printf("%d\n", strlen(arr));
//6,""末尾有隐藏'\0'
printf("%d\n", strlen(arr+0));
//6
printf("%d\n", strlen(*arr));
//arr是首元素的地址,*arr就是首元素,a是97,会把97当成一个地址向后查找,可能会出现野指针问题。段错误
printf("%d\n", strlen(arr[1]));
//arr[1]代表b,第二个元素,把98当成一个地址向后查找,野指针。
printf("%d\n", strlen(&arr));
//&arr整个数组的地址,随机值,和strlen(arr)相同
printf("%d\n", strlen(&arr+1));
//随机值
printf("%d\n", strlen(&arr[0]+1));
//随机值

char *p = "abcdef";
printf("%d\n", sizeof(p));
//p是一个字符指针,指针大小4/8
printf("%d\n", sizeof(p+1));
//p类型是char*,p+1指针大小,4/8
printf("%d\n", sizeof(*p));
//*p是char类型,sizeof(char) = 1
printf("%d\n", sizeof(p[0]));
// sizeof(char) = 1
printf("%d\n", sizeof(&p));
//p是char*,&p是char**,指针大小4/8
printf("%d\n", sizeof(&p+1));
//&p+1,指针大小
printf("%d\n", sizeof(&p[0]+1));
//&p[0]就是char*类型,+1后,指针大小4/8
printf("%d\n", strlen(p));
//6
printf("%d\n", strlen(p+1));
//5
printf("%d\n", strlen(*p));
//经典野指针
printf("%d\n", strlen(p[0]));
//野指针,随机值
printf("%d\n", strlen(&p));
//随机值
printf("%d\n", strlen(&p+1));
//随机值
printf("%d\n", strlen(&p[0]+1));
//&p[0]是char*类型,等于5

//二维数组
int a[3][4] = {0}; 
printf("%d\n",sizeof(a)); 
//sizeof数组名为 整个数组的大小,是12*4 = 48
printf("%d\n",sizeof(a[0][0])); 
//一个int的大小,4
printf("%d\n",sizeof(a[0])); 
// a[0]单独存在,就是代表整个数组,在二维数组中,a[0]代表第一行,4*4 = 16
printf("%d\n",sizeof(a[0]+1)); 
// a[0]并非单独存在,代表首元素的地址,就是a[0][0]的地址,指针大小
printf("%d\n",sizeof(*(a[0]+1))); 
//a[0]+1,就是a[0][1]的地址,解引用后就是sizeof(int) = 4
printf("%d\n",sizeof(a+1)); 
//a并非单独存在,代表首元素地址,这里指首行的地址,加1仍为指针大小。
printf("%d\n",sizeof(*(a+1))); 
//a并非单独存在,代表首行的地址,+1代表第二行的地址,*后为16
printf("%d\n",sizeof(&a[0]+1)); 
//&a[0],代表第1行的地址,+1仍为指针大小
printf("%d\n",sizeof(*(&a[0]+1))); 
//代表第一行的地址,解引用后变为了16
printf("%d\n",sizeof(*a)); 
//a并非单独存在,代表首行地址,*后,16
printf("%d\n",sizeof(a[3]));
//a[3]是代表第4行的大小,总共四个元素,16
  1. 计算程序的运行结果:

    int main()
    {
    		int a[5] = {1,2,3,4,5};
    		int *ptr = (int*)(&a + 1);
    		printf("%d, %d", *(a + 1),*(ptr - 1));
    		return 0;
    }
    

    分析:

    a为一个5元素的数组,&a代表整个数组的地址,&a+1为跳过a数组的地址,强制转化为int*类型,ptr-1就指向a最后一个元素的地址。a+1代表着首元素后面一个元素的地址。所以结果为:2,5.在这里插入图片描述

  2. 计算程序的运行结果:

    struct Test{
    		int Num;
    		char* pcName;
    		short sDate;
    		char ch[2];
    		short sh[4];
    }*p;
    //假设结构体的大小为20个字节
    //假设p的值为Null(0x0).求下面表达式的值。
    int main()
    {
      	printf("%p\n", p + 0x1);
      	printf("%p\n", (unsigned long)p + 0x1);
      	printf("%p\n", (unsigned int*)p + 0x1);
      	return 0;
    }
    

    分析:

    p本身的类型为struct Test*,

    p + 0x1,向后跳过多少个字节,本身是根据p的类型决定的。p的类型是struct Test*,+1就是跳过一个 struct Test的大小。所以0x14

    (Unsigned long)p 类型是无符号long,其实就是把地址当作整数去加减,而不能用指针加减整数的规则。所以就是0x1

    (unsigned int*)p 类型是 unsigned int星,+1就是跳过一个unsigned int的大小,所以结果为0x4 。

  3. 计算程序的结果:

    int main()
    {
    		int a[4] = {1,2,3,4};
    		int* ptr1 = (int*)(&a + 1);
    		int* ptr2 = (int*)((int)a + 1);
    		printf("%x, %x", ptr1[-1], *ptr2);
    		return 0;
    }
    

    分析:

    a是一个int数组,&a代表整个数组的地址,+1代表指向数组之后的内存空间。用(int*)强转后,指向不变。ptr1[-1]就等价于 *(ptr1 - 1),可以发现就是4。

    ptr2略微麻烦一些。a强转成int后,+1就变成了直接加1,比如原来a的地址为0x0,(int)a + 1就是 0x1,相当于是在内存中往后面跳过了一个字节的大小,如图所示。(默认内存小端存储)在这里插入图片描述

  4. 计算程序的结果:

    int main()
    {
    		int a[3][2] = { (0,1), (2,3), (4,5) };
    		int* p = a[0];
    		printf("%d", p[0]);
    		return 0;
    }
    

    分析:

    a是一个二维数组,二维数组在内存中也是按照顺序连续排放的。

    但是,二维数组的定义方式是{ {0,1}, {2,3}, {4,5} }; 而不是小括号,而小括号的含义里面是 逗号表达式,(0,1) 是1,(2,3)是3,(4,5)是5。

    所以a这个三行二列的二维数组的真实组成实际上是:{1,3,5,0,0,0}

    a[0] 代表的是a[0]数组的首元素地址,p[0]代表的就是1.

  5. 计算程序的运行结果

    int main()
    {
    		int a[5][5];
    		int(*p)[4];
    		p = a;
    		printf("%p, %d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    		return 0;
    }
    

    分析:

    int(*p)[4]; p = a;实际上就是把a看成是四列的数组去展开,图解这个题目。在这里插入图片描述

  6. int main()
    {
    	char* a[] = {"work", "at", "home"};
    	char** pa = a;
    	pa++;
    	printf("%s\n", *pa);
    	return 0;
    }
    

    分析:

    char* 的数组,里面有三个元素"work" “at” 和 “home”。pa是char**类型,pa++就是越过一个char星的大小,所以pa不再指向work,而是指向at。

  7. int main()
    {
    		char* c[] = {"ENTER", "NEW", "POINT", "FIRST"};
    		char** cp[] = { c+3, c+2, c+1, c};
    		char*** cpp = cp;
    		printf("%s\n", **++cpp);
        printf("%s\n", *--*++cpp+3);
        printf("%s\n", *cpp[-2]+3);
        printf("%s\n", cpp[-1][-1]+1);
    		return 0;
    }
    

    分析:

    解析如图。

    请添加图片描述

数组与指针小节完。

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

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

相关文章

写字楼/园区/购物中心空置率太高?快用快鲸智慧楼宇系统

客户租不租你的写字楼&#xff0c;事关区位、交通、环境、价格、面积、装修等诸多因素&#xff0c;但很多招商部对这些影响客户决策的数据并不重视&#xff0c;在客户初次上门看房时仅简单记录姓名、联系方式、需求面积&#xff0c;对其他核心数据熟视无睹&#xff0c;也为日后…

第十三届蓝桥杯

这里写目录标题一、刷题统计&#xff08;ceil函数返回的是等值于某最小整数的浮点值&#xff0c;不强制转换回int就wa&#xff0c;没错就连和int整数相加都wa二、修剪灌木&#xff08;主要应看清楚会调转方向三、统计子矩阵&#xff08;前缀和滑动窗口⭐&#xff09;四、[积木画…

【算法】笔记:LeetCode 206. 反转链表

文章目录前言思考问题&#xff1a;把分开的节点连在一起结合原题&#xff1a;使用[迭代]解决卡点引入新指针边界条件代码反转的逻辑代码&#xff08;完整答案&#xff09;结合原题&#xff1a;使用[递归]解决卡点完整代码问题的子问题当前层要干什么递归出口前言 这道题可以拆…

冰箱压缩机 方案

压缩机是制冷系统的心脏&#xff0c;它从吸气管吸入低温低压的制冷剂气体&#xff0c;通过电机运转带动活塞对其进行压缩后&#xff0c;向排气管排出高温高压的制冷剂气体&#xff0c;为制冷循环提供动力&#xff0c;从而实现压缩→冷凝→膨胀→蒸发 ( 吸热 ) 的制冷循环。压缩…

C#开发的OpenRA的游戏主界面怎么样创建

通过前面加载界面布局数据,可以把整个界面逻辑的数据加载到内存, 但是这些数据怎么显示出来,又是没有定义的。比如前面定义了多个界面的布局, 又是怎么样知道需要显示哪一个界面? 现在就来解决这个问题,其实整个游戏都是可以通过yaml文件进行配置的, 所以我们需要从yaml…

水果FLStudio21.0.0中文版全能数字音乐工作站DAW

FL Studio 21.0.0官方中文版重磅发布纯正简体中文支持&#xff0c;更快捷的音频剪辑及素材管理器&#xff0c;多样主题随心换&#xff01;Mac版新增对苹果M2/1家族芯片原生支持。编曲、剪辑、录音、混音&#xff0c;20余年的技术积淀和实力研发&#xff0c;FL Studio 已经从电音…

【基础算法】单链表的OJ练习(3) # 移除链表元素 # 相交链表 #

文章目录前言移除链表元素相交链表写在最后前言 本章的OJ练习也是相对简单的&#xff0c;只要能够理解解题的思路&#xff0c;并且依照这个思路能够快速的写出代码&#xff0c;我相信&#xff0c;你的链表水平已经足够了。 对于OJ练习&#xff08;2&#xff09; : ->传送门…

不平凡的一天——

作者&#xff1a;指针不指南吗 专栏&#xff1a;个人日常记录 &#x1f43e;或许会很慢&#xff0c;但是不可以停下来&#x1f43e; 文章目录1.自我介绍2.上学期3.不凡的一天4.新学期写个博客&#xff0c;简单记录一下&#xff0c;新学期加油&#xff01;&#xff01;&#xff…

day7 同步互斥

作业 1.将一个文件中的数据打印到终端上类似cat一个文件&#xff0c;要求如下 &#xff08;1&#xff09;a线程读取文件中的数据 &#xff08;2&#xff09;B线程将A线程读取到的数据打印到终端上 &#xff08;3&#xff09;文件打印完毕后&#xff0c;结束进程 方法1&#…

CMMI流程规范—服务与维护

服务与维护&#xff08;Service and Maintenance, SM&#xff09;是指产品销售之后的客户服务和产品维护。客户服务和产品维护的宗旨就是提高客户对产品以及对开发方的满意度。服务与维护过程域是SPP模型的重要组成部分。本规范阐述了服务与维护过程域的两个主要规程&#xff1…

蓝库云|五大关键引领制造业数字化智慧升级

蓝库云根据《2023制造产业趋势展望》报告&#xff0c;并归纳出「强化企业韧性与敏捷、提升留才诱因、建构多元供应链、兼顾安全的智慧工厂、循环催化永续经营」是牵动制造产业发展的五大关键。将永续目标整合至企业中长期策略中&#xff1b;数字化方面则搭配五大发展关键&#…

【Redis应用】基于Redis实现共享session登录(一)

&#x1f697;Redis应用学习第一站~ &#x1f6a9;本文已收录至专栏&#xff1a;数据库学习之旅 &#x1f44d;希望您能有所收获 &#x1f449;相关推荐&#xff1a;使用短信服务发送手机验证码进行安全校验 一.引入 ​ 在开发项目过程中&#xff0c;我们常常能碰到需要登录注…

Linux操作系统学习(文件IO)

文章目录基础IO系统相关接口文件描述符一切皆文件文件描述符的分配规则重定向fork后的文件描述符基础IO 系统相关接口 在C语言中对文件的操作有fopen打开、fclose关闭、fread读、fwrite写等函数&#xff1b;其实这些都是在系统调用接口上进行的封装。 这里介绍4个系统调用接…

【异常】因多租户字段缺少导致Error updating database. Column ‘tenant_id‘ cannot be null

一、报错内容 org.springframework.dao.DataIntegrityViolationException: ### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: Column tenant_id cannot be null ### The error may exist in com/xxx/cloud/mall/admin/mapper/Goods…

303. 区域和检索 - 数组不可变

303. 区域和检索 - 数组不可变 给定一个整数数组 nums&#xff0c;处理以下类型的多个查询: 计算索引 left 和 right &#xff08;包含 left 和 right&#xff09;之间的 nums 元素的 和 &#xff0c;其中 left < right 实现 NumArray 类&#xff1a; NumArray(int[] num…

英语六级的三大经典句型

目录 1.强调句型&#xff08;提前想好要写在哪个地方&#xff09; 2.虚拟语气 3.第三个句型 3.倒装&#xff08;写一到两个倒装&#xff09; &#xff08;1&#xff09;否定词放在句首就是倒装 &#xff08;2&#xff09;so...that句型结构的句子将so引导的部分置于句首时…

一、一篇文章打好高数基础-函数

1.连续函数的性质考点分析函数的连续性主要考察函数的奇偶性、有界性、单调性、周期性。例题判断函数的奇偶性的有界区间为&#xff08;&#xff09; A.(-1,0) B(0,1) C(1,2) D(2,3)2.闭区间上连续函数的性质考点分析闭区间上连续函数的性质主要考察函数的最大最小值定理、零点…

JavaScript 函数参数

JavaScript 函数对参数的值(arguments)没有进行任何的检查。JavaScript 函数参数与大多数其他语言的函数参数的区别在于&#xff1a;它不会关注有多少个参数被传递&#xff0c;不关注传递的参数的数据类型。函数显式参数与隐藏参数(arguments)在先前的教程中&#xff0c;我们已…

删除启动台(LaunchPad)残留的图标

忘记什么版本的时候以前在 “应用程序” 删除应用后&#xff0c;启动台自动更新删掉 不知道什么时候开始&#xff0c;直接在 “应用程序” 里删掉后&#xff0c;启动台居然不能删除了…… 10.13和10.14后&#xff0c;无论是按住 Option&#xff0c;还是按住 Control Option …

Fisco-Bcos的环境搭建及Data-Export导出组件的使用

注意&#xff1a;由于Data-Export组件暂时只支持Fisco-Bcos 2.x系列&#xff08;这个也是目前使用最多最稳定的系列&#xff09;&#xff0c;故这里使用的是目前最新的Fisco-Bcos 2.x。 Fisco-Bcos链环境搭建 区块链网络部署 主要一步步按照这个官方的操作即可区块链网络搭建…