Linux环境C语言开发基础

C语言是一门面向过程的计算机编程语言,与C++、C#、Java等面向对象编程语言有所不同。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、仅产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。
C语言诞生于美国的贝尔实验室,由丹尼斯·里奇(Dennis MacAlistair Ritchie)以肯尼斯·蓝·汤普森(Kenneth Lane Thompson)设计的B语言为基础发展而来。在它的主体设计完成后,汤普森和里奇用它完全重写了UNIX,且随着UNIX的发展,c语言也得到了不断的完善。1989年,诞生了第一个完备的C标准,简称“C89”,也就是早期的“ANSI C”,截至2023年,最新的 ISO的C语言标准为"C2x"。 👴
作为嵌入式工程师,跟C语言打交道是不可避免的。本篇文章抛弃了根据C语言语法点逐个分析讲解的做法,采用依托Liunx开发环境,在介绍完基本概念之后,直接从实际的问题入手讲解,也就是说是“倒过来讲”。因为我们依托的是Linux环境,我们使用的C库其实是 GNU C。

Linux开发环境搭建

现在可以使用WSL + VS Code Remote来搭建windows与Linux的交叉开发环境,各位如果喜欢这种新的方式可以移步这里。我下面要介绍的是用PC机win10+虚拟机64位Ubuntu 14.04+Source insight, smba来搭建。

首先安装VMware,下载Ubuntu镜像文件,ubuntu-14.04.5-desktop-amd64.iso。新建虚拟机,根据需要选择磁盘容量之后,点击完成。

修改虚拟机设置,根据后续需要设置虚拟机内存大小,在 CD/DVD(SATA)项,选择光盘提供的 ubuntu-14.04.5-desktop-amd64.iso 镜像文件。网络选择桥接模式。点击运行之后,按步骤安装Ubuntu即可。


按住键盘Ctrl + Alt + t ,打开一个命令解析器,输入sudo passwd root,添加一个root密码,这儿密码配置成1。设置开机时可以选择root 登录,执行命令gedit /usr/share/lightdm/lightdm.conf.d/50-unity-greeter.conf,打开编辑50-unity-greeter.conf 文件,在打开文件中添加如下信息,来设置登录时可以选择用户登录,如下图所示:

然后再在shell终端输入命令gedit /root/.profile,添加tty -s &&信息,如下

点击保存,重启后既可以选择root登陆了。


为了方便在windows主机编辑代码,可以安装samba服务器,当然,如果您使用Vim比较熟悉的话,作为嵌入式工程师直接用Vim修改系统代码即可。输入命令apt-get install samba smbclient,遇提示[Y/n]敲回车默认安装。修改配置文件,编辑smb.conf 文件:vi /etc/samba/smb.conf。在配置文件的最末尾加上下面内容:

设置Samba用户和密码,命令smbpasswd -a root 接着按提示输入密码,重新启动samba服务,命令/etc/init.d/samba restart,查看Ubuntu ip地址,命令ifconfig:

最后在电脑-计算机-下右键添加一个网络位置,输入:\\上面查看的ip地址\share,点击下一步

修改网络名称,点击下一步,按提示输入Samba用户名密码,点击确定。

成功完成Samba共享访问后,会看到Ubuntu系统下Samba共享的文件夹。

PS:上面共享目录是整个Ubuntu系统的目录,我们一般只需要访问/home下目录,所以需要修改目录对应的权限如修改qihua目录权限,命令chmod 777 /home/qihua/ 。


在这里说一下Linux下的编译器:gcc。gcc的编译过程:C源文件-预处理-编译-汇编-链接-可执行文件。

  • 预处理:gcc -E hello.c > hello.i (重定向保存,预处理文件后缀为.i)

  • 编译:gcc -S hello.i (默认产生后缀为.s的目标文件)

  • 汇编:gcc -c hello.s (默认产生后缀为.o的目标文件)

  • 链接:gcc hello.o -o hello (指定生成可执行文件的名字)

  • 可执行文件:./hello 执行当前目录下的可执行文件hello

当然,我们在实际编译的过程中不需要这样一步一步执行,使用 gcc hello.c -o myhello (指定生成可执行文件的名字)即可将源文件编译成可执行文件,也可以使用make hello ,使用默认makefile生成可执行文件hello。

C基本概念

在 C 语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间,以及如何解释存储的位模式。C中的数据类型可以分为以下几种:

  • 基本数据类型(short;int; long;float;double;char)

  • 构造类型(array;struct;union;enum)

  • 指针类型

  • 空类型(void)

float类型数无法和一确切的数比较是否相等(float类型本身并不精确,),不同形式的0值,数值0,字符'0',字符串"0",转义符0'\0',以及指针空NULL。


常量是在程序执行过程中值不会发生变化的量,包括整型常量,实型常量,字符常量,字符串常量,标识常量。

  • 整型常量:1790,34,56。

  • 实型常量:3.14,2.56,0.67。

  • 字符常量:单引号引起来的单个字符或转义字符,如'a','D','\n','\0','\ddd'(三位八进制数),'\xhh'(两位16进制数) '\015','\x7f','\018'(非法!)。

  • 字符串常量:双引号引起来的一个或多个字符组成的序列,如"helloworld","a",""(空字符串,只有一个'\0'占用一个字节)"abcd\n\021\018"(特殊)。

  • 标识常量:#define,处理在程序预处理阶段,占编译时间,优点是一改全改,缺点是不检查语法,只是单纯的宏体与宏名之间的替换。

变量是用来保存一些特定内容,并且在程序执行过程中值随时会发生变化的量。定义如下图:

  • 标识符:由字母,数字,下划线组成且不能以数字开头的一个标识序列,取标识符尽量做到见名生义。笔者一般采用驼峰命名法,不同公司会有不同的要求。

  • 数据类型:基本数据类型 + 构造类型

  • 值:注意与数据类型匹配,否则会出现精度丢失等问题。

变量可以添加关键字来说明存储类型。

  • auto:默认,自动分配空间,自动回收空间

  • static:静态型,自动初始化为0值或空值,并且其变量的值具有继承性,常用于修饰变量或函数。

  • register:寄存器类型(建议性关键字),register int i = 1; 由编译器决定是否存储在寄存器中, 大小有限制只能用来定义局部变量,32位机器只能定义32位大小的数据类型,如double就不可以,寄存器没有地址,所以无法打印寄存器类型变量的地址进行查看或使用。

  • extern:说明型,不能改变被说明的变量的值或类型。

变量具有生命周期和作用范围,全局变量是作用范围从定义位置开始直到程序结束,变量一直存在于内存中。局部变量的作用范围从申明位置开始直到当前块作用域结束 ,当前块结束后内存资源释放。


C语言包含很多中运算符,参与运算的操作数个数为1的单目运算符,条件运算符,以及赋值运算符。在同一条语句中运算符之间根据优先级排序执行运算顺序。

注意%要求左右操作数必须为整型, == 和 = 的不同区别(别看这个简单,我review别人代码发现过多次这两个用混了),逻辑运算符(&&, ||的短路特性。

作为嵌入式工程师 ,位运算是我们常常需要用到的。

  • 将操作数中第n位置1,其他位不变:num = num | 1 << n;

  • 将操作数中第n位清0,其他位不变:num = num & ~(1 << n);

  • 测试第n位:if(num & 1 << n)

  • 从一个指定宽度为w的数中取出第(m -> n)位:(num << (w-n)) >> (w-n+m)

输入输出与流程控制

从Shell终端输入a, b, c的值,求二次方程的根( , x = (-b +/- sqrt(delta)) / 2a )。代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define WEIGHT  3.0e-23
#define KQ 950

static void root()
{
    float a, b, c;
    float delta;
    float x1, x2;

    printf("please enter for a, b, c: \n");
    scanf("%f%f%f", &a, &b, &c);
    delta = b*b - 4*a*c;
    if(delta < 0)
    {
        fprintf(stderr, "Input error.\n");
        exit(1);
    }

    x1 = (-b + sqrt(delta)) / (2*a);
    x2 = (-b - sqrt(delta)) / (2*a);

    printf("x1 = %f\n", x1);
    printf("x2 = %f\n", x2);

    return;
}

int main()
{
    root();

    exit(0);
}

以上代码演示的是格式化输入输出函数,涉及输入/输出的库函数有以下三类:

  • 格式化输入输出函数:int scanf(const char *format, ...);int printf(const char *format, ...)。注意%s作为输入项非常危险,其不会提示越界问题。

  • 字符输入输出函数: int getchar(void); int putchar(int c);

  • 字符串输入输出函数:char *gets(char *s);int puts(const char *s)。gets函数十分危险,可以用fgets,getline替代


百钱买百鸡:鸡翁一,值钱五;鸡母一,值钱三;三鸡雏,值钱一。百钱买百鸡,问鸡翁,鸡母,鸡雏各几只。代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
static void Buychicken()
{
    int a, b, c;

    for(int a = 0; a < 20; a++)
    {
        for(int b = 0; b <= 33; b++)
        {
            c = 100 - a - b;
            if(c % 3 == 0)
            {
                if(a*5 + b*3 + c/3*1 == 100)
                    printf("🐓 = %d, 母鸡 = %d, 小鸡 = %d\n", a, b, c);
            }
        }
    }

    return;
}
int main()
{
    Buychicken();

    exit(0);
}

以上这个例子for循环和if判断。C语言的流程控制分以下三类:

  • 顺序:语句逐句执行,C语言为面向过程语言,语句顺序执行。

  • 选择:出现一种以上情况,if....else;switch....case。

  • 循环:语句块循环执行,while;do....while;for;if....goto。

还有一些辅助控制的关键字,像是continue不执行下面的语句,回到循环开始;break跳出循环。

指针与函数

"follow me", "basic", "great", "hello", "hi"五个字符串根据strcmp()比较结果进行排序。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char* name[5] = {"follow me", "basic", "great", "hello", "hi"};
    char* tmp = NULL;
    int flag = 0, num;

    for(int i = 0; i < 5; i++)
    {
        printf("%s\n", name[i]);
    }

    for(int i = 0; i < 4; i++)
    {
        for(int j = i+1; j < 5; j++)
        {
            if(strcmp(name[j], name[i]) < 0)
            {
                flag = 1;
                num = j;
            }
        }
        if(flag)
        {
            flag = 0;
            tmp = name[i];
            name[i] = name[num];
            name[num] = tmp;
        }
    }

    printf("\nafter sort:\n\n");
    for(int i = 0; i < 5; i++)
    {
        printf("%s\n", name[i]);
    }

    exit(0);
}

上面这个例子涉及到字符串的存储,要使用一个“hello”字符串,有如下两种方式:

  • char *str = "hello"; str = "world"; puts(str);

  • char str[] = "hello"; strcpy(str, "world"); puts(str);

指针作为C语言中最为重要的概念,它允许我们直接访问某个地址下的数据,下面是几个涉及指针的关键知识点。

  • 指针分不同数据类型的指针,int * ptr与char *ptr在内存中前者指向一个int类型数据的存储地址(一般为32位),而后者指向的是一个char类型数据的存储空间,ptr++在数值上前者增加了4,而后者增加了1。

  • 指针定义时可以使用空类型void* p = NULL,后边使用*(int *)p,强制类型转换之后再获得指针指向数据。

  • 避免野指针问题,内存空间释放之后记得把指向此空间的指针置为空。

指针还允许我们跳到不同的代码块,这个时候指针的名称叫做函数指针。下面这个例子是一个函数指针的例子:

# include <stdio.h>
int Max(int, int);  //函数声明
int main(void)
{
    int(*p)(int, int);  //定义一个函数指针
    int a, b, c;
    p = Max;  //把函数Max赋给指针变量p, 使p指向Max函数
    printf("please enter a and b:");
    scanf("%d%d", &a, &b);
    c = (*p)(a, b);  //通过函数指针调用Max函数
    printf("a = %d\nb = %d\nmax = %d\n", a, b, c);
    return 0;
}
int Max(int x, int y)  //定义Max函数
{
    int z;
    if (x > y)
    {
        z = x;
    }
    else
    {
        z = y;
    }
    return z;
}

指针变量本身也是存在内存的数值,那么指向指针变量的地址就是二级指针,有些面试题喜欢拿这个“套娃”知识点做题,我就不详述了。


在讲函数之前,我简单介绍一下makefile,makefile定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。下面是一个makefile的最简单形式,它告诉编译make工具使用maic.c和tool.c来生成mytool可执行文件:

OBJS=main.o tool.o
CC=gcc
RM=rm -f
CFLAGS+=-c -Wall -g


mytool:$(OBJS)
    $(CC) $^ -o $@

%.o:%.c
    $(CC) $^ $(CFLAGS) -o $@


clean:
    $(RM) *.o mytool -r

下面是一个函数的定义,它在tool.c中定义,它的形式如下图所示:

#include <stdio.h>

#include "tool.h"

void mytool()
{
    printf("tool print\n\n");
}

下面是main.c的内容,main()函数调用了mytool()函数:

#include <stdio.h>
#include <stdlib.h>

#include "tool.h"

int main()
{
    mytool();

    exit(0);
}
  • 函数的传参分为通常所用的值传递,即为函数内部使用的入参为原始数据的副本,还有地址传递,这个时候就可以通过地址来改变原始数据的值。

  • 函数的调用分为正常的嵌套调用和递归调用,嵌套调用就是函数调用别的函数再调用别的函数,嵌套的级数越多对系统资源消耗越大。递归调用就是自己调用自己。

数组与构造类型

数组作为最常见的构造类型。下图是一个二维数的形式:

下面是利用二维数组来实现的矩阵乘法:

#include <stdio.h>
#include <stdlib.h>

#define M   2
#define N   3
#define K   2

static void multiply()
{
    int a[M][N] = {1,2,3,4,5,6};
    int b[N][K] = {1,0,0,1,1,0};
    int c[M][K] = {0};

    for(int i = 0; i < M; i++)
    {
        for(int j = 0; j < K; j++)
        {
            for(int k = 0; k < N; k++)
            {
                c[i][j] += a[i][k] * b[k][j];
            }
        }
    }

    for(int i = 0; i < M; i++)
    {
        for(int j = 0; j < K; j++)
        {
            printf("%d\t", c[i][j]);
        }
        printf("\n");
    }

    return;
}

int main()
{
    multiply();

    exit(0);
}

C标准库提供了一系列字符数组的操作函数,包括:strlen & sizeof; strcpy & strncpy;strcat & strncat;strcmp & strncmp。


我们常见的构造体类型是结构体,当遇到大型软件的时候,最难梳理的往往就是结构体之间的关系。下面是一个结构体的例子,非常简单我就不说语义了,我提一个问题,最后打印的两个sizeof分别是多少?加不加"__attribute__((packed))"对结果有什么影响?知道答案的可以在评论区留言。

struct simple_st
{
    int i;
    float f;
    char ch;
};    //__attribute__((packed));
void func(struct simple_st b)
{
    printf("size of b = %lu\n", sizeof(b));
}

void func2(struct simple_st *p)
{
    printf("size of p = %lu\n", sizeof(p));
}
int main()
{
    //TYPE NAME = VALUE;
    struct simple_st a;
    struct simple_st *p = &a;

    func(a);
    func2(p);
}

比较少用到的就是共用体了,它是允许你通过不同的结构去理解同一片存储区域。你可以看到这个共用体的大小由最大的成员决定。

#include <stdio.h>
#include <stdlib.h>

union test_un
{
    int i;
    float f;
    double d;
    char ch;
};

int main()
{
    union test_un a;
    union test_un *p = &a;
    union test_un arr[3];

    a.f = 345.678;
    printf("size of a = %lu\n", sizeof(a));
    printf("a.f = %f\n", a.f);
    printf("p->f = %f\n", p->f);
    //printf("a.i = %d\n", a.i);

    exit(0);
}

共生体一般会和位域配合使用,能够给使用者提供针对某一个完整数据类型其中某个位段数据的操作,这在车载领域的CAN报文应用广泛,我们可以根据DBC定义的Message编写对应的联合体定义,针对其不同的Signals赋值之后就可以通过CAN控制器发送报文。

union add_un
{
    struct
    {
        unsigned short a;
        unsigned short b;
    }n;
    unsigned int c;
};

int main()
{
    unsigned int i = 0x11223344;
    union add_un a;

    //printf("0x%x\n", ((i >> 16) + (i & 0x0000FFFF)));
    a.c = i;
    printf("0x%x\n", a.n.b);
    printf("0x%x\n", a.n.a);
    printf("0x%x\n", a.n.a + a.n.b);

    exit(0);
}

最后,聊聊枚举。不要以为枚举有什么好讲的,如果你阅读过Linux内核的代码,你就会发现它大量的使用了宏定义以及枚举等预编译技术,它们虽然会带来编译时长的增加,但是却会带来诸如易读以及执行速度的优势。

#include <stdio.h>
#include <stdlib.h>

enum day
{
    MON = 1,    //默认从0开始往下排
    TUS,
    WES,
    THR,
    FRI = 1,
    SAT,
    SUN
};

enum    //当作宏来使用
{
    STATE_RUNNING = 1,
    STATE_CANCELLED,
    STATE_OVER
};

struct job_st
{
    int id;
    int state;
    time_t start, end;
};

int main()
{
    struct job_st job1;

    job1.state = STATE_OVER;
    switch (job1.state)
    {
        case STATE_RUNNING:
            break;
        case STATE_CANCELLED:
            break;
        case STATE_OVER:
            break;
        
        default:
            break;
    }


    #if 0
    enum day a = SUN;

    a = TUS;
    printf("%d\n", a);
    #endif

    exit(0);
}

十六宿舍 原创作品,转载必须标注原文链接。
©2023 Yang Li. All rights reserved.
欢迎关注 『十六宿舍』 ,大家喜欢的话,给个 👍 ,更多关于嵌入式相关技术的内容持续更新中。

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

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

相关文章

进程和线程的区别和联系

进程和线程的区别和联系1. 认识线程2. 进程和线程的关系3. 进程和线程的区别4. 线程共享了进程哪些资源1. 上下文切换2. 线程共享了进程哪些资源1.代码区2. 数据区3. 堆区1. 认识线程 线程是进程的一个实体,它被包含在进程中,一个进程至少包含一个线程,一个进程也可以包含多个…

Linux分文件编程:静态库与动态库的生成和使用

目录 一&#xff0c;Linux库引入之分文件编程 ① 简单说明 ② 分文件编程优点 ③ 操作逻辑 ④ 代码实现说明 二&#xff0c;Linux库的基本说明 三&#xff0c;Linux库之静态库的生成与使用 ① 静态库命名规则 ② 静态库制作步骤 ③ 静态库的使用 四&#xff0c;Linu…

Java二叉树的前中后序遍历

Java二叉树的前中后序遍历1.前序遍历1.1前序遍历概念1.2前序遍历习题2.中序遍历2.1中序遍历概念2.2中序遍历习题3.后续遍历3.1后序遍历概念3.2后序遍历习题大家好&#xff0c;我是晓星航。今天为大家带来的是 Java二叉树的前中后序遍历 的讲解&#xff01;&#x1f600; 1.前序…

Hive 数据倾斜

数据倾斜&#xff0c;即单个节点任务所处理的数据量远大于同类型任务所处理的数据量&#xff0c;导致该节点成为整个作业的瓶颈&#xff0c;这是分布式系统不可能避免的问题。从本质来说&#xff0c;导致数据倾斜有两种原因&#xff0c;一是任务读取大文件&#xff0c;二是任务…

超详细的堆排序,进来看看吧。

1.堆的基本概念1.1什么是堆堆是一种叫做完全二叉树的数据结构&#xff0c;1.2大堆和小堆大堆:每个节点的值都大于或者等于他的左右孩子节点的值小根堆:每个结点的值都小于或等于其左孩子和右孩子结点的值1.3完全二叉树节点之间的关系leftchild parent*2 1rightchild parent*…

ESP32设备驱动-LM35温度传感器驱动

LM35温度传感器驱动 文章目录 LM35温度传感器驱动1、LM35介绍2、硬件准备3、软件准备4、驱动实现1、LM35介绍 LM35 系列是精密集成电路温度传感器,其输出电压与摄氏(摄氏度)温度成线性比例。 因此,LM35 优于以开尔文校准的线性温度传感器,因为用户无需从其输出中减去较大…

深入理解WebSocket协议

“ 一直以来对WebSocket仅停留在使用阶段&#xff0c;也没有深入理解其背后的原理。当看到 x x x was not upgraded to websocket&#xff0c;我是彻底蒙了&#xff0c;等我镇定下来&#xff0c;打开百度输入这行报错信息&#xff0c;随即看到的就是大家说的跨域&#xff0c;或…

Ribbon负载均衡的原理(源码分析)

SpringCloud底层其实是利用了一个名为Ribbon的组件&#xff0c;来实现负载均衡功能的。1&#xff09;LoadBalancerIntercepor可以看到这里的intercept方法&#xff0c;拦截了用户的HttpRequest请求&#xff0c;然后做了几件事&#xff1a;1.request.getURI()&#xff1a;获取请…

网络编程1(网络背景知识)

A给B发送消息如何保证数据一定能够发送到B的主机上&#xff0c;而不是其他地方 通过IP地址可以实现网络中制定的两个主机之间的通信&#xff0c;除此之外还要确定是哪个进程来处理&#xff0c;这里就用到端口&#xff08;port&#xff09; 端口—在一台主机上用于唯一标识一个…

全面剖析OpenAI发布的GPT-4比其他GPT模型强在哪里

最强的文本生成模型GPT-4一、什么是GPT-4二、GPT-4的能力三、和其他GPT模型比较3.1、增加了图像模态的输入3.2、可操纵性更强3.3、复杂任务处理能力大幅提升3.4、幻觉、安全等局限性的改善3.6、风险和缓解措施改善更多安全特性3.7、可预测的扩展四、与之前 GPT 系列模型比较五、…

win10下使用docker运行部署nginx,mysql

一、docker的步骤&#xff1a;1.进入docker官网下载安装包2.打开控制面板 - 程序和功能 - 启用或关闭Windows功能&#xff0c;勾选Hyper-V&#xff0c;然后点击确定即可&#xff0c;如图&#xff1a;3.重新启动电脑4.启动Docker在桌面找到Docker for Windows快捷方式&#xff0…

java如何创建线程

java如何创建线程1. java如何创建线程1.1 通过继承Thread类来创建线程1.2 通过实现Runnable接口来创建线程1.3 通过匿名内部类来创建线程1.4 lambda表达式1.5 通过实现Runnable接口的方式创建线程目标类的优缺点1. java如何创建线程 一个线程在Java中使用一个Thread实例来描述…

JVM监控搭建

文章目录JVM监控搭建整体架构JolokiaTelegrafInfluxdbGrafanaJVM监控搭建 整体架构 JVM 的各种内存信息&#xff0c;会通过 JMX 接口进行暴露。 Jolokia 组件负责把 JMX 信息翻译成容易读取的 HTTP 请求。Telegraf 组件作为一个通用的监控 agent&#xff0c;和 JVM 进程部署在…

改进YOLO系列 | CVPR2023最新 PConv | 提供 YOLOv5 / YOLOv7 / YOLOv7-tiny 模型 YAML 文件

DWConv是Conv的一种流行变体,已被广泛用作许多神经网络的关键构建块。对于输入 I ∈ R c h w I \in R^{c \times h \times w} I∈

*p++,*(p++),*++p,(*p)++区别?

*p++:等同于:*p; p += 1; 解析:由于和++的运算优先级一样,且是右>结合。故p++相当于*(p++),p先与++结合,>然后p++整体再与结合。前面陈述是一种最 常见的错误,很多初学者也是这么理解的。 但是,因为++后置的时候,本身含义就是先 运算后增加1(运算指的是p++作为…

GPT-4来了!看看她究竟强在哪里!

GPT-4来了&#xff01;OpenAI老板Sam Altman直接开门见山地介绍说&#xff1a;这是我们迄今为止功能最强大的模型&#xff01;GPT-4是一个超大的多模态模型&#xff0c;也就是说&#xff0c;它的输入可以是文字&#xff08;上限2.5万字&#xff09;&#xff0c;还可以是图像。我…

大学四年..就混了毕业证的我,出社会深感无力..辞去工作,从头开始

时间如白驹过隙&#xff0c;一恍就到了2023年&#xff0c;今天最于我来说是一个值得纪念的日子&#xff0c;因为我收获了今年的第一个offer背景18年毕业&#xff0c;二本。大学四年&#xff0c;也就将就混了毕业证和学位证。毕业后&#xff0c;并未想过留在湖南&#xff0c;就回…

动手实现一遍Transformer

最近乘着ChatGpt的东风&#xff0c;关于NLP的研究又一次被推上了风口浪尖。在现阶段的NLP的里程碑中&#xff0c;无论如何无法绕过Transformer。《Attention is all you need》成了每个NLP入门者的必读论文。惭愧的是&#xff0c;我虽然使用过很多基于Transformer的模型&#x…

【2024考研】计算机考研,4轮复习时间安排

文章目录&#x1f3a8;第1轮复习&#xff08;暑假前&系统课&#xff09;英语1/2数学1/2专业课408&#x1f3a8;第2轮复习&#xff08;开学前&真题&#xff09;英语1/2试卷数学1/2试卷专业课408试卷&#x1f3a8;第3轮复习&#xff08;报名前&政治&#xff09;政治试…

基于stm32mp157 linux开发板ARM裸机开发教程Cortex-A7 开发环境搭建(连载中)

前言&#xff1a;目前针对ARM Cortex-A7裸机开发文档及视频进行了二次升级持续更新中&#xff0c;使其内容更加丰富&#xff0c;讲解更加细致&#xff0c;全文所使用的开发平台均为华清远见FS-MP1A开发板&#xff08;STM32MP157开发板&#xff09;针对对FS-MP1A开发板&#xff…
最新文章