C语言动态数组建立方法

动态数组是指在声明时没有确定数组大小的数组,即忽略圆括号中的下标;当要用它时,可随时用ReDim语句(C语言中用malloc语句)重新指出数组的大小。使用动态数组的优点是可以根据用户需要,有效利用存储空间。

动态数组,是相对于静态数组而言。静态数组的长度是预先定义好的,在整个程序中,一旦给定大小后就无法改变。而动态数组则不然,它可以随程序需要而重新指定大小。动态数组的内存空间是从堆(heap)上分配(即动态分配)的。是通过执行代码而为其分配存储空间。当程序执行到这些语句时,才为其分配。程序员自己负责释放内存。(欲详细了解堆请见堆栈)

为什么要使用动态数组?

在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。对于这种问题,用静态数组的办法很难解决。为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数结合指针可以按需要动态地分配内存空间,来构建动态数组,也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。

动态数组与静态数组的对比

  对于静态数组,其创建非常方便,使用完也无需释放,要引用也简单,但是创建后无法改变其大小是其致命弱点!
  对于动态数组,其创建麻烦,使用完必须由程序员自己释放,否则严重会引起内存泄露。但其使用非常灵活,能根据程序需要动态分配大小。
  如何构建动态数组

遵循原则

  申请的时候从外层往里层,逐层申请;
  释放的时候从里层往外层,逐层释放。

构建所需指针

   对于构建一维动态数组,需要一维指针;
  对于二维,则需要一维,二维指针;
  三维需要一,二,三维指针;
  依此类推。

构建所需函数

  
函数原型 返 回 功能说明
void *malloc(unsigned int size); 成功:返回所开辟
空间首地址 失败:返回空指针
向系统申请
size字节的
堆空间
void *calloc(unsigned int num, unsigned int size); 成功:返回所开辟
空间首地址 失败:返回空指针
按类型申请
num个size字
节的堆空间
void free(void *p); 无返回值 释放p指向
的堆空间
void *realloc(void *p,unsigned int size); 成功:返回新开辟
空间首地址 失败:返回空指针
将p指向的
堆空间变为
size

说明:

  (1)规定为void *类型,这并不是说该函数调用后无返回值,而是返回一个结点的地址,该
  地址的类型为void(无类型或类型不确定),即一段存储区的首址,其具体类型无法确定,只有使
  用时根据各个域值数据再确定。可以用强制转换的方法将其转换为别的类型。例如:double
  *pd=NULL; pd=(double *)calloc(10,sizeof(double)); 表示将向系统申请10个连续的
  double类型的存储空间,并用指针pd指向这个连续的空间的首地址。并且用(double)对calloc( )
  的返回类型进行转换,以便把double类型数据的地址赋值给指针pd。
  (2)使用sizeof的目的是用来计算一种类型的占有的字节数,以便适合不同的编译器
  (3)由于动态分配不一定成功,为此要附加一段异常处理程序,不致程序运行停止,使用户
  不知所措。通常采用这样的异常处理程序段:
  if(p==NULL) { printf("动态申请内存失败!\n"); exit(1); //异
  常退出 }
  (4)这四个函数头文件均包含在<stdlib.h>中。
  (5)分配的堆空间是没有名字的 只能通过返回的指针找到它。
  (6)绝不能对非动态分配存储块使用free。也不能对同一块内存区同时用free释放两次。
  如:free(p);free(p);
  (7)调用 free() 时, 传入指针指向的内存被释放, 但调用函数的指针值可能保持不变, 因
  为p是作为形参而传递给了函数。严格的讲, 被释放的指针值是无效的, 因为它已不再指向所申请
  的内存区。这时对它的任何使用便可能会可带来问题。
  malloc与calloc的区别
  对于用malloc分配的内存区间,如果原来没有被使用过,则其中的每一位可能都是0;反之,
  如果这部分内存空间曾经被分配、释放和重新分配,则其中可能遗留各种各样的数据。也就是说,
  使用malloc()函数的程序开始时(内存空间还没有被重新分配)能正常运行,但经过一段时间后(内
  存空间已被重新分配)可能会出现问题,因此在使用它之前必须先进行初始化(可用memset函数
  对其初始化为0),但调用calloc()函数分配到的空间在分配时就已经被初始化为0了。
  当你在calloc()函数和malloc()函数之间作选择时,你需考虑是否要初始化所分配的内存空
  间,从而来选择相应的函数。

具体构建方法

  以三维整型数组array[n1][n2][n3]为例。
  先遵循从外层到里层,逐层申请的原则:
  最外层指针是array,它是个三维指针,所指向的是array[],其为二维指针。所以给array
  申请内存应:
  array=(int***)calloc(n1,sizeof(int**));
  次层指针是array[],它是个二维指针,所指向的是array[][],其为一维指针。所以给array[]
  申请内存应:
  for(i=0;i<n1;i++)
  {
  array[i]=(int**)calloc(n2,sizeof(int*));
  }
  最内层指针是array[][],它是个一维指针,所指向的是array[][][],其是个整型常量。所
  以给array[][]申请内存应:
  for(i=0;i<n1;i++)
  {
  for(j=0;j<n2;j++)
  {
  array[i][j]=(int*)calloc(n3,sizeof(int));
  }
  }
  当然,你可以把它们整合在一起为:
  int i,j,k;
  int n1,n2,n3;
  int ***array;
  scanf("%d%d%d",&n1,&n2,&n3);
  array=(int***)calloc(n1,sizeof(int**));
  for(i=0;i<n1;i++)
  {
  array[i]=(int**)calloc(n2,sizeof(int*));
  for(j=0;j<n2;j++)
  {
  array[i][j]=(int*)calloc(n3,sizeof(int));
  for(k=0;k<n3;k++)
  {
  array[i][j][k]=i+j+k+1;
  }
  }
  }
  最后不要忘了释放这些内存,这要遵循释放的时候从里层往外层,逐层释放的原则。
  分析过程可参考上面的解答,这里不再赘述。只给出代码吧:
  for(i=0;i<n1;i++)
  {
  for(j=0;j<n2;j++)
  {
  free(array[i][j]);//释放第三维指针
  }
  }
  for(i=0;i<n1;i++)
  {
  free(array[i]);//释放第二维指针
  }
  free(array);//释放第一维指针
  其余维的如四维创建过程大同小异,这里不再赘述。

基础概念

  C#集合、C#动态数组的概念之集合,什么是集合呢?集合就如同数组,用来存储和管理一组特定类型的数据对象,除了基本的数据处理功能,集合直接提供了各种数据结构及算法的实现,如队列、链表、排序等,可以让你轻易地完成复杂的数据操作。在使用数组和集合时要先加入system.collections命名空间,它提供了支持各种类型集合的接口及类。集合本身上也是一种类型,基本上可以将其作为用来存储一组数据对象的容器,由于c#面向对象的特性,管理数据对象的集合同样被实现成为对象,而存储在集合中的数据对象则被称为集合元素。这里提到了接口这个概念,它也是面向对象编程进化的重要标准,我们在这里不做过多的讲解,先注重学习集合中的对象及其使用就可以了,下面我们来学习第一种集合:
  C#集合、C#动态数组的概念之C#动态数组ArrayList.ArrayList 类提供了继承了IList接口。什么是继承呢?这也是面向对象语言的重要特点之一,现在你们先把它理解为,如果一个对象继承了类或接口,那么它也具有了这个类和接口中的方法、属性,可以用这些继承的方法和属性来做相应的操作,比如:数组增加元素没有Add()方法,但是动态数组ArrayList继承了一个增加元素有Add()方法的接口,那么当它要增加元素的时候,不仅可以用索引,也可以用继承下来的Add()方法了。随着学习的深入,我会给大家再具体讲解继承的概念和使用继承的好处。那么下面让我们来看看动态数组所继承的这个接口IList它有什么特性呢?
  C#动态数组之 Ilist接口:定义了利用索引访问集合对象的方法,还继承了ICollection和IEnumerable接口,除实现了接口原有的方法成员外,其本身也定义多个专门的方法成员,例如新增、移除、在指定位置插入元素或是返回特定元素在集合中所在的位置索引,这些方法主要为集合对象提供类似数组的元素访问功能。
  C#动态数组之ILsit接口成员:add、insert、RemoveAt、Remove、contains、Clear、indexof方法,它最大的特色在于提供类似数组索引的访问机制。
  C#集合、C#动态数组的概念的基本情况就向你介绍到这里,希望对你了解和学习C#集合、C#动态数组的概念有所帮助。

构建实例

一维

  #include <stdio.h>
  #include <stdlib.h>
  int main()
  {
  int n1,i;
  int *array;
  puts("输入一维长度:");
  scanf("%d",&n1);
  array=(int*)malloc(n1*sizeof(int));//第一维
  for(i=0;i<n1;i++)
  {
  array[i]=i+1;
  printf("%d\t",array[i]);
  }
  free(array);//释放第一维指针
  return 0;
  }

二维

  #include <stdlib.h>
  #include <stdio.h>
  int main()
  {
  int n1,n2;
  int **array,i,j;
  puts("输入一维长度:");
  scanf("%d",&n1);
  puts("输入二维长度:");
  scanf("%d",&n2);
  array=(int**)malloc(n1*sizeof(int*)); //第一维
  for(i=0;i<n1; i++)
  {
  array[i]=(int*)malloc(n2* sizeof(int));//第二维
  for(j=0;j<n2;j++)
  {
  array[i][j]=i+j+1;
  printf("%d\t",array[i][j]);
  }
  puts("");
  }
  for(i=0;i<n1;i++)
  {
  free(array[i]);//释放第二维指针
  }
  free(array);//释放第一维指针
  return 0;
  }

三维

  #include <stdlib.h>
  #include <stdio.h>
  int main()
  {
  int n1,n2,n3;
  int ***array;
  int i,j,k;
  puts("输入一维长度:");
  scanf("%d",&n1);
  puts("输入二维长度:");
  scanf("%d",&n2);
  puts("输入三维长度:");
  scanf("%d",&n3);
  array=(int***)malloc(n1*sizeof(int**));//第一维
  for(i=0; i<n1; i++)
  {
  array[i]=(int**)malloc(n2*sizeof(int*)); //第二维
  for(j=0;j<n2;j++)
  {
  array[i][j]=(int*)malloc(n3*sizeof(int)); //第三维
  for(k=0;k<n3;k++)
  {
  array[i][j][k]=i+j+k+1;
  printf("%d\t",array[i][j][k]);
  }
  puts("");
  }
  puts("");
  }
  for(i=0;i<n1;i++)
  {
  for(j=0;j<n2;j++)
  {
  free(array[i][j]);//释放第三维指针
  }
  }
  for(i=0;i<n1;i++)
  {
  free(array[i]);//释放第二维指针
  }
  free(array);//释放第一维指针
  return 0;
  }

四维

  #include <stdlib.h>
  #include <stdio.h>
  int main()
  {
  int n1,n2,n3,n4;
  int ****array;
  int i,j,k,m;
  puts("输入一维长度:");
  scanf("%d",&n1);
  puts("输入二维长度:");
  scanf("%d",&n2);
  puts("输入三维长度:");
  scanf("%d",&n3);
  puts("输入四维长度:");
  scanf("%d",&n4);
  array=(int****)malloc(n1*sizeof(int***));//第一维
  for(i=0; i<n1; i++)
  {
  array[i]=(int***)malloc(n2*sizeof(int**)); //第二维
  for(j=0;j<n2;j++)
  {
  array[i][j]=(int**)malloc(n3*sizeof(int*)); //第三维
  for(k=0;k<n3;k++)
  {
  array[i][j][k]=(int*)malloc(n4*sizeof(int));//第四维
  for(m=0;m<n4;m++)
  {
  array[i][j][k][m]=i+j+k+m+1;
  printf("%d\t",array[i][j][k][m]);
  }
  puts("");
  }
  puts("");
  }
  puts("");
  }
  for(i=0;i<n1;i++)
  {
  for(j=0;j<n2;j++)
  {
  for(k=0;k<n3;k++)
  free(array[i][j][k]);//释放第四维指针
  }
  }
  for(i=0;i<n1;i++)
  {
  for(j=0;j<n2;j++)
  {
  free(array[i][j]);//释放第三维指针
  }
  }
  for(i=0;i<n1;i++)
  {
  free(array[i]);//释放第二维指针
  }
  free(array);//释放第一维指针
  return 0;
  }

数组案例

  #include <stdio.h>
  #include <stdlib.h>
  int main()
  {
  int*n,*p;
  int i;
  n=(int*)calloc(1,sizeof(int));
  for(i=0;i<5000;i++)
  {
  n[i]=i+1;
  printf("%d\t",n[i]);
  p=(int*)realloc(n,(i+2)*sizeof(int));//动态扩充数组
  if(p!=NULL)
  n=p;
  else
  {
  puts("error!");
  return 0;
  }
  }
  free(n);
  return 0;
  }

游戏应用

预备知识

  (1)getch()
  函数原型: int getch(void);
  函数功能: 从控制台读取一个字符,但不显示在屏幕上。
  函数返回: 读取的字符。
  (2)rand()
  函数原型: int rand(void);
  函数功能: 随机函数, 产生0到32767间的随机整数(0到0x7fff之间)。
  函数返回: 随机整数
  所属文件: <stdlib.h>
  (3)srand()
  函数原型: void srand(unsigned seed);
  函数功能: 该函数和rand随机函数配合使用,产生随机数的起始发生数据。
  参数说明: seed为无符号整数。
  所属文件: <stdlib.h>
  (4)time()
  函数原型: time_t time(time_t *timer)
  函数功能: 得到机器的日历时间或者设置日历时间。
  函数返回: 机器日历时间。
  参数说明: timer=NULL时得到机器日历时间,timer=时间数值时,用于设置日历时间,
  time_t是一个long类型。
  所属文件: <time.h>
  (5)'\b'实现退格,即当前光标后退一格。
  (6)'\a'实现响铃,即执行时计算机会嘟一声。
  (7)得到随机数值范围在a~b(包含b)的方法:rand()%(b-a+1)+a;

代码

  #include <stdio.h>
  #include <conio.h>//getch()函数所需头文件
  #include <stdlib.h>//随机函数所需头文件
  #include <time.h>//time()函数所需头文件
  void clear(void)//此函数用以清除当前行
  {
  printf("\r \r");
  }
  void start(void)
  {
  puts("这是消单词游戏的精简版,还不会图像编程的人可以看一看");
  puts(" 由于没有引人数据库,所以此单词是模拟的。");
  puts("按任意键开始,按Esc键结束,按tab键重新开始:");
  puts(" 一旦输错,将发出声音警告,你必须重新输入。");
  puts(" 按任意键开始,按Esc键结束:");
  if( '\x1b' == getch() ) //按Esc键结束
  {
  exit( 1 );
  }
  }
  int main()
  {
  char*c_rand,*c_input;
  int i,j,N,n,space,N_rand;
  start();
  printf("\r你想消最多由多少个字母组成的单词?(输入数字(1-9)");
  n=getch();
  N=n-'0';//将输入的字符转换为整型数字
  clear();//清除当前行
  if(!((N>=1&&N<=9)||n==27))//27是键Esc的ASII值
  {
  printf("\r范围错误,请重新开始:");
  getch();
  exit(1);//异常退出
  }
  if(n==27)
  return 0;
  srand(time(NULL));//用来对随机函数初始化
  c_rand=(char*)malloc(N+1);
  c_input=(char*)malloc(N+1);
  while(1)
  {
  N_rand=rand()%N+1;//实现单词长度随机定义
  for(i=0;i<N_rand;i++)
  {
  c_rand[i]=(rand() ==0)?(rand()&+'A'):(rand()&+'a');//随机取个字母
  }
  c_rand[N_rand]='\0';
  //下面四行代码实现单词位置随机出现
  space=rand()P;
  putchar('\r');
  for(i=0;i<space;i++)
  putchar(' ');
  printf("%s",c_rand);
  for(i=0;i<N_rand;i++)
  {
  c_input[i]=getch();//用户输入
  if(c_input[i]=='\x1b')//"\x1b"代表的是Esc键
  break;
  //下面五行代码实现字母消失效果
  putchar('\r');
  for(j=0;j<space;j++)
  putchar(' ');
  for(j=0;j<=i;j++)
  putchar(' ');
  if(c_input[i]!=c_rand[i])
  {
  i=-1;
  putchar('\a');
  //下面四行代码代码字母重现效果
  putchar('\r');
  for(j=0;j<space;j++)
  putchar(' ');
  printf("%s",c_rand);
  }
  }
  if(c_input[i]=='\x1b')//"\x1b"代表的是Esc键
  break;
  }
  free(c_rand);
  free(c_input);
  return 0;

  }



转:http://blog.sina.com.cn/s/blog_7414b2930100v20t.html

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

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

相关文章

Linux小宝典之理解Chroot模式

Chroot 在 Linux 系统中发挥了根目录的切换工作&#xff0c;同时带来了系统的安全性等好处。本文通过编写 chroot 来理解 chroot 的作用和好处&#xff0c;这不仅有助于更好的使用 chroot&#xff0c;同时加深了对 Linix 系统初始 RAM 磁盘工作的认识。 chroot&#xff0c;即 …

jHipster入门,第1部分

因此&#xff0c;您想保持技术的领先地位&#xff0c;但对所有活动部件感到不知所措。 你真幸运&#xff01; 这是jHipster发光的地方。 如果您喜欢Ruby on Rails或Grails的方法来快速启动和运行应用程序&#xff0c;那么这可能是适合您的选择。 jHipster旨在使设置应用程序变…

python每隔30s检查一次_用Python写一个“离线语音提示器”来提醒我们别忘记了时间...

前言本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。环境&#xff1a;Win7系统&#xff0c;外网未连接&#xff0c;主机接有返听音箱。准备&#xff1a;这里明显要用语音合成&#xff0c;既然是离线状态…

vue中map组件

分享一个比较好用的基于百度地图的vue组件。也有react版本的&#xff0c;可以自行选择。 分享链接&#xff1a;点击转载于:https://www.cnblogs.com/yunnex-xw/p/9996600.html

MATLAB中mean的用法

1、函数功能 求数组的平均数或者均值 2、使用方法 ① M mean(A) 返回沿数组中不同维的元素的平均值。 如果A是一个向量&#xff0c;mean(A)返回A中元素的平均值。 如果A是一个矩阵&#xff0c;mean(A)将其中的各列视为向量&#xff0c;把矩阵中的每列…

Linux中profile、bashrc、bash_profile之间的区别和联系

/etc/profile:此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行.并从/etc/profile.d目录的配置文件中搜集shell的设置. 英文描述为&#xff1a; # /etc/profile # System wide environment and startup programs, for login setup # Functions and alias…

python selenium 等待元素出现_Python Selenium等待加载几个元素

考虑到Mr.E.和Arran的评论&#xff0c;我在CSS选择器上完全遍历了列表。棘手的部分是关于我自己的列表结构和标记(更改类等)&#xff0c;以及动态创建所需的选择器并在遍历期间将它们保存在内存中。我通过搜索任何未加载状态的内容来处理等待几个元素的问题。您也可以使用“&am…

网络操作系统第242页作业

1.简述电子邮件的工作原理。 答&#xff1a;用户编辑的E mail被转换成一个标准的邮件格式&#xff0c;这个邮件格式中可以包含各种样式的文件&#xff0c;如图像&#xff0c;声音&#xff0c;可执行程序等。邮件的内容以各种编码方式转换成ASCII码的形式&#xff0c;以便在网络…

MATLAB中median函数的用法

1.功能 求矩阵的中间值。 2.格式 ①median(M) 每一列返回一个值,为M该列的从大到小排列的中间值. ②median(M,dim) dim为1&#xff0c;2。其中1表示按每列返回一个值,为该列从大到小排列的中间值,, 2表示按每行返回一个值,为该行从大到小排列的中间值. 注意:如果行或列的个数为…

测试驱动开发 测试前移_测试驱动陷阱,第2部分

测试驱动开发 测试前移单元测试中单元的故事 在本文的上半部分 &#xff0c;您可能会看到一些不好但很受欢迎的测试示例。 但是我不是一个专业的批评家&#xff08;也被称为“巨魔”或“仇恨者”&#xff09;&#xff0c;没有任何建设性的话就抱怨。 多年的TDD教给我的不仅仅是…

CentOS6.3 重启后/etc/resolv.conf 被还原解决办法

今天一台服务器上不了网&#xff0c;设置了nameserver&#xff0c;重启后/etc/resolv.conf文件就被自动还原了&#xff0c;最后发现是被Network Manager修改了。 解决方法&#xff1a; 停止Network Manager服务 service NetworkManager stop重启网络服务 /etc/init.d/network r…

react安装_超全面详细一条龙教程!从零搭建React项目全家桶(上篇)

React是近几年来前端项目开发非常火的一个框架&#xff0c;其背景是Facebook团队的技术支持&#xff0c;市场占有率也很高。很多初学者纠结一开始是学react还是vue。个人觉得&#xff0c;有时间的话&#xff0c;最好两个都掌握一下。从学习难度上来说&#xff0c;react要比vue稍…

MATLAB中find函数详解

&#xff08;转自http://blog.sina.com.cn/emily250886&#xff09; 功能&#xff1a; 寻找非零元素的索引和值语法&#xff1a;1. ind find(X) 2. ind find(X, k) 3. ind find(X, k, first) 4. ind find(X, k, last) 5. [row,col] find(X, ...) 6. [row,col,v] f…

Oracle Service Bus简介

我们正在为电信提供商设计一个新系统&#xff0c;在该系统中&#xff0c;我们研究了用作企业服务总线的Oracle服务总线&#xff08;OSB&#xff09;。 对我来说&#xff0c;第一个优点是它提供了惊人的工具支持。 通过将其捆绑为Oracle SOA套件&#xff0c;Oracle已将其所有企业…

VS2008中源文件与模块生成时的文件不同,仍要让调试器使用它吗

解决办法: 1.在工具-> 选项-> 调试-> 常规 里 把“要求源文件和版本匹配”的勾勾去掉 然后去掉“地址级的调试”的勾勾&#xff08;这样就不会出现反汇编代码了&#xff09; 然后重新引用 2.直接把WEB项目下面的Bin文件夹下的BLL全部删除。 3.若出现如下的警告信息 仔…

Linux中的15个‘echo’ 命令实例

作者&#xff1a; tecmint Avishek Kumar 译者&#xff1a; LCTT geekpi | 2014-10-03 12:42 评论: 4 收藏: 5 分享: 5 echo是一种最常用的与广泛使用的内置于Linux的bash和C shell的命令&#xff0c;通常用在脚本语言和批处理文件中来在标准输出或者文件中显示一行文本或…

springboot redis 断线重连_Redis高可用方案实现

redis中为了实现高可用&#xff08;High Availability&#xff0c;简称HA&#xff09;&#xff0c;采用了如下两个方式&#xff1a;主从复制数据。采用哨兵监控数据节点的运行情况&#xff0c;一旦主节点出现问题由从节点顶上继续进行服务。主从复制redis中主从节点复制数据有全…

罗德里格斯(Rodrigues)旋转向量与矩阵的变换

在做双目立体视觉深度图像生成的时候&#xff0c;遇到旋转向量&#xff08;1x3&#xff09;与旋转矩阵&#xff08;3x3&#xff09;的概念&#xff0c;得知二者可以通过罗德里格斯相互转化。 1.旋转的表示 处理三维旋转问题时&#xff0c;通常采用旋转矩阵的方式来描述旋转变换…

Mongodb 分片与副本集

测试搭建192.168.3.110mongos 30000&#xff0c;30001,30002config 40000,40001,40002shard1 50001,50002,50003shard2 50004,50005,50006,shard3 50007,50008,50009[rootmysql-slave10 data]# mkdir -p /data/config/1/data[rootmysql-slave10 data]# mkdir -p /data/config/2…

python必读5本书籍_免费下载!5本从Python入手机器学习的必备电子书!(附链接)...

大数据文摘授权转载自数据派THU作者&#xff1a;Richard Gall编译&#xff1a;车前子、丁楠雅今天&#xff0c;机器学习已成为软件工程所有领域中最重要的发展趋势之一。这门技术不再局限于研究者和数据分析者&#xff0c;从网络安全到网络开发机&#xff0c;器学习都至关重要。…