C语言入门篇——编译篇

news2024/11/24 7:44:58

目录

1、程序环境

1.1 ANSI C 标准

1.2程序的翻译环境和执行环境

1.3运行环境

2、预处理详解

2.1、预定义符号

2.2、#define

2.2.1#define定义表示符

2.2.2#define定义宏

2.2.3#define替换规则

2.4#和##

2.2.5带副作用的宏参数

2.2.6宏和函数对比

3、#undef

4、命令行定义

5、条件编译

6、文件包含


1、程序环境

1.1 ANSI C 标准

C语言发展之初,并没有所谓的C标准。1978年,布莱恩·柯林汉和丹尼斯·里奇合著的The C Programming Language(《C语言程序设计》)第一版是公认的C标准,通常称之为K&R C或经典C。随着C的不断发展,越来越广泛地应用于更多系统中,C社区意识到需要一个更全面、更新颖、更严格的标准。美国国家标准协会(ANSI)于1983年组建了一个委员会,开发了一套新标准,并于1989年正式公布。该标准(ANSI C)定义了C语言和C标准库。国际标准化组织于990年采用了这套C标准(ISO C)。ISO C和ANSI C是完全相同的标准。ANSI/ISO标准的最终版本通常叫做C89(C90)。另外,由于ANSI先公布C标准,因此业界认识通常使用ANSI C。1994年,ANSI/ISO联合委员会开始修改C标准,最终发布了C99标准。该委员会遵循了最初C90标准的原则,包括保持语言的精炼简单。委员会的用意不是在C语言中添加新特性,而是为了达到新的目标。

ANSI C是由美国国家标准协会(ANSI)及国际化标准组织(ISO)推出的关于C语言的标准。
ANSI C 主要标准化了现存的实现, 同时增加了一些来自 C++ 的内容 (主要是函数原型)
并支持多国字符集 (包括备受争议的三字符序列)。

1.2程序的翻译环境和执行环境

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

翻译环境:在该环境中,源代码被转换为可执行的机器指令

执行环境:用于实际执行代码

add.c
#include "add.h"


int add(int a, int b)
{
	return a + b;
}

test.c
#includde "add.h"
int main(void)
{
    int ret = add(3,4);
    printf("a + b = %d\n",ret);

    return 0;
}

组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)

每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。

连接器同时也会引入标准C库函数中任何被该程序所用到的函数,且可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。

举个例子:test.c、add.c、minu.c

1.3运行环境

程序执行过程:

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

2、预处理详解

2.1、预定义符号

__FILE__      //进行编译的源文件,表示当前源代码文件名的字符串字面量

__LINE__     //文件当前的行号,表示当前源代码文件中的行号的整型常量

__DATE__    //文件被编译的日期,预处理的日期

__TIME__    //文件被编译的时间,翻译代码的时间

__FUNCTION__  //文件被编译的函数名

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

__STDC_HOSTED__   //本机环境设置为1;否则设置为0

__STDC_VERSION__  //支持C99标准,设置为199901L:支持C11标准,设置201112L

void print(void) 
{
	printf("文件被编译的函数名:%s\n", __FUNCTION__);
}

int main(void)
{
	printf("进行编译的源文件:%s\n", __FILE__);
	printf("文件当前的行号:%d\n", __LINE__);
	printf("文件被编译的日期:%s\n", __DATE__);
	printf("文件被编译的时间:%s\n", __TIME__);
	printf("文件被编译的函数名:%s\n", __FUNCTION__);
	//printf("如果编译器遵循ANSI C,其值为1,否则未定义:%d\n", __STDC__);

	printf("---------------------\n");
	print();

	return 0;
}

 我使用的的编译器为vs2022版本,对于__STDC__这个符号没有定义,这些预定义符号都是语言内置的。

2.2、#define

(#define定义的标识符和宏和枚举一样,习惯用大写)(程序员的约定俗成)

2.2.1#define定义表示符

语法:#define name stuff

name:替换的名字

stuff:被替换之后的内容

根据上面的语法我们就可以写出下面的例子:

#define MAX 1000

#define DEBUG_PRINT printf("file:%s\tline:%d\t \

                         date:%s\ttime:%s\n" ,\

                        __FILE__,__LINE__ ,\        

                        __DATE__,__TIME__ )

stuff是可以分开几行写,只是需添加反斜杠(续航符)

这里有一个很特殊的问题了,之前语句篇(C语言入门篇——语句篇_sakura0908的博客-CSDN博客)中说语句都是用分号结尾,那这里的最后要不要添加分号呢?在一些场景中会容易导致问题(语法错误),建议不要加上分号

2.2.2#define定义宏

#define name(parament-list) stuff

name:替换的名字

parament-list:由逗号隔开的符号表(参数列表)

stuff:被替换之后的内容

介绍:#define 机制允许把参数替换到文本中,这种实现通常被称为宏(macro)或定义宏(define macro),parament-list 是一个由逗号隔开的符号表,他们可能出现在 stuff 中。

注意事项:

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

测试案例代码如下:

#define Multiply(X) X*X

int main(void)
{
	printf("X * X = %d\n", Multiply(3));

	return 0;
}

 

那么 Multiply(3+1) 的结果是什么?一些初学者可能认为是16,但当运行之后答案却不一样,这是什么原因呢?

怎么去理解这答案呢?要把宏定义中的参数列表作为一个整体完全替换。宏的参数是完成替换的,他不会提前完成计算,而是替换进去后再计算。替换是在预处理阶段时替换,表达式真正计算出结果是在运行时计算。如果想获得 3+1 相乘(也就是得到 4×4 = 16) 的结果,我们需要给他们添加括号:

#define Multiply(X) X*X
#define Multiply2(X) (X)*(X)

int main(void)
{
	printf("X * X = %d\n", Multiply(3 + 1));
	printf("X * X = %d\n", Multiply2(3 + 1));

	return 0;
}

 另外,整体再套一个括号!让代码更加严谨,防止产生不必要的错误。

#define Multiply3(X) ((X)*(X))

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

2.2.3#define替换规则

在程序中扩展 #define 定义符号或宏时,需要涉及的步骤如下:

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

如果包含,就重复上述处理过程。 

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

2.4#和##

2.4.1#的作用

这里所说的#并不是#define和#include中的#,这里所说的#的作用是:把一个宏参数变成对应的字符串

这个#到底有什么实际的作用呢?在介绍#的作用的之前,我先向大家说明一下:字符串是有自动连接的特点的

    char arr[] = "hello ""world!";
    //等价于char arr[] = "hello world!";
    printf("helll ""world!\n");
    //等价于printf("helll world!\n");

int main(void)
{
	int age = 22;
	printf("The value of age is %d\n", age);
	double pi = 3.1415;
	printf("The value of pi is %f\n", pi);
	int* p = &age;
	printf("The value of p is %p\n", p);
	return 0;
}

 printf要打印的内容大部分是一样的,那么,为了避免代码冗余,我们可不可以将其封装成一个函数或是宏呢?这时就需要用到这个#了

#define print(data,format) printf("The value of "#data" is "format"\n",data)
int main()
{
	int age = 22;
	print(age, "%d");
	double pi = 3.1415;
	print(pi, "%f");
	int* p = &age;
	print(p, "%p");
	return 0;
}

2.4.2##的作用

##可以把位于它两边的符号合成一个符号。 它允许宏定义从分离的文本片段创建标识符

例如,下面定义的宏可以将传入的两个符号合成一个符号,测试案例代码如下:

#define STRCAT(x,y) x##y
int main(void)
{
	int xy = 100;
	printf("%d\n", STRCAT(x, y));//打印什么?
	return 0;
}

2.2.5带副作用的宏参数

在介绍带副作用的宏参数之前,我们先看看带有副作用是什么意思

int a = 10;	
int b = a + 1;//无副作用
int c = a++;//有副作用

代码中,b和c都想得到a+1的值,但不改变a的值。b得到a+1的值后,a的值并没有发生改变,所以无副作用;但是c得到a+1的值后,a的值也变化了,也就是有副作用。简单来说,代码执行后,除了达到我们想要的结果之外,还导致了其他问题的发生,我们就说该条语句带有副作用。

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
例如,我们要比较a和b的大小,并将其较大值赋值给c,之后再将a和b同时加1。

#define MAX(x,y) ((x)>(y)?(x):(y))
int main(void)
{
	int a = 10;
	int b = 20;
	int c = MAX(a++, b++);
	printf("%d\n", c);
	return 0;
}

 

 这段代码看似没有问题,但是结果却是不正确的,因为该宏经过替换后,等价于以下代码:

int main(void)
{
	int a = 10;
	int b = 20;
	int c = ((a++)>(b++)?(a++):(b++));
	printf("%d\n", c);
	return 0;
}

经过替换后,我们一分析便可得出答案,c的最后的结果是21,并且代码执行后,a和b的值并不是同时加1,a的值变为了11,而b的值却变为了22。

 所以,当我们使用宏的时候,应该避免传入带有副作用的宏参数

2.2.6宏和函数对比

宏与函数的对比表
属性#define定义宏函数
代码长度每次使用时,宏代码都会被插入到程序中。除了非常 小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方;每 次使用这个函数时,都调用那个 地方的同一份代码
执行速度更快存在函数的调用和返回的额外开 销,所以相对慢一些
操作符优先级宏参数的求值是在所有周围表达式的上下文环境里, 除非加上括号,否则邻近操作符的优先级可能会产生 不可预料的后果,所以建议宏在书写的时候多些括 号函数参数只在函数调用的时候求 值一次,它的结果值传递给函 数。表达式的求值结果更容易预测
带有副作用的参数参数可能被替换到宏体中的多个位置,所以带有副作 用的参数求值可能会产生不可预料的结果函数参数只在传参的时候求值一 次,结果更容易控制
参数类型宏的参数与类型无关,只要对参数的操作是合法的, 它就可以使用于任何参数类型函数的参数是与类型有关的,如 果参数的类型不同,就需要不同 的函数,即使他们执行的任务是 不同的
调试宏是不方便调试的函数是可以逐语句调试的
递归宏是不能递归的函数是可以递归的

3、#undef

#undef NAME 用于移除一个宏定义。(也不用在后面加分号)

#undef NAME

//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除

测试案例代码如下:

#define TEST 1
int main(void)
{
    int a = TEST;
    printf("%d\n", TEST);

#undef TEST// 移除宏定义

    return 0;
}

4、命令行定义

许多C编译器提供了一种能力,允许你在命令行中定义符号,用于启动编译过程。例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性便起到了作用。(假定某个程序中声明了一个某长度的数组,但是一个机器的内存有限,我们需要一个很小的数组,但是另外一个机器的内存很大,我们需要一个较大的数组。)

#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;
}

可以看到,代码中没有明确定义数组的大小。在编译这种代码时,我们需要使用命令行对数组的大小进行定义。

例如,在Linux环境下,编译指令如下:

gcc -D programe.c ARRAY_SIZE = 10

经过该编译指令后,便可以打印出0到9的数字。

5、条件编译

条件编译,即满足条件就参与编译,不满足条件就不参与编译。

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

常见的条件编译指令有以下几种:
1.单分支的条件编译

#if 表达式
    //待定代码
#endif

如果#if后面的表达式为真,则“待定代码”的内容将参与编译,否则“待定代码”的内容不参与编译。

2.多分支的条件编译

#if 表达式
    //待定代码1
#elif 表达式
    //待定代码2
#elif 表达式
    //待定代码3
#else 表达式
    //待定代码4
#endif

多分支的条件编译类似于if-else语句,“待定代码1,2,3,4”之中只会有一段代码参与编译。

3.判断是否被定义

//第一种的正面
#if defined(表达式)
    //待定代码
#endif

//第一种的反面
#if !defined(表达式)
    //待定代码
#endif

如果“表达式”被#define定义过,则“第一种的正面”的“待定代码”将参与编译,否则不参与编译。“第一种的反面”的执行机制与“第一种的正面”恰好相反。

//第二种的正面
#ifdef 表达式
    //待定代码
#endif

//第二种的反面
#ifndef 表达式
    //待定代码
#endif

如果“表达式”被#define定义过,则“第二种的正面”的“待定代码”将参与编译,否则不参与编译。“第二种的反面”的执行机制与“第二种的正面”恰好相反。

4.嵌套指令

#include <stdio.h>
#define MIN 10
int main()
{
#if !defined(MAX)
#ifdef MIN
    printf("hello\n");
#else
    printf("world\n");
#endif
#endif
    return 0;
}

这里条件编译指令的嵌套类似于if-else语句的嵌套,详情可阅读此篇博客(C语言入门篇——语句篇_sakura0908的博客-CSDN博客),博友们可以类比理解。

注意:未满足条件编译指令的代码,在预处理阶段将被编译器自动删除,不参与后面的代码编译过程。
例如,以下代码:

#include <stdio.h>
int main()
{
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d\n", i);
#if 0
        printf("hello world!\n");
#endif
    }
    return 0;
}

因为#if后面的表达式为假,语句 #if 0 和 #endif 之间的代码将不参与编译,所以在预处理阶段过后,编译器编译的代码是:

//#include <stdio.h>
//预处理阶段头文件也被包含了
int main()
{
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d\n", i);
    }
    return 0;
}

所以,代码运行后只会打印0到9的数字。

6、文件包含

我们知道,#include指令可以使被包含的文件参与编译,在预处理阶段,就会进行文件的包含
例如:

#include <stdio.h>

在预处理阶段,编译器会先删除该指令,并用stdio.h文件中的内容进行替换。

但是,文件的包含有两种:

#include <stdio.h>
#include "stdio.h"

一种是用尖括号将要包含的文件括起来,另一种是用双引号将要包含的文件引起来。这两种方法,在某些情况下似乎都可行,那么这两种方法到底有什么区别呢?

< >:如果使用尖括号的方式对头文件进行包含,那么当代码运行到预处理阶段,将对头文件进行包含时,编译器会自动去自己的安装路径下查找库目录,若库目录中含有该头文件,则将其进行包含,若库目录下不存在该头文件,则提示编译错误。

" ":如果使用双引号的方式对头文件进行包含,那么当代码运行到预处理阶段,将对头文件进行包含时,编译器会首先去正在编译的源文件目录下进行查找,若没有找到目标头文件,则再去库目录下进行查找,若两处都没有找到目标头文件,则提示编译错误。

这样看来,当我们要包含的头文件是库函数的头文件的时候,我们使用尖括号或者双引号都可以,但是当我们要包含的头文件是自定义的头文件时,我们只能用双引号进行头文件的包含

但是如果我们明明知道自己要包含的头文件是库函数的头文件,那我们就没有必要使用双引号去包含,因为那样会降低代码的效率。所以说,为了提高代码执行效率:
< >:一般用于包含C语言提供的库函数的头文件。
" ":一般用于包含自定义的头文件。

关于头文件,还有一点值得注意的是,当我们使用#include来包含头文件时,如果我们重复包含同一个头文件,那么在预处理阶段就会重复包含该头文件的内容,会大大加长代码量,导致代码冗余。

避免该问题的发生,有以下两种方法(以add.h为例):
方法一:

#ifndef __ADD_H__
#define __ADD_H__

//头文件内容

#endif

当第一次包含该头文件时,会用#define定义符号__ADD_H__,当第二次重复包含该头文件时,因为__ADD_H__已经被定义过,就无法再次包含该头文件的内容了。

方法二:

#pragma once

//头文件内容

只需在头文件开头加上这句代码,那么该头文件就只会被包含一次。

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

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

相关文章

项目1:登录功能设计

需求 后端接口设计MySQL表常用功能模块 后端总和前端实现方案 home页面 需求 实现一个登录功能 实现的功能 注册(邮箱注册) 登录(邮箱密码) 重置密码 查看操作记录(登录, 注册, 重置密码, 登出. 都算操作) 登出 后端接口设计 1. 人机验证 只要下面出现 人机验证 的功能都需要使…

容器化:MySQL

1 缘起 开启容器化之路。 2 容器化MySQL 2.1 查看MySQL镜像 docker search mysql2.2 指定版本&#xff1a;5.7.30 通过官网查看&#xff1a;https://hub.docker.com/ docker pull mysql:5.7.302.3 路径挂载 容器路径挂载到宿主机。 新建宿主机路径 mkdir -p /home/xind…

ElasticSearch-索引和文档的创建修改删除

目录 一、创建索引 二、查看索引 三、索引是否存在 四、删除索引 五、创建文档 六、查看文档 七、更新文档 八、文档是否存在 九、删除文档 一、创建索引 # 创建一个默认的索引&#xff0c;默认是标准分词器的索引 PUT /es_db2# 创建一个默认为ik分词器的索引 PUT /e…

十万条数据,后端不分页咋办!(如何优化长列表渲染)

十万条数据&#xff0c;后端不分页咋办&#xff01;&#xff08;如何优化长列表渲染&#xff09; 长列表是什么&#xff1f; 我们通常把一组数量级很大的数据叫做长列表&#xff0c;比如渲染一组上千条的数据&#xff0c;我们以数组的形式拿到这些信息&#xff0c;然后遍历渲…

Rust快速安装

Rust依赖C编译&#xff0c;Rust官方推荐的安装方式是利用VisualSudio安装C环境&#xff0c;VisualStuidio用过的都懂&#xff0c;庞大无比、卡顿、下载还贼慢(我当时装了一上午好像)&#xff0c;因此我们通过其它方式配置C 安装C环境 下载MinGW 64 win32 seh Mingw官网&…

Java的并发集合框架

文章目录 一、并发集合框架1. 简介2. 接口Iterable2. 接口Collection3. 接口List4. 接口Set5. 接口Queue6. Deque 二、非阻塞队列1. 概念2. ConcurrentHashMap使用&#xff08;1&#xff09;简介&#xff08;2&#xff09;验证HashMap不是线程安全的&#xff08;3&#xff09;验…

ld文件中指定变量在flash中的地址定义

本文说的是在gcc环境中&#xff0c;Keil或IAR可能有自己的使用方法。 我们在定义变量时&#xff0c;有时候需要把变量定义放到flash中的固定位置或区域&#xff0c;此时需要修改工程中的链接文件&#xff08;link file&#xff0c;ld文件&#xff09;。 方法一 修改ld文件中的…

软件IIC通信以及源码解析(如何使用)

以对读取MPU6050为例&#xff0c;解析如何采用IIC通信源码。 IIC的的通信&#xff0c;通常三种用途读写。分为&#xff1a; 1&#xff1a; 2&#xff1a; 3&#xff1a; 注&#xff1a;其中最常用的就是1和3了。 对1进行讲解&#xff1a; 指定地址写&#xff0c;通常用作对状…

【网络】- TCP/IP四层(五层)协议 - 网际层(网络层) - IP地址

目录 一、概述 二、IP地址的定义 三、IP地址由网络和主机两部分标识组成 一、概述 上篇文章简单介绍了网际协议IP。网际协议 IP 大致分为三大作用模块&#xff0c; ①IP寻址、 ②路由&#xff08;最终节点为止的转发&#xff09; 、③IP分包与组包。 这篇文章主要详细介绍IP地址…

【HTTP协议详解】

目录 1.什么是http2.抓包工具2.1 抓包工具2.2 抓包原理 3.Http协议格式3.1Http请求报文3.2Http响应报文的格式 4.请求报文格式4.1 报文首行4.2 请求报文header 5. 响应报文格式6.构造Http请求7.Https协议7.1 对称密钥7.2 非对称密钥7.3 证书 1.什么是http HTTP全称为“超文本协…

《MySQL是怎么运行的》阅读分享

mysql运行的整体架构简介 Mysql是由两部分构成&#xff0c;一部分是服务器程序&#xff0c;一部分是客户端程序。 服务器程序又包括两部分&#xff1a; 第一部分server层包括连接器、查询缓存、分析器、优化器、执行器等。涵盖 MySQL 的大多数核心服务功能&#xff0c;以及所有…

LeetCode:738.单调递增的数字 714.买卖股票的最佳时机含手续费 968.监控二叉树

738.单调递增的数字 题目 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递增 。 贪心 class Solution {public int monotoneIncrea…

数据结构——单链表(C语言)

在这⼀条⼗分漫长的路上&#xff0c;我⾛过阳关⼤道&#xff0c;也⾛过独⽊⼩桥。路旁有深⼭⼤泽&#xff0c;也有平坡宜⼈&#xff1b;有杏花春⾬&#xff0c;也有塞北秋风&#xff1b;有⼭重⽔复&#xff0c;也有柳暗花明&#xff1b;有迷途知返&#xff0c;也有绝处逢⽣。—…

[论文分享] Function Representations for Binary Similarity

Function Representations for Binary Similarity [TDSC 2022] Luca Massarelli , Giuseppe Antonio Di Luna, Fabio Petroni, Leonardo Querzoni, and Roberto Baldoni 二进制相似度问题在于仅考虑两个函数的编译形式来判断它们是否相似。近年来&#xff0c;计算二进制相似度…

【2023 · CANN训练营第一季】进阶班 Atlas 200I DK 智能小车

1 智能小车三维结构设计 1.1 基本模块 坚固酷炫结构模块运动控制模块超声波传感器模块摄像头视觉模块其他传感器模块 1.2 结构设计基本原则 从零开始设计并搭建智能小车&#xff0c;在满足外观要求的基础上&#xff0c;要满足小车运转过程中的运动干涉率为O&#xff0c;并且…

玩转ChatGPT:Show Me插件尝鲜

一、写在前面 之前&#xff0c;不少人问我GPT能否画技术路线图&#xff0c;然后看到了这个插件&#xff1a;Show Me&#xff1a; 简单问GPT这个插件的使用方法&#xff1a; 二、尝鲜过程 &#xff08;1&#xff09;用TA提供的例子试一试&#xff1a; 咒语&#xff1a;请用图…

009、实例连接访问控制

实例连接访问控制 1、实例连接访问控制概述2、pg_hba.conf文件3、名单格式4、pg_hba.conf 示例:5、当有重复或者冲突的时候1、实例连接访问控制概述 • 实例访问控制就像是一道防火墙,用它来控制来自于不同主机、不同用户是否 允许访问指定的数据库、以及验证方式。 2、pg…

【Tcp通信服务器流程】

TCP通信流程 1、服务器端&#xff08;被动接收连接的角色&#xff09; &#xff08;1&#xff09;创建一个用于监听的套接字 - 监听&#xff1a;监听有客户端的连接 - 套接字&#xff1a;这个套接字其实就是一个文件描述符 &#xff08;2&#xff09;将这个监听文件描述符和…

TextCNN文本分类

本文主要介绍TextCNN文本分类&#xff0c;主要从TextCNN的原理的Pytorch实现来逐步讲解。主要思想来自论文《Convolutional Neural Networks for Sentence Classification&#xff08;EMNLP2014&#xff09; 论文连接&#xff1a;[1408.5882] Convolutional Neural Networks f…

设计模式系列/ 职责链模式

必读 本篇主要分析设计模式之 职责链模式。 什么人适合学习本章节呢。 从未接触过设计模式的同学有n年工作经验 && 对职责链模式不了解的同学 1. 职责链模式意识形态 设计模式充斥着我们开发过程中的方方面面&#xff0c;责任链模式也是如此。也许你用到了设计模式&…