文章杂记 | C++动态内存分配

news2024/12/25 9:28:01

1、C++类和动态内存分配

https://blog.csdn.net/u011381222/article/details/137734622
Stringbad.h

#pragma once
#ifndef STRINGBAD_
#define STRINGBAD_

#include<iostream>

using namespace std;

class Stringbad
{
private:
    char* str;//字符串地址
    int len;    //长度
    static int num_strings; //字符串个数
public:
    Stringbad(const char* s);
    Stringbad();
    ~Stringbad();
    Stringbad(const Stringbad&);
    friend ostream& operator<<(ostream& os, Stringbad& t);
};

#endif

Stringbad.cpp

#define _CRT_SECURE_NO_WARNINGS

#include"stringbad.h"
#include<cstring>

int Stringbad::num_strings = 0;

Stringbad::Stringbad(const char* s)
{
    len = strlen(s);
    str = new char[len + 1];
    strcpy(str, s);

    num_strings++;
    cout << " " << num_strings << ": \"" << str << ".\"" << endl;
}

//没用到
//Stringbad& Stringbad::operator=(const Stringbad& t) { 
//    delete[] str;
//    len = t.len;
//    str = new char[len + 1];
//    strcpy(str, t.str);
//    return *this;
//}

Stringbad::Stringbad(const Stringbad& s) {
    len = s.len;
    str = new char[len + 1];
    strcpy(str, s.str);
    num_strings++;
}

Stringbad::Stringbad()
{
    len = 4;
    str = new char[4];
    strcpy(str, "C++");
    num_strings++;
}

Stringbad::~Stringbad()
{
    cout << " \"" << str << "\"object deleted." << endl;
    --num_strings;
    cout << " " << num_strings << " left. " << endl;
    delete[] str;
}

ostream& operator<<(ostream& os, Stringbad& t)
{
    os << t.str;
    return os;
}

main.cpp

#define _CRT_SECURE_NO_WARNINGS

#include"stringbad.h"
#include<iostream>

void callme1(Stringbad& st);
void callme2(Stringbad st);

int main(void)
{
    Stringbad  a1("Hello world");
    Stringbad  a2("Good morning");
    Stringbad a3;

    cout << a1 << a2 << a3;
    callme1(a1);
    callme2(a2);
}

void callme1(Stringbad& st)
{
    cout << "String passed by reference: " << st << endl;
}
void callme2(Stringbad st)
{
    cout << "String passed by value: " << st << endl;
}

1、错误 C4996 ‘strcpy’: This function or variable may be unsafe.
1)使用 strcpy_s 代替 strcpy:
这是推荐的方法。strcpy_s 函数需要额外的参数来指定目标缓冲区的大小,这有助于避免溢出。修改代码如下:原来的代码可能是这样的:

char dest[50];
const char* src = "This is the source";
strcpy(dest, src);

修改后的代码:

char dest[50];
const char* src = "This is the source";
strcpy_s(dest, sizeof(dest), src);

这里 sizeof(dest) 是用来传递目标数组 dest 的大小。
2)定义宏 _CRT_SECURE_NO_WARNINGS:
如果你决定继续使用 strcpy 而不是 strcpy_s,可以通过定义宏 _CRT_SECURE_NO_WARNINGS 来禁止这类警告。这可以在项目的编译器设置中完成,或者在代码中直接定义

在项目设置中定义:
在 Visual Studio 中,右键点击项目->属性->C / C+±>预处理器->预处理器定义,添加 _CRT_SECURE_NO_WARNINGS。
在代码中定义:
在包含任何标准头文件之前添加以下行:

#define _CRT_SECURE_NO_WARNINGS

选择哪种方法取决于你的需求。如果是新项目或安全非常重要的项目,使用 strcpy_s 或其他更安全的函数是更好的选择。如果是为了保持代码的兼容性或者修改成本考虑,可能会选择定义 _CRT_SECURE_NO_WARNINGS 宏。不过,请注意,忽略这些安全警告可能会导致安全风险

2、函数调用:
callme1(a1); —— 由于是按引用传递,因此不会触发构造函数或赋值运算符,直接传递 a1 的引用

callme2(a2); —— 由于是按值传递,将触发拷贝构造函数以创建 st 的局部副本,并没有直接调用重载的赋值运算符
函数返回对象时也是调用拷贝构造函数: 无优化时的行为 当函数返回一个局部对象时,理论上会通过拷贝构造函数创建一个临时对象作为函数的返回值,因为局部变量没了

返回值优化(RVO)和命名返回值优化(NRVO)
RVO:直接在返回值应存储的位置构造临时对象
NRVO:当返回的是一个局部命名对象(如上例中的temp),编译器可能直接在调用方的返回值位置构造这个命名对象

3、返回引用需要特别注意的是,不能返回局部对象的引用,因为当函数执行完毕后,局部对象将会被销毁,而返回的引用则指向了一个已经不存在的对象

#include <iostream>
#include <vector>

using namespace std;

class Store {
private:
    vector<int> data;
public:
    Store() {
        // 假设初始化一些数据
        data.push_back(10);
        data.push_back(20);
    }

    // 返回指定索引处数据的引用
    int& accessData(int index) {
        return data[index];
    }

    // 显示所有数据
    void displayData() {
        for (int i : data) {
            cout << i << " ";
        }
        cout << endl;
    }
};

int main() {
    Store myStore;
    myStore.displayData();  // 显示数据:10 20

    // 调用函数并修改返回的引用,左边也需要是一个引用
    int& ref = myStore.accessData(1);
    ref = 100; // 修改引用的值,相当于修改了存储在Store中的数据

    myStore.displayData();  // 显示修改后的数据:10 100

    return 0;
}

4、

String& String::operator=(const char* st)
{
    delete[] str;
    len = strlen(st);
    str = new char[len + 1];
    strcpy(str, st);
    return *this;
}

istream& operator>>(istream& is, String& str1)
{
    char temp[String::CINLIM];
    is.get(temp, String::CINLIM);

    if (is)
        str1 = temp;
    while (is && is.get() != '\n')
        continue;
    return is;
}

istream.get() 是 C++ 中用于从输入流(例如 cin)读取单个字符的函数。它通常与 char 类型的变量一起使用,以便逐个读取输入的字符

#include <iostream>
using namespace std;

int main() {
    char ch;

    cout << "Enter a character: ";
    cin.get(ch); // 从标准输入读取一个字符并将其存储在变量 ch 中

    cout << "You entered: " << ch << endl;

    return 0;
}
 istream& get (char& c, streamsize n);

这个版本的 get 函数从输入流中读取最多 n 个字符,并将它们存储在字符数组 c 中。它返回输入流对象的引用

char temp[String::CINLIM];
is.get(temp, String::CINLIM);

在这里,我们声明了一个名为 temp 的字符数组,用于存储从输入流中读取的数据。String::CINLIM 是一个常量,它代表了可以读取的最大字符数。然后,我们使用 is.get() 函数从输入流中读取最多 String::CINLIM - 1 个字符,并将它们存储在 temp 中

if (is)
	str1 = temp;

这个条件语句检查输入流的状态。如果读取操作成功(即没有出现错误),则将 temp 中的数据赋值给 str1 对象。这意味着,如果输入流中有足够的字符可供读取,并且没有其他错误发生,那么 str1 对象将被赋予输入流中的数据

while (is && is.get() != '\n')
	continue;

在这里,我们检查输入流的状态以及当前读取的字符。如果输入流仍然有效(即没有遇到文件结束符或其他错误),并且当前读取的字符不是换行符 \n,则继续读取下一个字符。这样做的目的是清除输入流中的额外字符,直到遇到换行符为止,以确保输入流处于正确的状态

5、 cout<<String::howmany()<<endl;对静态类成员函数,正常类成员函数使用 实例化对象.类成员函数的方式调用

6、一个类对象使用定位new运算符指定存储的的位置为p
调用默认构造函数

p1 = new(p) A;

delete可以与常规的new运算符配合使用,但是不能与new运算符配合使用,因为常规new运算符开辟了内存空间,而定位new运算符是在别处开辟的内存空间

并没有为定位new运算符在该内存块中创造的对象调用析构函数,因此,我们需要显式地为定位new运算符创建的对象调用析构函数,显示调用析构函数时,必须知道要销毁的对象

p1->~A();
A *p3 = new(p + sizeof(A)) A("C",6);

避免p3调用时就会将原本p1的内容进行覆盖
对于我们使用定位new运算符创建的对象,应该使用与创建顺序相反的顺序进行删除,因为晚创建的对象可能依赖于早创建的对象,并且需要在所有的对象都被销毁后,才能释放用于存储这些对象的缓冲区

2、C++的动态分配内存

https://blog.csdn.net/qq_45491628/article/details/131335613

2.1 内存分区

1、内存分区:
在C++中,内存区分为5个区,分别是堆、栈、自由存储区、全局/静态存储区、常量存储区;
在C中,C内存区分为堆、栈、全局/静态存储区、常量存储区;

malloc是从堆上开辟空间,而new是从自由存储区开辟;(自由存储区是 C++抽象出来的概念,不仅可以是堆,还可以是静态存储区)。

  1. C内存分布
    BSS段: 用来存放程序中未初始化的全局变量。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。
    数据段:用来存放程序中已初始化的全局变量。数据段属于静态内存分配。
    代码段:用来存放程序执行代码。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等(相当于文字常量区??)。
    堆:堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc/free等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张)/释放的内存从堆中被剔除(堆被缩减)
    栈:栈又称堆栈, 存放程序的局部变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外在函数被调用时,栈用来传递参数和返回值。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。

  2. C++内存分布
    栈:内存由编译器在需要时自动分配和释放。通常用来存储局部变量和函数参数。
    堆:内存使用new进行分配使用delete或delete[]释放。如果未能对内存进行正确的释放,会造成内存泄漏。但在程序结束时,会由操作系统自动回收。
    自由存储区:使用malloc进行分配,使用free进行回收。和堆类似。
    全局/静态存储区:全局变量和静态变量被分配到同一块内存中,C语言中区分初始化和未初始化的,C++中不再区分了。
    常量存储区:存储常量,不允许被修改。

  3. 区分栈和堆
    管理方式:栈由编译器管理,堆由程序员控制。
    空间大小:VC下栈默认是1MB,堆在32位的系统上可以达到4GB。
    碎片问题:栈不会产生碎片,堆会产生碎片。
    生长方向:堆向着内存地址增加的方向增长,栈向着内存地址减少的方向增长。
    分配方式:堆是动态分配的。栈是静态分配和动态分配的,静态分配由编译器完成,动态分配由alloca函数进行分配,由编译器释放。
    分配效率:栈的分配效率非常高。堆的分配机制很复杂,效率比栈要低得多。

  4. c语言存储类别,c++存储方案
    C语言有4种存储类别:自动的(auto)、静态的(static)、寄存器(register)、外部的(exteren)
    C++(自C++11起)使用四种不同的方案来存储数据,这些方案的区别就在于数据保留在内存中的时间。
    自动存储: 在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。它们在程序开始执行所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。C++有两种存储持续性为自动的变量。
    静态存储: 在函数定义外被定义的变量和使用关键字 static 定义的变量。它们在程序整个运行过程中都存在。C++有3种存储持续性为静态的变量。

int  global = 100; //static duration,外部链接性
static int in_file = 10;  //static duration, 内部链接性
int main(){
    static int count = 1; // static duration,无链接性
    ……
}

C++为静态存储持续性变量(静态变量)提供了3种链接性:外部链接性(可在其他文件中访问)、内部链接性(只能在当前文件中访问)和无链接性(只能在当前函数或代码块中访问)。这3种链接性都在整个程序执行期间存在,与自动变量相比,它们的寿命更长。如果没有显示的初始化变量,默认情况下,静态数组、结构的每个元素或成员都被设置为0

动态存储: 用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序结束为止。这种内存的持续性为动态,有时被称为自由存储(free store) 或 堆(heap)。

线程存储(C++11):当前,多核处理器很常见,这些CPU可同时处理多个执行任务。这让程序能够将计算放在可并行处理的不同线程中。如果变量是使用关键字thread_local声明的,则其生命周期与所属的线程一样长

2.2 new和delete

2、malloc 和 allocator 都是用于在 C++ 中动态分配内存的工具,但它们有一些不同点

  1. 语言环境:
    malloc 是 C 语言标准库中的函数,需要包含头文件 <cstdlib> 或 <stdlib.h>。
    allocator 是 C++ 标准库中的一个模板类,定义在头文件 <memory> 中,属于 C++ 的一部分。
  2. 返回类型:
    malloc 的返回类型是 void*,需要进行显式的类型转换。
    allocator 的 allocate 函数返回的是特定类型的指针,不需要进行类型转换。
  3. 内存大小:
    malloc 必须显式地指定要分配的字节数。
    allocator 使用模板参数来推断要分配的对象的大小,不需要显式指定。
  4. 初始化:
    malloc 分配的内存不会被初始化,内容是未定义的。
    allocator 分配的内存会被初始化为对象的默认值(例如,int 类型会被初始化为 0)。
  5. 释放内存:
    malloc 使用 free 函数来释放分配的内存。
    allocator 使用 deallocate 函数来释放分配的内存。
  6. 异常安全:
    malloc 和 free 不是异常安全的,如果分配失败会返回空指针,需要手动处理。
    allocator 的 allocate 函数可以抛出异常,但其析构函数会自动释放已分配的内存,因此更为安全。

总的来说,malloc 和 allocator 都可以用于动态分配内存,但 allocator 是 C++ 中更为灵活和安全的选择,尤其在面对异常情况时,allocator 更能保证内存的正确管理

3、malloc 和 new 都是用于在 C++ 中动态分配内存的方法,但它们有一些区别。

  1. 语言环境:
    malloc 是 C 语言标准库中的函数,需要包含头文件 <cstdlib> 或 <stdlib.h>。
    new 是 C++ 关键字,不需要包含额外的头文件。
  2. 返回类型:
    malloc 的返回类型是 void*,需要进行显式的类型转换。
    new 返回的是指定类型的指针,不需要进行类型转换。
  3. 内存大小:
    malloc 必须显式地指定要分配的字节数。
    new 会根据所需类型的大小来分配内存,不需要显式指定。
  4. 构造函数和初始化:
    malloc 分配的内存不会被初始化,内容是未定义的。
    new 分配的内存会调用对象的构造函数进行初始化,并初始化为对象的默认值。
  5. 释放内存:
    malloc 使用 free 函数来释放分配的内存。
    new 使用 delete 关键字来释放分配的对象,或者 delete[] 关键字来释放数组。
  6. 异常安全:
    malloc 和 free 不是异常安全的,需要手动管理内存的分配和释放。
    new 和 delete 是异常安全的,如果 new 分配失败会抛出 std::bad_alloc 异常,而 delete 会自动调用对象的析构函数来释放资源。
    (ew 初始化对象,调用对象的构造函数,对应的delete调用相应的析构函数 ;malloc 仅仅分配内存,free仅仅回收内存)

总的来说,虽然 malloc 和 new 都可以用于动态分配内存,但在 C++ 中,通常推荐使用 new 来进行内存分配,因为它更加安全、方便,并且支持对象的构造和析构

4、要初始化常规结构或数组,需要使用大括号的列表初始化,还可将列表初始化用于单值变量。这要求编译器支持C++11

struct where {double x; double y; double z;};
where * one = new where {2.5,5.3,7.2};  // C++11
int * ar = new int [4]{2,4,6,7};        //C++11

int *pin = new int {}; // *pi set to 6
double * pdo = new double {99.99} ;// *pd set to 99.99

2.3 类和动态内存分配

5、在申请自由空间内存的时候,如果空间被耗尽,new就会失败,普通的new空间失败会throw bad_alloc异常,但使用定位new new(nothrow)之后,不会抛出异常,而是在申请空间失败的时候返回一个空指针

6、静态数据成员独立于对象被保存,所以为整个对象分配内存时,只为指针和其它成员分配内存(注意是为指针变量分配内存,指针指向的内存由构造函数分配),创建对象将调用构造函数,后者分配用于指针的内存。然后,当程序不再需要该对象时,使用delete删除它。这将只释放用于指针和len成员的空间,并不释放指针指向的内存,而该任务将由析构函数来完成

在这里插入图片描述

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

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

相关文章

前端三剑客 HTML+CSS+JavaScript ④ HTML标签

祝你先于春天&#xff0c;翻过此间铮铮山峦 —— 24.4.23 一、HTML排版标签 1.标题标签 h1~h6 标签含义&#xff1a;标题 单/双标签&#xff1a;双 主要用前三个 2.段落标签 p 标签含义&#xff1a;段落 单/双标签&#xff1a;双 3.div 没有任何含…

Day17-Java进阶-网络编程(IP, 端口, 协议)TCP和UDP三次握手和四次挥手

1. 网络编程介绍 1.1 初始网络编程 1.2 网络编程三要素 1.2.1 IP InetAddress 的使用 package com.itheima.Inetaddress;import java.net.InetAddress; import java.net.UnknownHostException;public class InetAddressDemo1 {/*static InetAddress getByName(String host) 确…

Redis分布式锁 - 基于Jedis和LUA的分布式锁

先基于单机模式&#xff0c;基于Jedis手工造轮子实现自己的分布式锁。 首先看两个命令&#xff1a; Redis 分布式锁机制&#xff0c;主要借助 setnx 和 expire 两个命令完成。 setnx命令: setnx 是 set if not exists 的简写。将 key 的值设为 value &#xff0c;当且仅当…

基于PaddlePaddle平台训练物体分类——猫狗分类

学习目标&#xff1a; 在百度的PaddlePaddle平台训练自己需要的模型&#xff0c;以训练一个猫狗分类模型为例 PaddlePaddle平台&#xff1a; 飞桨&#xff08;PaddlePaddle&#xff09;是百度开发的深度学习平台&#xff0c;具有动静统一框架、端到端开发套件等特性&#xf…

Mac装虚拟机好不好 Mac装虚拟机和装Windows系统一样吗 PD虚拟机

随着跨系统操作的不断发展&#xff0c;虚拟机技术在生产力领域扮演着越来越重要的角色。Mac作为一款主流的操作系统&#xff0c;也有着运行虚拟机的能力。接下来给大家介绍Mac装虚拟机好不好&#xff0c;Mac装虚拟机和装Windows系统一样吗的具体内容。 一、Mac装虚拟机好不好 …

直接用表征还是润色改写?LLM用于文生图prompt语义增强的两种范式

直接用表征还是润色改写&#xff1f;LLM用于文生图prompt语义增强的两种范式 导语 目前的文生图模型大多数都是使用 CLIP text encoder 作为 prompt 文本编码器。众所周知&#xff0c;由于训练数据是从网络上爬取的简单图文对&#xff0c;CLIP 只能理解简单语义&#xff0c;而…

linux文件句柄数满,linux文件句柄数超出系统限制怎么办?

1、问题阐述&#xff1a; too many open files&#xff1a;顾名思义即打开过多文件数。 不过这里的files不单是文件的意思&#xff0c;也包括打开的通讯链接(比如socket)&#xff0c;正在监听的端口等等&#xff0c;所以有时候也可以叫做句柄(handle)&#xff0c;这个错误通常…

自动化立体库安全使用管理制度

导语 大家好&#xff0c;我是智能仓储物流技术研习社的社长&#xff0c;老K。专注分享智能仓储物流技术、智能制造等内容。 新书《智能物流系统构成与技术实践》 完整版文件和更多学习资料&#xff0c;请球友到知识星球 【智能仓储物流技术研习社】自行下载 关于自动化立体库安…

Linux--系统烧写

前面几篇文章&#xff0c;已经搞定了Linux移植三巨头&#xff1a;uboot、kernel(包含dtb)和rootfs&#xff0c;除了uboot是烧写在SD中的&#xff0c;其它的都是在ubuntu虚拟机的nfs服务器中&#xff0c;运行时必须通过网络将这些文件加载到开发板的内存中运行。 本篇就来研究&a…

JAVAEE—HTTP

文章目录 HTTP导读HTTP解析HTTP的格式分析环境准备 HTTP请求格式首行headerHostContent-LengthContent-TypeUser-Agent (简称 UA)RefererCookie 空行body HTTP响应格式认识HTTP的方法POST方法POST和GET的区别第一&#xff1a;用处第二&#xff1a;传递数据第三&#xff1a;GET不…

QWidget | Qt::WindowType 枚举的取值及意义QFlags 模板类详解

01 与 QWidget 类有关的部分类的继承图 3、QObject 是所有 Qt 对象的基类,QPaintDevie 是所有可绘制对象的基类。 4、QWidget 类是所有用户界面对象的基类,QWidget 及其子类是开发桌面应用的核心,这些类都位于 QtWidgets 模块内,注意:QtWidgets 是模块,QWidget 是类(少一…

装饰模式【结构型模式C++】

1.概述 装饰模式是一种结构型设计模式&#xff0c; 允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。 2.结构 抽象构件&#xff08;Component&#xff09;角色&#xff1a;定义一个抽象接口以规范准备接收附加责任的对象。具体构件&#xff08;Concrete…

区块链安全应用------压力测试

测试要求&#xff1a; 1. 对以下AccountManager智能合约进行压测(基础要求set函数测试&#xff0c;balanceOf涵为20分加分项)2. 在本地链进行测试&#xff0c;需要监控本地进程的资源使用情况。每个进程的multiOutput属性为Avg3. 需要将每一个更改的配置文件截图&#xff0c;和…

指针(5)

前言 本节是有关指针内容的最后一节&#xff0c;本节的内容以讲解指针习题为主&#xff0c;那么就让我们一起来开启本节的学习吧&#xff01; sizeof和strlen的对比 1.sizeof 我们在学习操作符的时候&#xff0c;学习了sizeof。sizeof存在的意义是用来计算变量所占用的内存空…

智慧火电厂合集 | 数字孪生助推能源革命

火电厂在发电领域中扮演着举足轻重的角色。主要通过燃烧如煤、石油或天然气等化石燃料来产生电力。尽管随着可再生能源技术的进步导致其比重有所减少&#xff0c;但直至 2023 年&#xff0c;火电依然是全球主要的电力来源之一。 通过图扑软件自主研发 HT for Web 产品&#xf…

百度智能云千帆 ModelBuilder 技术实践系列:通过 SDK 快速构建并发布垂域模型

​百度智能云千帆大模型平台&#xff08;百度智能云千帆大模型平台 ModelBuilder&#xff09;作为面向企业开发者的一站式大模型开发平台&#xff0c;自上线以来受到了广大开发者、企业的关注。至今已经上线收纳了超过 70 种预置模型服务&#xff0c;用户可以快速的调用&#x…

企业微信hook接口协议,开放平台id转企业用户id

开放平台id转企业用户id 参数名必选类型说明uuid是String每个实例的唯一标识&#xff0c;根据uuid操作具体企业微信 请求示例 {"uuid":"3240fde0-45e2-48c0-90e8-cb098d0ebe43","openid":["woO9o4EAAAUg47yCUh1mDYVh71AJ8R3w"] } …

陪诊小程序的市场潜力与发展趋势研究

随着社会的快速发展和人口老龄化的加剧&#xff0c;医疗服务需求日益增长&#xff0c;而陪诊服务作为医疗服务的重要补充&#xff0c;正逐渐受到人们的关注和青睐。陪诊小程序作为一种便捷、高效的陪诊服务平台&#xff0c;其市场潜力和发展趋势值得关注。 一、市场潜力分析 人…

【现代交换原理与通信网技术】期末突击

文章目录 自己老师画的重点1. 程控交换机结构2. 测试模拟电路的七项功能3.中继电路的六项功能4.数字用户电路和模拟用户电路比较5.路由规划的基本原则6.七路信令的结构7.随路信令和公共信道信令8.软交换9.无极网和分级网10.路由选择.流量控制的原则/方法11.电路交换&&分…

微软Phi-3,3.8亿参数能与Mixtral 8x7B和GPT-3.5相媲美,量化后还可直接在IPhone中运行

Phi-3系列 Phi-3是一系列先进的语言模型&#xff0c;专注于在保持足够紧凑以便在移动设备上部署的同时&#xff0c;实现高性能。Phi-3系列包括不同大小的模型&#xff1a; Phi-3-mini&#xff08;38亿参数&#xff09; - 该模型在3.3万亿个令牌上进行训练&#xff0c;设计得足…