当前位置: 首页 > news >正文

C语言 | 预处理知识详解 #预处理指令有哪些?他们如何使用?宏和函数有哪些区别?...#

在这里插入图片描述
在这里插入图片描述

文章目录

  • 前言
  • 预定义符号介绍
  • 预处理指令#define
    • #define替换规则
  • 预处理指令 #undef
  • 宏和函数的对比
    • 宏和函数的对比图
    • 命名约定
  • 命令行定义
  • 条件编译
  • 预处理指令 #include
    • 嵌套文件包含
  • 其他预处理指令
  • 写在最后

前言


上篇文章介绍了一个程序运行的 编译与链接 ,其中编译阶段有个预处理,他会对一些预处理指令进行处理,本章就对这些预处理相关的指令,操作符等等进行探讨。


预定义符号介绍

这里介绍一些可能会常用到的符号:

__FILE__      //进行编译的源文件
__LINE__     //文件当前的行号
__DATE__    //文件被编译的日期
__TIME__    //文件被编译的时间
__STDC__    //如果编译器遵循ANSI C,其值为1,否则未定义

这些预定义符号都是语言内置的,都已经为其设定了特有的值,下面来看看个别的值是啥呢:

#include <stdio.h>int main()
{printf("%s\n", __FILE__);printf("%d\n", __LINE__);printf("%s\n", __DATE__);printf("%s\n", __TIME__);// 下面的_STDC_在vs上是未定义的,编译就会报错//printf("%d\n", __STDC__); return 0;
}

在这里插入图片描述
有了这些预定义符号,我们可以随时随地的知道此时的时间和文件所在位置啦。

预处理指令#define

在C或C++语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。

  • #define#include 一样,也是以“#”开头的。凡是以“#”开头的均为预处理指令,#define也不例外。

  • #define又称宏定义,标识符为所定义的宏名,简称宏。标识符的命名规则与前面讲的变量的命名规则是一样的。#define 的功能是将标识符定义为其后的常量。一经定义,程序中就可以直接用标识符来表示这个常量。是不是与定义变量类似?但是要区分开!变量名表示的是一个变量,但宏名表示的是一个常量。可以给变量赋值,但绝不能给常量赋值。

  • 宏定义 #define 一般都写在函数外面,与 #include 写在一起。当然,写在函数里面也没有语法错误,但通常不那么写。#define 的作用域为自 #define 那一行起到源程序结束。如果要终止其作用域可以使用 #undef 命令,下面会介绍。


还需详细了解 #define, 可以点此链接观摩大佬解析。


语法:

#define name stuff

举些个栗子:

#define MAX 100
#define FOREVER for(;;)
#define reg register
  • 可以看到宏的命名习惯都是大写,这样更能区别。

  • 标识符的定义与常量是以空格隔开的。

  • 第一个定义了一个标识符 MAX ,它是常量 100,当我们在用这个标识符时,在预处理阶段,MAX 将会被替换成100

  • 第二个是用更形象的符号来替换一种实现, for( ; ; ) 相当于死循环,这里用 FOREVER 来形象的表示它。

  • 第三个是为 register这个关键字,创建一个简短的名字。

那么我们在define 定义标识符的时候,要不要在最后加上 ;

比如:

#define MAX 100;

建议不要加上,因为当我们写C语言程序时,都会习惯在后面加上分号,如果是一个变量等于这个MAXint max = MAX;),这时预处理阶段,会把这个 MAX 替换,变成 int max = 100;; ,此时有两个分号,这就出现了语法问题。


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

下面是宏的申明方式:

#define name( parament-list ) stuff

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

注意:

  1. 参数列表的左括号必须与name紧邻。
  2. 如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

如:

#define SQUARE(x)  x * x

这个宏接收一个参数 x ,如果在上述声明后,有以下写法:

SQUARE(5);
SQUARE(2 + 3);

第一种预处理阶段被替换后表达式变为:5 * 5;(计算结果为25
第二种预处理阶段被替换后表达式变为:2 + 3 * 2 + 3;(计算结果为11

可以看出,第二种并不是我们想要的结果,所以这种定义宏的方式有问题

那如果这样子使用呢:

SQUARE((2 + 3));

替换后表达式变为:(2 + 3) * (2 + 3)这样子是可以的,但是这样治标不治本,而且写的代码还不好看,所以我们直接在定义宏处加上括号,这样就更好了。

更新之后:

#define SQUARE(x)  (x) * (x)

那么这样子是否还会有问题呢?实际上还是有的。

如果是一下定义的宏:

#define SQUARE(x)  (x) + (x)

有了上面的声明后,进行一下操作:

int a = 5 * SQUARE(5);

替换后表达式为:

int a = 5 * 5 + 5;

计算结果为:30,这也与我们想要的值不符。

所以我们在定义时要给整体也加上一个括号,这样才不会出错

正确规范定义:

#define SQUARE(x)  ((x) + (x))

所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间产生的不可预料的相互作用。


#define替换规则

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

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

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


预处理指令 #undef

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

在一个程序块中用完宏定义后,为防止后面标识符冲突需要取消其宏定义。

例如:

#define MAX 100
int a = 100;
#undef MAX

这里第三行就取消了MAX的红定义,在下面还可以继续定义以MAX为标识符的宏。


宏和函数的对比

宏通常被应用于执行简单的运算,比如在两个数中找出较大的一个:

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

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

原因有两点:

  1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多(也就是函数调用和函数返回的栈帧的创建和销毁可能比实际的代码功能运行时间还要长)。所以宏比函数在程序的规模和速度方面更胜一筹。
  2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏可以适用于整形、长整型、浮点型等可以用>来比较的类型。宏是类型无关的。

当然宏跟函数比较,也有其缺点:

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

根据宏的优点举个栗子(宏的参数可以出现类型,但是函数做不到):

#define MALLOC(num, type) (type *)malloc(num * sizeof(type))
...
//使用
MALLOC(10, int);  //类型作为参数//预处理器替换之后:
(int *)malloc(10 * sizeof(int));

宏和函数的对比图

在这里插入图片描述


命名约定

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

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

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

命令行定义

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

例如:

  • 当我们根据同一个源文件要编译出一个程序的不同版本的时候,这个特性有点用处(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一个机器内存大些,我们需要一个数组能够大些)。

如有下面的代码(在linux中):

#include <stdio.h>
int main()
{int array [ARRAY_SIZE];int i = 0;for(i = 0; i < ARRAY_SIZE; i++){array[i] = i;}for(i = 0; i < ARRAY_SIZE; i++){printf("%d " ,array[i]);}printf("\n" );return 0;
}

可以看到,上面的 ARRAY_SIZE是未定义的,但是我们可以通过以下指令对其赋值:

linux 环境演示
指令: gcc -D ARRAY_SIZE=10 programe.c

这样也可以灵活的控制数组大小啦。


条件编译

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

比如说:

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

#include <stdio.h>#define __DEBUG__int main()
{int i = 0;int arr[10] = {0};for(i=0; i<10; i++){arr[i] = i;#ifdef __DEBUG__printf("%d\n", arr[i]);    //为了观察数组是否赋值成功。 #endif     //__DEBUG__}return 0;
}

#ifdef 是如果定义了就干嘛干嘛,#endif 是截断 #ifdef的作用继续往下延伸。

可以看到,前面定义了_DEBUG_,所以后面的 printf("%d\n", arr[i]); 这条语句将会被编译执行。

  • 常见的条件编译指令:
1.
#if 常量表达式//...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__//..
#endif2.多个分支的条件编译
#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol4.嵌套指令
#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif

预处理指令 #include

#include 可以说是再熟悉不过了,我们已经知道, #include 指令可以使另外一个文件被编译。就像它实际出现于 #include 指令的地方一样。

这种替换的方式很简单:

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

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

  • 一种是本地文件包含:
#include "filename"

查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。如果找不到就提示编译错误。


Linux环境的标准头文件的路径:

/usr/include

VS环境的标准头文件的路径:

注意: 不同编译器可能放在不同地方,要按照自己的安装路径去找。

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include

  • 一种是库文件包含
#include <filename.h>

查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。这样是不是可以说,对于库文件也可以使用 “ ” 的形式包含?答案是肯定的,可以。 但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。

嵌套文件包含

如果出现这样的场景:

在这里插入图片描述

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

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

注:

推荐 《高质量C/C++编程指南》 中附录的考试试卷(很重要)。

笔试题:

1. 头文件中的 ifndef/define/endif是干什么用的?
2. #include <filename.h> 和 #include "filename.h"有什么区别?

其他预处理指令

#error
#pragma
#line
...

这里就不一一做介绍,可以自己去了解。 #pragma pack() 在 结构体一章 介绍了,可以去看噢。

写在最后

C语言阶段的知识学到这里,差不多就结束了呢,一路过来还是学到了非常多的知识,这也让我更加认清了自己的路还长着呢,接下来我会继续更新 基本数据结构 阶段的相关知识。

感谢阅读本小白的博客,错误的地方请严厉指出噢!

相关文章:

C语言 | 预处理知识详解 #预处理指令有哪些?他们如何使用?宏和函数有哪些区别?...#

文章目录前言预定义符号介绍预处理指令#define#define替换规则预处理指令 #undef宏和函数的对比宏和函数的对比图命名约定命令行定义条件编译预处理指令 #include嵌套文件包含其他预处理指令写在最后前言 上篇文章介绍了一个程序运行的 编译与链接 &#xff0c;其中编译阶段有个…...

如何实现LFU缓存(最近最少频率使用)

目录 1.什么是LFU缓存&#xff1f; 2.LFU的使用场景有哪些&#xff1f; 3.LFU缓存的实现方式有哪些&#xff1f; 4.put/get 函数实现具体功能 1.什么是LFU缓存&#xff1f; LFU缓存是一个具有指定大小的缓存&#xff0c;随着添加元素的增加&#xff0c;达到容量的上限&…...

【C++之容器篇】精华:vector常见函数的接口的熟悉与使用

目录前言一、认识vector1. 介绍2. 成员类型二、默认成员函数&#xff08;Member functions&#xff09;1. 构造函数2. 拷贝构造函数vector (const vector& x);3. 析构函数4. 赋值运算符重载函数三、迭代器&#xff08;Iterators&#xff09;1. 普通对象的迭代器2. const对象…...

InstructGPT

文章目录Abstract 给定人类的命令&#xff0c;并且用人工标注想要的结果&#xff0c;构成数据集&#xff0c;使用监督学习来微调GPT-3。 然后&#xff0c;我们对模型输出进行排名&#xff0c;构成新的数据集&#xff0c;我们利用强化学习来进一步微调这个监督模型。 我们把产…...

RTOS之一环境搭建(基于TM4C123GXL)

硬件TM4C123GXLBOOSTXL-EDUMKII keil5micriumOSA软件安装&#xff1a;1 ARM-MDK(MDK538aMDK_Stellaris_ICDI_AddOn)MDK538a链接&#xff1a;https://www.keil.com/demo/eval/arm.htmICDI链接&#xff1a;https://documentation-service.arm.com/static/60509bd61da8f8344a2ca1b…...

151、【动态规划】AcWing ——2. 01背包问题:二维数组+一维数组(C++版本)

题目描述 原题链接&#xff1a;2. 01背包问题 解题思路 &#xff08;1&#xff09;二维dp数组 动态规划五步曲&#xff1a; &#xff08;1&#xff09;dp[i][j]的含义&#xff1a; 容量为j时&#xff0c;从物品1-物品i中取物品&#xff0c;可达到的最大价值 &#xff08;2…...

DS期末复习卷(二)

选择题 1&#xff0e;下面关于线性表的叙述错误的是&#xff08; D &#xff09;。 (A) 线性表采用顺序存储必须占用一片连续的存储空间 (B) 线性表采用链式存储不必占用一片连续的存储空间 © 线性表采用链式存储便于插入和删除操作的实现 (D) 线性表采用顺序存储便于插…...

大数据技术架构(组件)31——Spark:Optimize--->JVM On Compute

2.1.9.4、Optimize--->JVM On Compute首要的一个问题就是GC,那么先来了解下其原理&#xff1a;1、内存管理其实就是对象的管理&#xff0c;包括对象的分配和释放&#xff0c;如果显式的释放对象&#xff0c;只要把该对象赋值为null&#xff0c;即该对象变为不可达.GC将负责回…...

ETL基础概念及要求详解

ETL基础概念及要求详解概念ETL与ELT数据湖与数据仓库ETL应用场景ETL具体流程及操作要求抽取清洗转换加载ETL设计模式SQL脚本语言ETL工具设计ETL工具SQLETL接口设计要求明确接口属性约定接口形式确定接口抽取方法规范接口格式概念 ETL即Extract&#xff08;抽取&#xff09;Tra…...

刷题记录:牛客NC23054华华开始学信息学 线段树+分块

传送门:牛客 题目描述: 题目latex公式较多,此处省略 输入: 10 6 1 1 1 2 4 6 1 3 2 2 5 7 1 6 10 2 1 10 输出: 3 5 26这道题让我体验到的线段树相对于树状数组的常数巨大 我们倘若直接用单点修改的话&#xff0c;如果D过小比如1那么我们足足要加n次&#xff0c;时间复杂度爆…...

二叉搜索树(查找,插入,删除)

目录 1.概念 2.性质 3.二叉搜索树的操作 1.查找 2.插入 3.删除(难点) 1.概念 二叉搜索树又称二叉排序树.利用中序遍历它就是一个有顺序的一组数. 2.性质 1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 2.若它的右子树不为空,则右子树上所有节点的值都…...

C# PictureEdit 加载图片

方法一&#xff1a; 如果要加载的图片的长宽比不是太过失衡&#xff0c; 1.可以改变picturebox的SizeMode属性为 PictureBoxSizeMode.StretchImage&#xff0c; 2.或者Dev控件 PictureEdit的SizeMode属性为Zoom。&#xff08;zoom:缩放&#xff1b;clip剪短&#xff1b;stret…...

3种方法设置PDF“打开密码”,总有一种适合你

PDF文件是我们工作中经常用到的文件之一&#xff0c;对于重要的文件&#xff0c;设置“打开密码”是一种很好的保护方式。下面就来说说&#xff0c;设置PDF“打开密码”有哪三种方法&#xff1f; 方法一&#xff1a;在线网站加密 市面上有很多可以直接在线上加密PDF文件的产品…...

第三章 数据链路层(点到点的传输服务)-计算机网络(笔记)

计算机网络 第三章 数据链路层&#xff08;点到点的传输服务&#xff09; 数据链路层属于计算机网络的低层。数据链路层使用的信道主要有以下两种类型&#xff1a; &#xff08;1&#xff09;点到点信道。这种信道使用一对一的点到点通信方式。 &#xff08;2&#xff09;广…...

volatile关键字与CAS机制

volatile关键字 volatile关键字可以对类的成员变量与静态变量进行修饰 volatile关键字的作用 1.保证被修饰属性的可见性,被修饰后的属性如果被更改后其他线程是会立即可见的 2.保证被修饰属性的有序性,被修饰后的属性禁止修改指令执行的顺序 注意:volatile关键字不能保证属性…...

LeetCode题解 动态规划(四):416 分割等和子集;1049 最后一块石头的重量 II

背包问题 下图将背包问题做了分类 其中之重点&#xff0c;是01背包&#xff0c;即一堆物件选哪样不选哪样放入背包里。难度在于&#xff0c;以前的状态转移&#xff0c;多只用考虑一个变量&#xff0c;比如爬楼梯的阶层&#xff0c;路径点的选择&#xff0c;这也是能用滚动数组…...

【FFMPEG源码分析】从ffplay源码摸清ffmpeg框架(二)

demux模块 从前面一篇文章中可以得知&#xff0c;demux模块的使用方法大致如下: 分配AVFormatContext通过avformat_open_input(…)传入AVFormatContext指针和文件路径&#xff0c;启动demux通过av_read_frame(…) 从AVFormatContext中读取demux后的audio/video/subtitle数据包…...

PCIE 学习笔记(入门简介)

PCIE 学习笔记书到用时方恨少啊&#xff0c;一年前学PCIE的笔记&#xff0c;再拿出来瞅瞅。发到博客上&#xff0c;方便看。PCIE基础PCIE和PCI的不同PCIE采用差分信号传输&#xff0c;并且是dual-simplex传输——每条lane上有TX通道和RX通道&#xff0c;所以每条lane上的信号是…...

锁的优化机制了解嘛?请进!

点个关注&#xff0c;必回关 文章目录自旋锁&#xff1a;自适应锁&#xff1a;锁消除&#xff1a;锁粗化&#xff1a;偏向锁&#xff1a;轻量级锁&#xff1a;从JDK1.6版本之后&#xff0c;synchronized本身也在不断优化锁的机制&#xff0c;有些情况下他并不会是一个很重量级的…...

5.点赞功能 Redis

Redis&#xff08;1&#xff09;简介Redis 是一个高性能的 key-value 数据库原子 – Redis的所有操作都是原子性的。多个操作也支持事务&#xff0c;即原子性&#xff0c;通过MULTI和EXEC指令包起来。非关系形数据库数据全部存在内存中&#xff0c;性能高。&#xff08;2&#…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)

cd /home 进入home盘 安装虚拟环境&#xff1a; 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境&#xff1a; virtualenv myenv 3、激活虚拟环境&#xff08;激活环境可以在当前环境下安装包&#xff09; source myenv/bin/activate 此时&#xff0c;终端…...

书籍“之“字形打印矩阵(8)0609

题目 给定一个矩阵matrix&#xff0c;按照"之"字形的方式打印这个矩阵&#xff0c;例如&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为&#xff1a;1&#xff0c;…...