【C语言】预处理编译链接调试技巧详解

主页:醋溜马桶圈-CSDN博客

专栏:C语言_醋溜马桶圈的博客-CSDN博客

gitee:mnxcc (mnxcc) - Gitee.com

目录

1.预处理

1.1 预定义符号

1.2 #define

1.2.1 #define 定义标识符

1.2.2 #define 定义宏

1.2.3 #define 替换规则

1.2.4 #和##

1.2.4.1 # 的作用

1.2.4.2 ## 的作用 

1.2.5 带有副作用的宏参数

1.2.6 宏和函数的对比 

对比

1.2.7 内联函数 

1.2.8 命名约定 

1.3 #undef

1.4 命令行定义 

1.5 条件编译

1.5.1 常见的条件编译指令

1.6 头文件包含

1.6.1 头文件被包含的方式

1.6.2 嵌套文件的包含

2.程序环境 编译和链接

2.1 翻译环境和执行环境

2.2 编译和链接

2.3 翻译

2.3.1 翻译的几个阶段

2.3.1.1 预编译

2.3.1.2 编译

词法分析

符号汇总

2.3.1.3 汇编

生成符号表

2.4 链接

2.4.1 合并段表

 2.4.2 合并符号表和重定位

2.5 运行

3.实用调试技巧 

3.1 什么是bug?

3.2 调试是什么?有多重要?

3.3 调试的基本步骤

3.4 debug和release的介绍

3.5 windows环境调试介绍

3.5.1 调试环境的准备

3.5.2 学会快捷键

3.5.3 调试的时候查看程序当前信息 

3.5.3.1 查看临时变量的值

3.5.3.2 查看内存信息

3.5.3.3 查看调用堆栈

3.5.3.4 查看汇编信息

3.5.3.5 查看寄存器信息

3.6 如何写出好(易于调试)的代码

3.6.1 常见的coding技巧

3.6.1.1 assert

3.6.1.2 const

常量指针

指针常量

区分常量指针和指针常量 

3.7 编程常见的错误

3.7.1 编译型错误

3.7.2 链接型错误

3.7.3 运行时错误

3.8 编程思维 


1.预处理

1.1 预定义符号

__FILE__          //进行编译的源文件

__LINE__         //文件当前的行号

__DATE__       //文件被编译的日期

__TIME__        //文件被编译的时间

__STDC__       //如果编译器遵循ANSI C,其值为1,否则未定义

这些预定义符号都是语言内置的

举个例子:

1.2 #define

#define是一种预处理指令,他有两种用法:

  1. #define 定义常量(标识符)
  2. #define 定义宏 

1.2.1 #define 定义标识符

语法:

        #define  name  stuff

举个例子:

#define 是完全替换,比如

所以在定义的时候,为了强调他是一个整体,需要自己带上括号:

注意:由于是完全替换,在define定义标识符的时候,不要在最后加   ;   否则替换的时候会将  也替换过去,会导致语法错误

1.2.2 #define 定义宏

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常会被解释为宏(macro)或定义宏(define macro)

下面是宏的声明方式:

#define name( parament-list ) stuff

其中的parament-list是一个由逗号隔开的符号表,他们可能出现在stuff中

注意:

参数列表的左括号必须与name紧邻

如果两者之间有任何空白存在,参数列表就会被释解释为stuff的一部分

如:

#define定义宏也是完全替换,比如:

为了防止出现失误,我们在声明的时候需要加上括号:

我们在写宏的时候,如果逻辑需要,我们可以加上足够多的括号来使宏变得完整

1.2.3 #define 替换规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,他们首先被替换
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程

注意:

  1. 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归
  2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索 

1.2.4 #和##

1.2.4.1 # 的作用

如何把参数插入到字符串中?、

我们发现字符串是有自动连接的特点的

假设有这样的代码:

我们如何用宏来实现printf的功能呢,这里我们使用#

他的替换是周怎么完成的呢 

这里只有当字符串作为宏参数的时候才可以把字符串放在字符串中

使用#,把一个宏参数变成对应的字符串 

比如:代码中的#N会被预处理器处理为:“N”

所以“#N”即被处理为““N””

1.2.4.2 ## 的作用 

##可以把位于他两边的符号合成一个符号

他允许宏定义从分离的文本片段创建标识符

注意:这样的连接必须产生一个合法的标识符,否则其结果就是未定义的

1.2.5 带有副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的有永久性效果

x+1;//不带副作用

x++;//带副作用

MAX宏可以证明具有副作用的参数所引起的问题 

这段代码输出的结果是什么?

这里我们得知道预处理之后的结果是什么: 

这段代码是证明执行的呢?

1.2.6 宏和函数的对比 

宏通常被应用于执行简单的运算

比如在两个数中找出较大的一个

#define MAX(a, b) ((a)>(b)?(a):(b))

那为什么不用函数来完成这个任务?

原因有二:

  1. 用于调用函数和从函数返回的代码可能实际执行这个小型计算工作所需要的时间更多
    所以宏比函数在程序的规模和速度方面更胜一筹
  2. 更为重要的是函数的参数必须声明为特定的类型
    所以函数只能在类型合适的表达式上使用
    宏是类型无关的

 宏的缺点:当然和函数相比,宏也有劣势的地方:

  1. 每次使用宏的时候,一份宏定义的代码将插入到程序中,除非宏比较短,否则可能大幅度增加程序的长度
  2. 宏是没法调试的
  3. 宏由于类型无关,也就不够严谨
  4. 宏可能会带来运算符优先级的问题,导致过程容易出现错误

 宏有时候可以做函数做不到的事情,比如:宏的参数可以出现类型,但是函数做不到

对比

建议:

如果逻辑比较简单,可以使用宏来实现

如果计算逻辑比较负责,那么就使用函数实现 

1.2.7 内联函数 

C99之后,C++引入了内联函数的概念  inline关键字

内联函数具有函数和宏的双重优点:

  1. 内联函数是函数
  2. 内联函数又像宏一样,在调用的地方展开

1.2.8 命名约定 

一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者

那我们平时的一个习惯是:

  • 把宏名全部大写               //MAX
  • 函数名不要全部大写        //Max

1.3 #undef

这条指令用于移除一个宏定义

1.4 命令行定义 

许多C的编译器提供了一种能力,允许在命令行中定义符号,用于启动编译过程

例如:当我们根据同一个源文件要编译出一个程序的不同版本的时候,这个特性有点用处

(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一 个机器内存大写,我们需要一个数组能够大写。)

1.5 条件编译

在编译一个程序序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的,因为我们有条件编译指令

条件编译就是:满足条件就编译,不满足条件就不编译

比如说:

调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译

1.5.1 常见的条件编译指令

1.
#if   常量表达式
        //...
#endif
//常量表达式由预处理器求值
如:
#define __DEBUG__ 1
#if __DEBUG__
        //..
#endif

表达式为真则编译,为假则不编译

2.多个分支的条件编译
#if 常量表达式
        //...
#elif 常量表达式
        //...
#else
        //...
#endif

只会选择以一个#if或者#elif执行 

3.判断是否被定义
#if defined(symbol)
#ifdef symbol

#if !defined(symbol)
#ifndef symbol

判断(symbol)是否被定义过,如果被定义过则执行代码

4.嵌套指令
#if defined(OS_UNIX)
        #ifdef OPTION1
                unix_version_option1();
        #endif
        #ifdef OPTION2
                unix_version_option2();
        #endif
#elif defined(OS_MSDOS)        
        #ifdef OPTION2
                msdos_version_option2();
        #endif
#endif

注意:#if 与 #endif 是配套使用的,同时出现,同时消失

1.6 头文件包含

我们已经知道,#include 指令可以使另外一个文件被编译,就像它实际出现于 #include 指令的地方一样

这种替换的方式很简单:

预处理器先删除这条指令,并用包含文件的内容替换
这样一个源文件被包含10次,那就实际被编译10次

1.6.1 头文件被包含的方式

头文件的包含一般有两种方式:

1.包含本地文件(自己的.h文件)
#include "xxx.h"(用双引号)

2.包含标准库中的文件
#include <xxx.h> (用尖括号)

查找策略:

先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件

#include包括""和<>这两种情况

""是在用户工作目录下寻找(用户的工作目录是通过编译器指定的)
<>是找系统标准库函数,通过系统环境变量指定系统库目录

如果找不到就提示编译错误

1.6.2 嵌套文件的包含

如果出现这样的场景

comm.h和comm.c是公共模块
test1.h和test1.c使用了公共模块
test2.h和test2.c使用了公共模块
test.h和test.c使用了test1模块和test2模块。
这样最终程序中就会出现两份comm.h的内容
这样就造成了文件内容的重复

我们可以用条件编译解决这个问题

每个头文件的开头写:

#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif  //__TEST_H__

或者 

#pragma once

就可以避免头文件的重复引入

2.程序环境 编译和链接

2.1 翻译环境和执行环境

在ANSI C的任何一种实现环境中,存在两个不同的环境

  • 第一种是翻译环境,在这个环境中源代码被转换为可执行的机器指令
  • 第二种是执行环境,它用于实际执行代码

2.2 编译和链接

2.3 翻译

  • 组成一个程序的每个源文件通过编译过程分别抓换成目标代码(object code)
  • 每个目标文件文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序
  • 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也 链接到程序中

2.3.1 翻译的几个阶段

2.3.1.1 预编译

预编译的过程:

  1. 注释的替换(删除) 注释被替换成一个空格
  2. 头文件的包含  #include < >
  3. #define 符号的替换

所有的预处理指令都是在预编译阶段处理的 (文本操作)

2.3.1.2 编译
词法分析

假如有下面一段代码

array[index] = (index+4)*(2+6) 

将源代码程序输入扫描器,扫描器的任务就是简单的进行词法分析,把代码中的字符分割成一系列的记号(关键字、标识符、字面量、特殊字符等)

上面程序进行词法分析后得到了16个记号:

生成一棵语法树

符号汇总

一个工程中可以包含多个.c文件,如何在一个.c文件中调用另一个.c文件中的函数呢

这里我们了解一个概念叫做符号汇总

假设有这样的代码

进行符号汇总

注意:符号汇总只能汇总全局变量

2.3.1.3 汇编

把汇编代码翻译成了二进制的指令,生成了.o文件(目标文件)

生成符号表

假设给汇总的符号给上地址,生成一个符号表

2.4 链接

2.4.1 合并段表

 2.4.2 合并符号表和重定位

2.5 运行

程序执行的过程:

  1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成
  2. 程序的执行便开始。接着便调用main函数
  3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程中一直保留他们的值
  4. 终止程序。正常终止main函数;也有可能是意外终止

3.实用调试技巧 

3.1 什么是bug?

作为程序员,每天被bug支配着,当然应该了解下对手了。

bug原意本来为昆虫的意思,1947年9月9日,葛丽丝·霍普(Grace Hopper)发现了第一个电脑上的bug。当在Mark II计算机上工作时,整个团队都搞不清楚为什么电脑不能正常运作了。经过大家的深度挖掘,发现原来是一只飞蛾意外飞入了一台电脑内部而引起的故障(如图所示)。这个团队把错误解除了,并在日记本中记录下了这一事件。也因此,人们逐渐开始用“bug”来称呼计算机中的隐错。

在这里插入图片描述

3.2 调试是什么?有多重要?

所有发生的事情都一定有迹可循,如果问心无愧,就不需要掩盖也就没有迹象了;如果问心有愧,就必然需要掩盖,那就一定会有迹象,迹象越多就越容易顺藤而上,这就是推理的途径

顺着这条途径顺流而下就是犯罪,逆流而上就是真相

一名优秀的程序员是一名出色的侦探

每一次尝试都是尝试破案的过程

调试(英语:Debugging / Debug),又称除错,是发现和减少计算机程序或电子仪器设备中程序

错误的一个过程。

3.3 调试的基本步骤

  • 发现程序错误的存在
  • 隔离、消除等方式对错误进行定位
  • 确定错误产生的原因
  • 提出纠正错误的解决办法
  • 对程序错误予以改正,重新测试

发现程序错误:程序员自己、测试人员、用户 

3.4 debug和release的介绍

  • Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序
  • Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用

测试人员站在用户的角度,测试的是发布版本 

代码:

#include <stdio.h>
int main()
{
 char *p = "hello world";
 printf("%s\n", p);
 return 0;
}

上述代码在Debug环境的结果展示

上述代码在Release环境的结果展示

Debug和Release反汇编展示对比

所以我们说调试就是在Debug版本的环境中,找代码中潜伏的问题的一个过程

那编译器进行了哪些优化呢? 请看如下代码:

#include <stdio.h>
int main()
{
    int i = 0;
    int arr[10] = {0};
    for(i=0; i<=12; i++)
   {
        arr[i] = 0;
        printf("hehe\n");
   }
    return 0;
}

如果是 debug 模式去编译,程序的结果是死循环。

如果是 release 模式去编译,程序没有死循环。

那他们之间有什么区别呢?

就是因为优化导致的

3.5 windows环境调试介绍

3.5.1 调试环境的准备

在环境中选择 debug 选项,才能使代码正常调试

3.5.2 学会快捷键

最常使用的几个快捷键:

F5

  • 启动调试,经常用来直接跳到下一个断点处

F9

  • 创建断点和取消断点
  • 断点的重要作用,可以在程序的任意位置设置断点
  • 这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去

F10

  • 逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句

F11

  • 逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部

CTRL + F5

  • 开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用 

在笔记本电脑上可以配合使用FN键 

3.5.3 调试的时候查看程序当前信息 

3.5.3.1 查看临时变量的值

在调试开始之后,用于观察变量的值

3.5.3.2 查看内存信息

在调试开始之后,用于观察内存信息

3.5.3.3 查看调用堆栈

通过调用堆栈,可以清晰的反映函数的调用关系以及当前调用所处的位置

3.5.3.4 查看汇编信息

3.5.3.5 查看寄存器信息

可以查看当前运行环境的寄存器的使用信息

多多动手,尝试调试,才能进步

  •  一定要熟练掌握调试技巧
  • 初学者可能80%的时间在写代码,20%的时间在调试。但是一个程序员可能20%的时间在写程序,但是80%的时间在调试。
  • 我们所讲的都是一些简单的调试。
  • 以后可能会出现很复杂调试场景:多线程程序的调试等
  • 多多使用快捷键,提升效率 

3.6 如何写出好(易于调试)的代码

  1. 代码运行正常
  2. bug很少
  3. 效率高
  4. 可读性高
  5. 可维护性高
  6. 注释清晰
  7. 文档齐全

3.6.1 常见的coding技巧

  1. 使用assert(断言)
  2. 尽量使用const
  3. 养成良好的编码风格
  4. 添加必要的注释
  5. 避免编码的陷阱
3.6.1.1 assert

assert函数是C语言标准库<assert.h>中的一个函数,函数原型为:

    void assert(int expression)

该函数输入参数只有一个int类型参数,返回值为void类型

assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行

用法总结与注意事项

  1. 在函数开始处检验传入参数的合法性
  2. 每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败
  3. 不能使用改变环境的语句,因为assert只在Debug中生效,如果这么做,会使用程序在真正运行时遇到问题
  4. assert和后面的语句应空一行,以形成逻辑和视觉上的一致感
  5. 有的地方,assert不能代替条件过滤
3.6.1.2 const
常量指针

常量指针是指针指向的内容是常量,可以有以下两种定义方式

const int* n;

int const* n;

常量指针说的是不能通过这个指针改变变量的值,但是可以通过其他的引用来改变变量的值

int a=5;
const int* n=&a;
a=6;

常量指针指向的值不能改变,但是这并不意味着指针本身不能改变,常量指针可以指向其他的地址

int a=5;
int b=6;
const int* n=&a;
n=&b;
指针常量

指针常量是指指针本身是个常量,不能再指向其他的地址,写法如下

int*const n;

需要注意的是,指针常量指向的地址不能改变,但是地址中保存的数值是可以改变的,可以通过其他指向改地址的指针来修改

int a=5;
int*p=&a;
int* const n=&a;
*p=8;
区分常量指针和指针常量 

区分常量指针和指针常量的关键就在于星号(*)的位置,我们以星号为分界线

  • 如果const在星号的左边,则为常量指针 
  • 如果const在星号的右边,则为指针常量

如果我们将星号读作"指针",将const读作‘常量'的话,内容正好符合。

  • int const * n;是常量指针
  • int * const n;是指针常量

3.7 编程常见的错误

3.7.1 编译型错误

直接看错误提示信息(双击),解决问题,或者凭借经验就可以搞定

3.7.2 链接型错误

看错误提示信息,主要再代码中找到错误信息中的标识符,然后定位问题所在

一般是标识符名不存在或者拼写错误

3.7.3 运行时错误

借助调试,逐步定位问题

3.8 编程思维 

作为初学编程的各位小伙伴们,肯定已经或多或少地听说过编程思维这个词了,那么到底什么是编程思维呢

编程语言

表面含义,编程就是以各式的编程语言来编译代码,类似于英语,也是一门语言。那么作为语言,英语有诸多的语法,那么编程语言同样有一定的语法。毕竟写出来的代码是需要让机器看懂的。

编程思维

那么思维是什么呢,简单说就是做一件事情,脑海里要能够搭建起一个简单的框架,然后再填填补补。构建这个框架的思维就是编程思维,这要求咱们在编程前必须阅读并理解需求,不能只停留在代码的层面,要全局思考,结果会使得代码简洁又高效。

举例

For example,这是一段利用C语言写出最简单的逆序输出,显然,很通俗易懂,但是当输入n个内容,这段代码显然不适用了。

#include <stdio.h>
int main() {
    int a, b, c, d, e, f, g, h, i, j;
    scanf("%d %d %d %d %d %d %d %d %d %d",&a,&b,&c,&d,&e,&f,&g,&h,&i,&j);
    printf("%d %d %d %d %d %d %d %d %d %d", j, i, h, g, f, e, d, c, b, a);
}

那么就可以尝试使用下面这段码

#include<stdio.h>
int main()
{
    int arr[10] = {0};
    for(int i = 9;i>=0;i--)
    {
        scanf("%d",&arr[i]);
    }
    for(int i = 0;i<10;i++)
    {
        printf("%d ",arr[i]);
    }
    return 0;
}

显然,这段代码就有了一定的框架结构,这就是编程思维的外在体现。

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

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

相关文章

23. UE5 RPG制作属性面板(一)

随着角色的属性越来越多&#xff0c;我们不能每次都进行showdebug abilitysystem进行查看&#xff0c;而且玩家也需要查看角色属性&#xff0c;所以需要一个查看玩家角色属性的面板。 在前面&#xff0c;我们创建三种类型的属性 Primary Attributes&#xff08;主要属性&#…

prettier + eslint 配置

vue-cli 新建项目选择 ESLint Prettier 会自动下载相关包 settings.json {"editor.formatOnSave": true, // 开启保存文件自动格式化代码"editor.defaultFormatter": "esbenp.prettier-vscode", // 默认的代码格式化工具// "prettier.r…

11 Games101 - 笔记 - 几何(曲线与曲面)

11 几何&#xff08;曲线与曲面&#xff09; 贝塞尔曲线 定义 贝塞尔曲线&#xff1a;由控制点和线段组成的曲线&#xff0c;控制点是可拖动的支点。 如图&#xff0c;蓝色为贝塞尔曲线&#xff0c;p1, p2, p3为控制点&#xff0c;曲线和初始与终止端点相切&#xff0c;并且…

数字乡村引领新风尚:科技赋能农村实现全面进步

随着信息技术的迅猛发展&#xff0c;数字乡村正成为引领农村全面进步的新风尚。科技作为推动农村发展的强大引擎&#xff0c;正在深刻改变着传统农业的生产方式、农村的社会结构以及农民的生活方式&#xff0c;为农村经济社会的全面进步注入了新的活力和动力。本文将从数字乡村…

【深度学习】四种天气分类 模版函数 从0到1手敲版本

引入该引入的库 import torch import torch.nn as nn import matplotlib.pyplot as plt import torch.nn.functional as F import torchvision import torch.optim as optim %matplotlib inline import os import shutil import glob os.environ["KMP_DUPLICATE_LIB_OK&q…

双指针算法:三数之和

文章目录 一、[题目链接&#xff1a;三数之和](https://leetcode.cn/problems/3sum/submissions/515727749/)二、思路讲解三、代码演示 先赞后看&#xff0c;养成习惯&#xff01;&#xff01;&#xff01;^ _ ^<3 ❤️ ❤️ ❤️ 码字不易&#xff0c;大家的支持就是我坚持…

马蹄集oj赛(双周赛第二十三次)

目录 数列分割 小码哥的地毯 小码哥的三色墙 palace 高数考试 新全排列 黑白双煞 等差 数三角形 区间修改 相对马高 小码哥剪绳子 数列分割 难度:黄金 时间限制:1秒巴 占用内存:64 M 小码哥给你一个长度为n的数列&#xff0c;求将该数列分割成两个左右两个部分且两…

大模型学习笔记七:LLM应用

文章目录 一、维护生产级别的LLM应用,需要做的事二、符合需求的LLM App维护平台三、LangFuse1)替换OpenAI客户端(把跟OpenAI交互记录到LangFuse)1.1)几个基本概念2)通过LangChain的回调函数触发记录(上面用的原生OpenAI接口,下面是调用LangChain的接口)3)构建一个实际…

打开snipaste软件的界面后,上次的截图无法销毁?

现象&#xff1a; 鼠标放上去&#xff0c;如图会有1个圆圈&#xff0c;无法消除一直显示在电脑桌面上&#xff0c;无法使图片消失 解决办法&#xff1a; 你应该是点到了空格&#xff0c;开启了编辑模式&#xff0c;然后又选中了其中一个功能例如橡皮檫导致无法移动和销毁&…

Linux线程补充1

十、多线程中线程间的"独立" ​ 1.线程在代码段通过执行不同的函数&#xff0c;实现代码段的独立&#xff1b; ​ 2.新线程通过在共享区划分不同的管理属性和不同的栈空间&#xff0c;实现栈的独立&#xff0c;而主线程使用的是栈空间&#xff1b; ​ 3.线程通过获…

计算机二级大题

题目来源&#xff1a;计算机二级Python半个月抱佛脚大法&#xff08;内呈上真题版&#xff09; - 知乎 1.大题1 注意csv文件读取的处理 ls[] for line in f: ls.append(line.strip(\n).split(,)) 2. 大题2 第一问&#xff1a; #计算有效票张数 fopen("vote.txt",…

微服务鉴权的几种实现方案

1.Token 1.1 Token透传&#xff08;不推荐&#xff09; 刚开始接触微服务时网上给的方案大都数是通过透传Token做鉴权&#xff0c;但我认为这种方式不是很妥当。接着往下看&#xff1a; 这种方式通过透传Token使得各微服务都能获取到当前登录人信息&#xff0c;在代码编写上确…

SCI一区 | Matlab实现WOA-TCN-BiGRU-Attention鲸鱼算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测

SCI一区 | Matlab实现WOA-TCN-BiGRU-Attention鲸鱼算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测 目录 SCI一区 | Matlab实现WOA-TCN-BiGRU-Attention鲸鱼算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测预测效果基本介绍模型描述程序…

面试算法-78-两两交换链表中的节点

题目 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4] 输出&#xff…

NLP 笔记:LDA(训练篇)

1 前言&#xff1a;吉布斯采样 吉布斯采样的基本思想是&#xff0c;通过迭代的方式&#xff0c;逐个维度地更新所有变量的状态 1.1 举例 收拾东西 假设我们现在有一个很乱的屋子&#xff0c;我们不知道东西应该放在哪里&#xff08;绝对位置&#xff09;&#xff0c;但知道哪…

汉字之美,拼音之韵

title: 汉字之美&#xff0c;拼音之韵 date: 2024/3/23 18:41:56 updated: 2024/3/23 18:41:56 tags: 汉字拼音文化语言美学传承中文 1. 汉字之美 汉字作为中文的书写形式&#xff0c;承载着丰富的文化内涵。每一个汉字都蕴含着历史、传统和智慧&#xff0c;是中华文明的瑰宝…

关于Java发邮件提醒写周报实现(一)环境搭建

背景 由于公司每周都要写周报&#xff0c;而日常工作很忙&#xff0c;所以很容易忘记这件事件&#xff0c;因此开发一个写周报提醒的机器人&#xff0c;进行特定时间提醒是时候写周报了。 有一个大前提&#xff0c;本技术实现&#xff0c;本着不开通任何收费服务的态度去考察使…

JetBrains CLion 2022 for Windows:C++开发者的强大助手,引领编程新风尚

在数字化浪潮席卷全球的今天&#xff0c;编程语言的多样性和复杂性日益凸显。而在众多编程语言中&#xff0c;C以其独特的优势和广泛的应用领域&#xff0c;成为众多开发者的首选。JetBrains CLion 2022&#xff0c;作为一款专为C开发者打造的集成开发环境&#xff08;IDE&…

深度学习(二)安装tensorflow深度学习框架

0.前言 速度更新新的一期&#xff0c;快夸奖我。前情提要这是我在window10系统下完成的操作&#xff0c;并不是ubuntu&#xff0c;所以有相应的区别。 1.安装tensorflow和d2l 这里默认大家已经安装好了anconda或者miniconda并且以及创建了虚拟环境。 conda create -n huahuaji…

Cesium安装部署运行

目录 1.简介 2.Cesium项目下载 3.Cesium项目运行 4.cesium运行 1.简介 Cesium是国外一个基于JavaScript编写的使用WebGL的地图引擎。Cesium支持3D,2D,2.5D形式的地图展示&#xff0c;可以自行绘制图形&#xff0c;高亮区域&#xff0c;并提供良好的触摸支持&#xff0c;且支…