string 底层模拟实现常用接口

目录

前言

什么是string? 为什么要学习使用string?string的优势?

因此,string类的成员变量也如图顺序表一样,如下图所示: 

构造函数 

拷贝构造 

 析构函数

size()  、capacity() 

 operator[] 

 迭代器 与 范围for

 reserve

push_back 

append 

operator += 

insert 

erase 

resize 

 operator = 

 swap

find 

substr 

clear() 

 string的重载运算符

string类的 流插入 和  流提取 

前提须知:

实现: 

 流插入

流提取 

完整代码:

扩容机制解读: 

getline() 


 

前言

本篇博客是从底层角度来模拟实现STL的string,在C++的学习过程中string是一个非常重要的知识点,在之前的介绍讲解中,我们了解到string有多个接口,且每个接口的功能和接收参数都不一样,所以本篇将会重点介绍和讲解几个重要接口的常用方式。

前情提要:C++:string相关内容的简单介绍-CSDN博客 

什么是string? 为什么要学习使用string?string的优势?

C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。不是很方便。

C++中有string类来提供各种各样的函数接口,大大提高了办事效率。

学过数据结构的朋又应该能够理解,string的本质其实就相当于是存放数据类型为char的顺序表。

换句话说,string是表示字符串序列的类。

因此,string类的成员变量也如图顺序表一样,如下图所示: 

private:
  char* _str;//指针,表示string开辟空间的地址
  size_t _size;//字符串长度
  size_t _capacity;//string开辟的空间的空间大小
构造函数 

 在使用string时,我们通常会发现在讲一串字符使用string修饰时,我们无需亲自为字符开辟空间,这是因为在底层的逻辑中,字符串的空间早就被string类使用构造函数开辟了。

string (const char*str = "")
    :_size(strlen(str))
{  
    _capacity = _size;
    _str = new char[_capacity + 1];
   strcpy(_str,str);
 
}
 
以上的代码设立了缺省值为空,表名了这是一个即是无参又是有参的一个构造函数。

设立缺省值const char*str = ""好处在:
当传递了一个无参的参数时,该构造函数会因为缺省值传输参数,但是参数是无效的,
所以再_size时就会得到长度为0,从而应发空间为0,
但是会因为 string s; 这种代码的存在,空间还是需要被开辟的,所以还是需要开辟空间。

所以 _capacity + 1表示无空间时进行开辟,表明存在,有空间时表示为delete的标识符,方便删除
拷贝构造 

 拷贝构造在之前的学习过程中,分为了深拷贝和浅拷贝。编译器自动生成的默认构造函数使用的是浅拷贝,浅拷贝的拷贝方式与取别名 & 的功能有些类似,单纯的拷贝,就连空间地址也是一模一样的完美复制,所以string类中的拷贝构造使用的是开创新空间进行存放的深拷贝

使用了深拷贝方式进行拷贝
string(const string& s)
{
  _str  = new char[s._capacity +1];
  strcpy(_str,s._str);
  _size = s._size;
  _capacity = s._capacity;
}

 深浅拷贝相关介绍:C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载-CSDN博客 

 析构函数
~string()
{
   delete[]_str; //空间的释放
   _str = nullptr;//指针的释放
   _size=_capacity =0;//内容的清空
}
size()  、capacity() 
size_t size()const//const是为了方便const对象使用的
{
  return _size;
}


size_t capacity()const
{
  return _capacity;
}
 operator[] 

operator[]在C++语言中是一种特殊的遍历方式,使用的是重载运算符[]和C语言中的数组遍历,结合出的一种全新遍历方式,在string中可以使用operator[]对字符串中的字符进行遍历,甚至是改变其中的字符:

而根据operator[]在string内部的使用情况operator[]需要分为两种,一种是const使用的一种是非const使用的,而operator的内部情况无疑于指针的遍历。

char& operator[](size_t pos)
{
  assert(pos<_size);
  return _str[pos];
}
 
 
const char& operator[](size_t pos)const
{
  assert(pos<_size);
  return _str[pos];
}
 迭代器 与 范围for

在某一些的编辑器中,迭代器的底层逻辑其实就是指针,两个分别指向字符串首字符和字符串末尾\0的指针 begin与end,所以在一些罗普大众的教材中,通常会直接把迭代器和指针挂钩,在它们看来,迭代器只不过是char换了个名字,然而功能还是和char类型指针一样的指针:

typedef char* iterator
iterator begin()
{
  return _str;
}
 
iterator end()
{
  return _str+size;
}

与operator[]相同的是,作为迭代器的iterator也有分为const使用和非const使用的类别: 

typedef const char* const_iterator //const使用的
const_iterator begin()const
{
  return _str;
}
 
const_iterator end()const
{
  return _str+size;
}

同时在迭代器是否是指针的问题上,我们需要认识范围for,范围for其实就是一种迭代器,在C++的底层,范围for使用的真是迭代器的代码进行运转:

for (auto ch :s3)
{
  cout << ch << "";
}
cout << endl;

而且,如果我们手搓了迭代器,且名字只要是迭代器的名字 iterator、  begin、  end   、const_iterator 范围for就可以使用我们手搓的迭代器

但是这几者中间名字对不上那范围for就对不上,用不了,只是所以范围for就是一种傻傻的替换并不会灵活变通的迭代器。

 reserve

作为string的内部扩容函数,在底层中常常被其他插入函数作为扩容机制调用,比如较常使用的插入函数:push_back、append等等。

void reserve(size_t n)
{
 
  if(n>_capacity)//扩容机制
  {
   char*tmp = new char[n+1];
//当然因为delete的问题,开辟空间需要额外开辟一个标识符进行使用,所以需要n+1
//这个n+1并不是给\0使用的而是给为了让delete辨认而多开辟的标识符使用的
 
   strcpy(tmp,_str);//把原来空间的内容放到新空间内部
   
   //delete的删除机制,删除原来的空间
   delete[]_str;
   _str = tmp;//指针和地址的赋予
   _capacity = n;//表名空间已经完成
  }
 
}
push_back 

push_back的主要作用是在字符串的尾部插入一个字符,增长字符串的长度,以及可能会扩大字符串所在空间的大小等等,同时需要注意:push_back的扩容机制是二倍扩容。

void push_back(char ch)
{
   //进行二倍扩容
   if(_size == _capacity)//如果长度和空间大小一样就需要扩容
     {
        reserve(_capiacty == 0? 4:2*_capacity);//同时需要考虑到长度和空间大小都是0
       //但是需要插入字符,所以需要进行空间的扩容
      }
  
   _str[_size] = ch;//ch表示插入的字符,_size表示插入的长度也表示需要插入的位置
   ++_size;
   _str[_size] = '\0';//在插入字符后面加上\0表示字符插入完成
 
}
append 

 append最常用的功能是在字符串尾部插入字符或者字符串,而且所以和push_back不同的是,它的扩容机制不能遵循二倍扩容,因为如果字符串的长度过长,使用二倍的空间可能不够用,所以append扩大的空间可能需要遵循字符串的长度来扩充 

void append(const char* str)
{  
   //扩容
   size_t len = strlen(str);
   if(_size + len > _capacity)//使用插入的长度和字符串长度相加 和空间大小对比
     {
         reserve(_size + len);//进行空间的扩大
      }
   
   strcpy(_str + _size , str);//在扩大空间后的原字符串尾部插入新的字符串
   _size + = len;//表名插入了字符串
 
}
operator += 

operator += 最常用的两个功能在底层的逻辑上就是调用了刚才写的push_back 和 append 

insert 

insert 在指定位置插入字符 或者字符串,而根据insert在string内部底层的方式和效率来看,insert的底层是需要进行数据的挪动,以此达到插入字符串的操作。

和string的其他插入函数一样,都具有扩容的功能的同时还有判断是否越界的功能 

插入字符功能的insert:
 
void insert(size_t pos, char ch)
{
   assert(pos <= _size);//进行越界机制的断言
   
   if(_size == _capacity)//判断是否需要扩容操作
   { 
     reserve(_capacity == 0? 4:2*_capacity);
    }
  
   size_t end = _size+1;//进行底层的数据移动操作
   while(end>pos)
   {
      _str[end]  = _str[end-1];
      --end;
    }
 
}


插入字符串功能的insert:
 
void insert(size_t pos, const char *str)
{
   assert(pos <= _size);//进行越界机制的断言
  
   szie_t len = sizeof(str); //获取插入字符串的长度
   
   if(_size + len == _capacity)//判断是否需要扩容操作
   { 
     reserve(_size + len);
    }
  
   size_t end = _size+len;//进行底层的数据移动操作
   while(end > pos + len -1)
   {
      _str[end]  = _str[end - len];
      --end;
    }

   strncpy(_str + pos,str,len);
   _size + len;
 
}

strncpy 函数介绍:strncpy-CSDN博客 

erase 

 erase的主要功能是在指定位置删除指定个数的字符。

erase分为两种删除,而erase的删除需要根据 npos进行判断,所以需要加入前置条件:npos ,在整个类的外面加上 const int string::npos = -1,与此同时在类的成员变量中加入public的变量 static const int pos;这样 npos才能够正常的使用

void erase(size_t pos,size_t len = npos)
{

    assert(pos<_size); 
    if(len == npos || len >= _size-pos);
    {
       _str[pos] = '\0';
       _size = pos;
    }
    else
    {   
      strcpy(_str + pos , _str+pos +len);
      _size -= len;
     
    }

}

earse的删除分为两种
1.全部删除:
对于全部删除满足的条件是指定删除的字符个数大于要剩下的长度或者是没有直接说明删除的字符个数,
这两种情况相当于在指定位置上的字符直接变成\0

代码:
if(len == npos || len >= _size-pos);
    {
       _str[pos] = '\0';
       _size = pos;
    }


2.区间删除:
区间删除,就是把这段区间后面的字符覆盖区间需要删除的空间位置,
可以使用strcpy进行拷贝,把需要删除的区间的首个字符地址上传,
然后把区间之后的字符串的首个字符上传

代码:
  else
    {   
      strcpy(_str + pos , _str+pos +len);
      _size -= len;
     
    }

关于npos :

  • npos是C++标准库中的一个常量,它代表了一个无效的或者不存在的位置。具体来说,npos是一个static成员变量,它的值是一个特别大的无符号整数,通常是-1。在字符串和容器类中,npos常常用于表示查找失败或者无效位置的情况。
  • 在字符串中,npos常常与find()函数一起使用。当find()函数无法找到指定的子串时,它会返回npos作为结果。例如,如果我们使用string类的find()函数查找一个不存在的子串,那么它会返回npos。
  • 在容器类中,npos常常与find()函数、rfind()函数、find_first_of()函数、find_last_of()函数等一起使用。这些函数在查找元素或者子串时,如果找不到匹配项,就会返回npos。
  • 总之,npos是一个表示无效位置的常量,在字符串和容器类中经常用于表示查找失败或者无效位置的情况。
resize 

在之前的学习中我们知道resize接口是用于调整字符串长度的,且分为三种情况进行使用:

一:指定的长度比字符串长度小,删除多余的部分

二:指定的长度比字符串大,但是在空间大小范围内,使用\0进行填补到相对因的指定长度

三:指定的长度比字符串大,且比空间大小大,需要进行扩容,且其余的指定长度位置的范围内的空缺地方用\0填补 

void resize(size_t n , char ch ='\0')
{
   
    if( n <= _size)//情况一
    {
       _str[n] = '\0';
       _size = n;
    }
    else
    {
       
       reserve(n);//需要扩容进入情况三不需要进入情况二
       for(size_t i = _size ; i < n; i++)
       {
          _str[i] = ch;
        
       }
       _str[n] = '\0';
       _size = n;
     }

}

在进入情况二和情况三之前先使用reserve进行检查,
因为reserve有一个检查机制,可以检查我们指定的长度是否大于空间大小

如果比空间大则扩容进入情况三,如果比空间小则不变进入情况二,
检查之后就开始情况2和情况3都有的共同操作:填充\0

 operator = 

operator= 的赋值过程其实就是一个深拷贝的过程,需要开辟单独的空间讲数据进行复制转移 

string & operatro=(const string& s)
{
   char*tmp = new char[s.capacity];
   strcpy(tmp , s._str);
   delete[];
   _str = tmp;
   _size = s._size;
   _capacity = s._capacity;
   return *this;
} 
 swap

默认的或者说直接用C++里的swap进行字符串和字符串之间的交换较为的复杂,因为需要调用深拷贝以及析构函数等等,且拷贝的过程中字符串的转移需要进行至少三次,也就是三次的拷贝,同时还需要利用析构函数解决中间用来存储字符串的变量,于是就有了string专用的swap:

void swap(string& s)
{
  std::swap(_str , s._str);
  std::swap(_size, s._size);
  std::swap(_capacity, s._capacity);
}

直接利用C++的swap进行一个地址交换,但是交换的是成员变量和地址
find 

 find的主要功能是从指定的位置(默认是索引为0的位置)查找字符或者字符串,如果找到了返回字符的位置,找不到返回npos,默认是从0开始查找字符,,如果是查找字符串,则找到了返回字字符串首个字符的位置,找不到就返回npos

查找字符:
size_t find(char ch ,size_t pos = 0)
{
   assert(pos < _size);//确认查找的位置是否存在
   for(size_t i =pos; i < _size; i++)
   {
      if(_str[i] == ch)//进行对照查找
         return i;
    }

    return npos;

}

查找字符串:
size_t find(const char* sub ,size_t pos = 0)const
{
   assert(pos < _size);
 
   const char* p =strstr(_str +pos ,sub);//借用了strstr的功能

   if(p)
   {
      return p - pos;//使用地址-地址的方式得到索引下标位置
   }
   else
   {
      return npos;
   }

}

strstr函数相关介绍:strstr函数-CSDN博客 

substr 

substr的功能是从指定位置获取字符,并可以指定获取字符的个数,如果没有指定获取字符的个数,那么将会从指定的位置获取字符,获取到原字符串的结尾。  

string substr(size_t pos = 0;size_t len = pos)
{
   string sub;
   
   if(len == npos || len >= _size - pos)//获取指定的个数大于剩下长度时
   {
       for(size_t i =pos;i < _size;i++)
       {
          sub += _str[i];//sub从pos位置获取到字符串尾部
       }
   
   }
   else
   {
       for(size_t i = pos;i < pos + len;i++)//获取指定个数小于剩下长度时
       {
          sub += _str[i];//sub只能获取指定长度的字符
       }
   
   }
   return sub;

}
clear() 

clear的主要功能就是清除数据,但仅仅只是数据的清除,空间并不一定清理掉,也就空间不会被释放或者空间不会被销毁!只是清除了空间内部的数据。

void clear()
{
   _size = 0;
   _str[_size] = '\0';

}
 string的重载运算符

string类的 流插入 和  流提取 
前提须知:

string的流插入和流提取是指将string对象与输入输出流进行交互的过程。通过流插入,可以将string对象的内容插入到输出流中;而通过流提取,可以将输入流中的内容提取到string对象中。

流插入操作使用输出流对象(如cout)和插入运算符(<<),将string对象的内容插入到输出流中。例如,可以使用以下方式将一个string对象插入到输出流中:

#include <iostream>
#include <string>

int main() {
    std::string str = "Hello, world!";
    std::cout << str;  // 将str插入到输出流中
    return 0;
}

 流提取操作使用输入流对象(如cin)和提取运算符(>>),将输入流中的内容提取到string对象中。例如,可以使用以下方式将用户输入的内容提取到string对象中:

#include <iostream>
#include <string>

int main() {
    std::string str;
    std::cout << "请输入一个字符串:";
    std::cin >> str;  // 将用户输入的内容提取到str中
    std::cout << "您输入的字符串是:" << str << std::endl;
    return 0;
}

上述代码中,std::cin >> str; 将用户输入的内容提取到字符串变量str中,并通过std::cout输出。

实现: 
 流插入

ostream& operator<<(ostream& out,const string& s)
{
   for(auto ch :s)//借用范围for 把string类型的字符串中的字符一个个传入ch中
                  //再用C++的<<进行传输
   {
      out << ch;
   }

   return out;

}
流提取 

在进行流提取之前,我们要知道string类的流提取底层模拟实现不能和流插入一样调用C++中的流提取符号,因为C++的默认流提取 >> 底层使用的 cin 是一个遇到空格符号或者回车符号终止提取和读取的函数

所以假设我们需要使用流提取进行连续的读取字符串,那么按照程序员写字符串的习惯,当需要写多个连续的字符串时,大多数程序员会使用空格或者回车作为多个字符串的分割标记。

这也就会导致使用cin的流提取会出现错误!因为cin遇到空格和回车会终止提取!

同时,对于流提取而言,它的本质上是遇到空格和回车就会停下读取,所以流提取一般不用于具有空格符号的字符串

并且流提取还附带覆盖的功能,当一个string类型的变量内部具有字符串且在进行 cin >> "hello" >>s;这种操作时,s字符串内部的东西需要被cin>>从键盘或者其他设备中提取的数据覆盖。

于是,string的流提取模拟实现需要完成,解决连续读取操作,解决覆盖操作,解决遇到空格和回车符号暂停读取的操作

同时,string会进行一种扩容操作,这是流提取的扩容机制,所以为了防止流提取时的频繁扩容,我们需要建立流提取的防止扩容频繁的操作 

完整代码:

扩容机制解读: 
  • 设立了一个足够大的字符数组,用字符数组进行扩容的相关操作。
  • 第二个if是表示如果字符串较短,那么将会在字符串的后面加上\0,在交给string 类型的s 并且使用+=符号读取字符串的内容方便string空间的开辟
  • 而+= 符号会自动读取数组内部的字符串的大小,开辟相对因的空间,进行空间的一次性开辟操作!
  • 其中第一个if表示如果字符串太长的话,那就分几次对string类型对象s的空间进行开辟
  • 如果等于127则127放置\0并且将这127个字符给string类型的s 使用+=进行空间的开辟,随后i=0 进入插入的循环,进行第二次轮的插入操作和空间的开辟操作!
getline() 

getline()的功能其实和流提取相差不多,不过不同的是getline()可以读取空格符号,所以getline()一般用于读取带有空格符号的字符串。

istream& getline(istream& in,string& s)
{
   s.clear();
   
   char ch;
   
   ch = in.get();
   while(ch != '\n')
   {
      s += ch;
      ch = in.get();
   }

   return in;

}

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++】什么是类与对象?

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:C ⚙️操作环境:Visual Studio 2022 目录 面向对象概述 封装 继承 多态 类 类是什么? C中类的引入 C中类的定义 类的两种定义方式: 1.声明和定义全部放在类体中 2.类声明与成员函数定义分别放在不同的工程文件中…

C语言数据结构之二叉堆

愿你千山暮雪 海棠依旧 不为岁月惊扰平添忧愁 &#x1f3a5;前期回顾-二叉树 &#x1f525;数据结构专栏 期待小伙伴们的支持与关注&#xff01;&#xff01;&#xff01; 目录 前期回顾 二叉堆的概念及结构 二叉堆的创建 顺序表的结构声明 顺序表的创建与销毁 二叉堆的插入 …

CUDA安装及环境配置——最新详细版

确定安装版本 在安装之前呢&#xff0c;我们需要确定三件事 第一&#xff1a;查看显卡支持的最高CUDA的版本&#xff0c;以便下载对应的CUDA安装包 第二&#xff1a;查看对应CUDA对应的VS版本&#xff0c;以便下载并安装对应的VS版本&#xff08;vs需要先安装&#xff09; 第三…

基于Jupyter快速入门Python,Numpy,Scipy,Matplotlib

文章目录 Jupyter 和 Colab 笔记本PythonPython 版本基础数据类型数字Numbers布尔值Booleans字符串Strings 容器列表List字典Dictionaries集合Sets元组Tuples 函数类 Numpy数组Array数组索引Array indexing数据类型DatatypesArray math广播Broadcasting Scipy图像操作MATLAB文件…

IOS覆盖率报告info文件解读

一&#xff0c;IOS覆盖率报告的生成 在做前端精准测试的时候&#xff0c;对于iOS端&#xff0c;通常会做如下操作&#xff1a; &#xff08;1&#xff09;合并覆盖率数据 如下操作&#xff1a; xcrun llvm-profdata merge coverage_file1657885040728.profraw coverage_fil…

Java 可变长参数

可变长参数定义 从 Java5 开始&#xff0c;Java 支持定义可变长参数&#xff0c;所谓可变长参数就是允许在调用方法时传入不定长度的参数。可变长参数允许方法接受任意多个相同类型的参数&#xff0c;在方法内部可以将这些参数视为数组来处理。可变长参数通过省略号&#xff0…

【洛谷 P8668】[蓝桥杯 2018 省 B] 螺旋折线 题解(数学+平面几何)

[蓝桥杯 2018 省 B] 螺旋折线 题目描述 如图所示的螺旋折线经过平面上所有整点恰好一次。 对于整点 ( X , Y ) (X, Y) (X,Y)&#xff0c;我们定义它到原点的距离 dis ( X , Y ) \text{dis}(X, Y) dis(X,Y) 是从原点到 ( X , Y ) (X, Y) (X,Y) 的螺旋折线段的长度。 例如 …

java SSM汽车租赁管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM汽车租赁管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用…

【Docker】了解Docker Desktop桌面应用程序,TA是如何管理和运行Docker容器(3)

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Docker容器》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…

MySQL实战:SQL优化及问题排查

有更合适的索引不走&#xff0c;怎么办&#xff1f; MySQL在选取索引时&#xff0c;会参考索引的基数&#xff0c;基数是MySQL估算的&#xff0c;反映这个字段有多少种取值&#xff0c;估算的策略为选取几个页算出取值的平均值&#xff0c;再乘以页数&#xff0c;即为基数 查…

Springboot+vue的项目申报管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的项目申报管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09…

盲盒抽卡机小程序——开启神秘之旅!

亲爱的朋友们&#xff0c;欢迎来到盲盒抽卡机小程序&#xff01;这里&#xff0c;是一个充满神秘与惊喜的世界&#xff0c;让你随时随地体验抽卡的乐趣。在这里&#xff0c;你可以轻松尝试各种盲盒&#xff0c;发现隐藏的宝藏&#xff0c;感受心跳加速的刺激。 【丰富多样的盲…

ARM单片机中程序在ROM空间和RAM空间的分布(分散加载文件,Scatter-Loading Description File)

对于 K e i l u V i s i o n I D E Keil\quad uVision\quad IDE KeiluVisionIDE&#xff0c;程序编译好之后&#xff0c;代码的下载位置&#xff08; R O M ROM ROM空间&#xff09;以及代码运行的时候使用的 R A M RAM RAM空间&#xff08; R A M RAM RAM空间&#xff09;默认…

华为云云耀云服务器L实例评测|用PHP从数据库到后端到前端完整实现一个中秋节祝福语项目

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师…

考虑到通信链路中断的(Delay Tolerant Network, DTN)

文章目录 A Study of DTN for Reliable Data Delivery from Space Station to Ground Stationabstractintroduction An Analytical Framework for Disruption of Licklider Transmission Protocol in Mars Communicationsabstract本文贡献 OVERVIEW OF RELIABLE DATA TRANSMISS…

Excel转pdf

1、excel-内存值--Workbook 转pdf /** * excel To pdf * * param outPath 输出路径 * param workbook excel-内存值 * throws IOException */ public static void excelToPdf(String outPath,Workbook workbook) throws IOException, DocumentException { Document documentnul…

Android studio Gradle下载失败,如何手动配置解决该问题详解

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂&#xff0c;风趣幽默"&#xff0c;感觉非常有意思,忍不住分享一下给大家。 &#x1f449;点击跳转到教程 前言&#xff1a; 今天在打开公司一个项目时&#xff0c;突然要重新下载相关的gradle&am…

图机器学习(4)-面向连接层面的人工特征工程

0 问题定义 通过已经连接去猜未知连接&#xff1a; 有两个思路&#xff1a; &#xff08;1&#xff09;直接提取link的特征&#xff0c;把link变成D维向量&#xff1b; &#xff08;2&#xff09;把link两端节点的D维向量拼在一起&#xff0c;缺点&#xff1a;丢失了link本身…

CSAPP-程序的机器级表示

文章目录 概念扫盲思想理解经典好图安全事件 概念扫盲 1.汇编代码使用文本格式&#xff0c;相较于汇编的二进制可读性更好 2.程序内存包括&#xff1a;可执行的机器代码、操作系统需要的信息、管理过程调用和返回的运行时栈、用户分配的内存块 3.链接器为函数调用找到匹配的可…

基于SpringBoot宠物领养系统的设计与实现(代码+数据库+文档)

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一、研究背景…