C语言预处理:开启编程新境界

news2024/11/22 20:00:31

pFp8UCq.jpg

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C语言学习
贝蒂的主页:Betty‘s blog

1. 预处理符号

在C语言中,我们可以通过一些预定义符号查看文件的相关信息。

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

我们首先通过下面这段代码在VS2022环境实验一下:

void Test()
{
    printf("源文件为:%s\n", __FILE__);		//进行编译的源文件
    printf("行号为:%d\n", __LINE__);		//文件当前的行号
    printf("日期为:%s\n", __DATE__);		//被编译的日期
    printf("时间为:%s\n", __TIME__);		//被编译的时间
    printf("%d\n", __STDC__);		        //VS2022没有遵循ANSIC 
}

img

而在Linuxgcc环境下是支持ANSIC的。

img

2. #define的使用

2.1#define定义标识符

有些常量数据可能需要更改,并且在程序中被大量使用,为了方便起见,我们可以使用 #define 来定义常量,其语法如下:

#define name stuff

下面是一些实例:

#define PI 3.14//将Pi定义成常量3.14
#define reg register//为 register这个关键字,创建⼀个简短的名字
#define do_forever for(;;)//⽤更形象的符号来替换死循环
// 如果定义的 stuff过⻓,可以分成⼏⾏写,除了最后⼀⾏外,每⾏的后⾯都加⼀个反斜杠(续⾏符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
                          date:%s\ttime:%s\n" ,\
                          __FILE__,__LINE__ ,  \
                          __DATE__,__TIME__ )

例如如果我们在程序中使用 PI 时,就相当于使用了 3.14 这个值。这样,当需要修改常量值时,只需在 #define 处修改即可,无需在程序中逐个查找替换,大大提升我们的效率。

img

其中需要特别注意的是#define定义标识符时末尾不需要加;。因为我们知道#define的标识符在预处理阶段就换完成替换,根本不需要;

2.2#define定义宏

#define机制包含了一个规定,允许将参数替换到文本中,这种实现通常被称为宏(macro)或定义宏(define macro),这一点和函数极为相似。

其语法如下:

#define name( parament-list ) stuff

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

  • 注意:参数列表的左括号必须与name紧邻,如果两者之间有任何空⽩存在,参数列表就会被解释为stuff的⼀部分。

下面是宏使用的具体实例:

#define ADD(x) x + x
int main()
{
	int ret = ADD(2);
	printf("%d\n", ret);
	return 0;
}

img

2.3 宏的副作用

(1) 宏替换

事实上定义宏有一个特别致命的问题,如下面这段代码:

#define SQUARE( x ) x * x
int main()
{
    int a = 5;
    printf("%d\n", SQUARE(a + 1));
    return 0;
}//输出什么?

输出结果:11

我们预计会输出36,但是为什么变成11了呢?要知道这一点,我们还是得强调宏只是在预处理阶段完成替换

在预处理时替换文本时,参数x被替换成a+1,所以这条语句实际上变成了:

printf ("%d\n",a + 1 * a + 1 ); 

这时我们就明白为什么出现错误的原因了,程序根本就没有按照我们预计的顺序走,解决这个问题也十分简单在宏定义上加上两个括号,这个问题便轻松的解决了。

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

但是这里还有个宏,按照以上方法处理,问题仍然未被解决:

#define DOUBLE(x) (x) + (x) 
int main()
{
    int a = 5;
    printf("%d\n", 10 * DOUBLE(a));
    return 0;
}

输出结果:55

加上括号后按照我们的预计应该输出100,但是事实上结果输出55,这明显又出现问题了。

替换⽂本时,参数x被替换成a,所以这条语句实际上变成了:

printf ("%d\n",10 * (5) + (5)); 

因为乘法优先级明显比加法高,所以并没有得到我们想要的结果,为了避免这种情况我们最后在最外层再加一层括号。

#define DOUBLE( x) ( ( x ) + ( x ) ) 
  • 注意:所有用于对数值表达式进行求值的宏定义都应当以这种方式加上括号,以防在使用宏时,由于参数中的操作符或与其邻近操作符之间出现不可预料的相互作用
(2) 宏参数

宏参数在宏的定义中出现不止一次的情况下,如果参数存在副作用,那么在使用该宏时就可能存在风险,致使出现不可预测的后果。所谓副作用,指的是表达式求值过程中产生的永久性效果

x+1; //不带副作⽤
x++; //带有副作⽤

我们可以以下面这个例子来说明这个问题:

#include<stdio.h>
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main()
{
     int a = 3;
     int b = 4;
     int max = 0;
     max = MAX(a++, b++);
     printf("max = %d, a = %d, b = %d\n", max, a, b);
     return 0;
}

这段程序到底输出什么呢?是【5 4 5】【5 5 4】【6 5 4】【6 4 6】中到底是哪个呢?

我们知道预处理之后程序会变成:

z = ( (a++) > (b++) ? (a++) : (b++)); 

而我们前面也学过前置++与后置++的区别

  1. 首先a,b先使用a,b比较,a<b。a,b再自增变为4,5。
  2. 在执行b++,先赋值,max就等于5,然自增成6。所以输出结果是:

img

从上面示例我们可以看出,像自增++,自减–传进宏定义中可能会发生我们意料之外的结果,所以我们平时在使用宏的时候一定要小心谨慎。

2.4 #和##

(1) #操作符

首先我们得知道在打印字符串时候,字符串本身是具有拼接能力的,即不同的字符串最终会被合并成一个字符串。

比如下面这段代码:

int main()
{
	printf("hello betty!");
	printf("hello" "betty!");//两个字符串最终会合并成一个字符串
	return 0;
}

img

#运算符能够将宏的一个参数转换为字符串字面量。它只允许在带参数的宏的替换列表中出现。#运算符所执行的操作可以理解为“字符串化”。

举个例子:当我们有一个变量 int a = 10 的时候,若想通过传参的方式打印出:the value of a is 10 ,就可以这样写。

#define PRINT(n) printf("the value of "#n " is %d", n); 

而如果没有#n就会被解析成整数而发生报错。

当我们按照下⾯的⽅式调⽤的时候:

PRINT(a)

代码就会将#n处理为字符串"a",然后与原字符串发生拼接。

printf("the value of ""a" " is %d", a);

通过这一特性我们可以通过宏实现一些函数无法实现的场景,比如:

//根据不同参数打印不同的字符串
#define PRINT(value, format) printf("the value of "#value" is " format "\n", value);
int main()
{
	int a = 1;
	char b = 'a';
	double c = 3.14;
	PRINT(a,"%d");
	PRINT(b, "%c");
	PRINT(c, "%lf");
	return 0;
}

img

(2) ##操作符

在C语言中,我们可以通过##实现记忆粘合,记号粘合是一种特殊的操作机制,它能够把位于其两侧的符号组合成一个新的符号。比如说,在宏定义中通过“记号粘合”,可以将原本分离的文本片段连接起来创建一个新的标识符。

以下是一个在 C 语言中关于“记号粘合”的简单示例:

#define CONCAT(X, Y) X##Y//记忆粘合

int main() 
{
    int num1 = 10, num2 = 20;
    int combined = CONCAT(num, 1);  // 这里相当于 num1
    printf("%d\n", combined);
    return 0;
}

img

在上述示例中,CONCAT(num, 1) 中的 ## 执行了记号粘合操作,将 num1 粘合在一起,形成了 num1 这个标识符。

但是需要注意的是记忆粘合后的标识符必须满足C语言对于标识符的定义,否则结果就是未定义的。在 C 语言中,标识符的规定如下:

  1. 由字母(A - Z,a - z)、数字(0 - 9)和下划线(_)组成。
  2. 首字符须为字母或下划线,不可为数字。
  3. 区分大小写,如 myVarMyVar 不同。
  4. 长度无严格限制,不同编译器可能有限制,通常前 31 个字符有意义。
  5. 不可用 C 语言关键字(如 intifwhile 等)作标识符。

合法示例: my_variable_underscore_startnum123

不合法示例: 123num(以数字开头)、if(是关键字) 。

并且利用记号粘合还可以实现C语言实现比较繁琐的情况。在此,我们思考这样一个情形:当编写一个用于获取两个数中较大值的函数时,由于数据类型的不同,我们不得不为每种数据类型分别编写不同的函数。例如:

int int_max(int x, int y) 
{ 
    return x>y?x:y; 
}
float float_max(float x, float y) 
{
    return x>y?x:y;
}

然而,如此编写方式显得极为繁琐。而现在,我们尝试采用如下方式进行代码编写:

//宏定义
#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \
    return (x>y?x:y); \
}

然后我们可以通过调用宏来生成函数,有效的简化了过程。

GENERIC_MAX(int) //替换到宏体内后int##_max ⽣成了新的符号 int_max做函数名
GENERIC_MAX(float) //替换到宏体内后float##_max ⽣成了新的符号 float_max做函数名
int main() 
{
    //调用函数
    int m = int_max(2, 3);
    printf("%d\n", m);
    float fm = float_max(3.5f, 4.5f);
    printf("%f\n", fm);
    return 0;
}

img

2.5 #undef

#undef这条指令⽤于移除⼀个宏定义,宏定义在被移除之后就不能再被使用。

#undef NAME
//如果现存的⼀个名字需要被重新定义,那么它的旧名字⾸先要被移除
#include<stdio.h>
//宏定义
#define PI 3.14
int main()
{
	printf("取消之前:%lf", PI);
#undef PI
	printf("取消之后:%lf", PI);
	return 0;
}

img

3. 宏的替换规则

最后让我们总结一下在程序中扩展#define定义的符号和宏,需历经以下步骤:

  1. 调用宏时,先检查参数是否有#define定义的符号,若有则先替换。
  2. 接着,将替换文本插入原位置,对于宏,用参数值替换参数名。
  3. 最后,再次扫描结果文件,若含#define定义的符号,重复上述处理。

并且需要注意的是:

  • 在宏参数和 #define 定义中,能够出现其他 #define 定义的符号。不过,宏不允许出现递归。
  • 当预处理器对 #define 定义的符号进行搜索时,不会搜索字符串常量的内容。

4. 宏与函数的对比

宏通常被用于执行简单的运算。然而,我们发现同样的问题其实也能够通过函数来解决,那么为何有时我们会选择宏而非函数呢?原因有如下三点:

  • 宏通常用于执行简单的运算。用于调用函数和从函数返回的代码所需时间,可能比实际执行小型计算工作所需的时间更多。因此,宏在程序的规模和速度上优于函数。
  • 函数的参数必须声明为特定的类型,只能在类型合适的表达式上使用。而宏可以适用于整型、长整型、浮点型等,宏的参数是类型无关的
  • 宏有时候可以做到函数做不到的事情,比如宏的参数可以出现类型,而函数无法实现。如下面动态开辟内存:
#define MALLOC(num, type) (type*)malloc(num*sizeof(type))

宏虽然常被用于执行简单运算,但也存在一些显著的缺陷。

  • 每次使用宏时,宏定义的代码会插入到程序中,若宏的定义较长,可能会大幅增加程序的长度。
  • 宏无法进行调试,这给排查问题带来了困难。
  • 宏因与类型无关,显得不够严谨。
  • 宏还可能引发运算符优先级的问题,致使程序容易出错。

这些不足使得在使用宏时需要谨慎权衡其利弊。

以下是宏与函数的对比:

属性#define定义宏函数
代码长度每次使用时宏代码插入程序,除非很小否则程序长度大幅增长代码只出现一处,每次调用同一份,通过函数栈帧执行
执行速度更快存在调用和返回的额外开销
操作符优先级宏参数求值在周围表达式上下文,除非加括号否则可能有意外,建议多写括号函数参数调用时求值一次,结果更易预测
带有副作用的参数参数可能在宏体多处替换,求值结果难料参数传参时求值一次,结果更易控制
参数类型参数与类型无关,操作合法即可用于任何类型参数与类型有关,不同类型需不同函数
调试不方便方便
递归不能

5. 命令行定义

许多 C 编译器具备一种能力,允许在命令行中定义符号,以此来启动编译过程。 例如,当依据同一个源文件编译一个程序的不同版本时,这个特性颇有用处。假定在某个程序中声明了一个特定长度的数组,如果机器内存有限,就需要一个很小的数组;而在另一个内存较大的机器上,就需要一个更大的数组。

我们将在Linuxgcc编辑器下实验如下代码,然后输入以下指令:

gcc -D ARRAY_SIZE=10 test.c

这句指令的含义就是:使用gcc编译器对test.c文件进行编译,并在编译前定义宏ARRAY_SIZE的值为 10。

#include <stdio.h>
int main()
{
    int array[ARRAY_SIZE];//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;
}

img

6. 条件编译

条件编译是一种在编译阶段根据特定条件决定是否将一段代码包含进最终生成的可执行文件或目标代码的技术。简单来说,就是通过预定义的条件,例如指定的宏、操作系统类型、编译器选项等,来控制哪些代码被实际编译,哪些代码被忽略。

接下来我们将介绍四种常见的条件编译的形式:

  1. #if 常量表达式 指令: 这是最基本的条件编译指令。预处理器会对常量表达式进行求值,如果结果为非零(即真),则会编译 #if#endif 之间的代码;否则,会忽略这段代码。 示例:
#define DEBUG_LEVEL 1
#if DEBUG_LEVEL > 0
    printf("Debugging is enabled.\n");
#endif
  1. 多个分支的条件编译: 这种结构允许根据不同的条件来选择编译不同的代码段。#if 后面的表达式如果为真,就编译其对应的代码段;如果为假,就继续检查 #elif 后面的表达式,依此类推。如果所有的条件都不满足,就编译 #else 后面的代码段。 示例:
#define OS_TYPE 2
#if OS_TYPE == 1
    printf("This is OS Type 1.\n");
#elif OS_TYPE == 2
    printf("This is OS Type 2.\n");
#else
    printf("Unknown OS Type.\n");
#endif
  1. 判断是否被定义: #if defined(symbol)#ifdef symbol 用于检查指定的符号(通常是宏)是否已被定义,如果已定义则编译相应的代码。#if!defined(symbol)#ifndef symbol 则用于检查指定的符号是否未被定义,如果未定义则编译相应的代码。 示例:
#define FLAG
#ifdef FLAG
    printf("FLAG is defined.\n");
#endif
#ifndef OTHER_FLAG
    printf("OTHER_FLAG is not defined.\n");
#endif
  1. 嵌套指令: 条件编译指令可以嵌套使用,以实现更复杂的条件判断逻辑。 示例:
#define OS_UNIX
#define OPTION1
#if defined(OS_UNIX)
    #ifdef OPTION1
        printf("This is Unix with Option 1.\n");
    #endif
    #ifdef OPTION2
        printf("This is Unix with Option 2.\n");
    #endif
#elif defined(OS_MSDOS)
    #ifdef OPTION2
        printf("This is MS-DOS with Option 2.\n");
    #endif
#endif

条件编译和选择语句(如 if-else 语句)在控制程序的执行流程方面有一些相似之处,但也存在明显的区别:

相同点:

两者都是基于某种条件来决定程序的执行路径或代码的处理方式。

不同点:

  1. 执行时机:
  • 条件编译发生在编译阶段,由预处理器根据定义的条件决定是否将某段代码包含在最终的编译结果中。
  • 选择语句则是在程序运行时,根据条件的真假来决定执行哪部分代码。
  1. 作用范围:
  • 条件编译影响的是代码的编译,可能会导致某些代码段完全不参与编译。
  • 选择语句作用于程序运行时的执行流程,所有代码都会被编译,但根据条件决定是否执行。
  1. 灵活性:
  • 条件编译通常用于处理与编译环境、平台等相关的差异,灵活性相对较低。
  • 选择语句可以处理更复杂和动态的条件,在运行时根据各种变化的条件进行决策,灵活性更高。

总之,条件编译主要用于在编译时根据预定义的条件控制代码的包含与否,而选择语句用于在程序运行时根据动态的条件控制执行路径。根据具体的需求和场景,选择合适的方式来实现所需的逻辑控制。

7. 头文件的包含

在预处理阶段,当编译器遇到 #include 指令时,它会根据指定的方式(例如 <> 用于系统头文件, "" 用于用户自定义头文件)去查找对应的头文件。

找到头文件后,会将头文件的全部内容直接插入到 #include 指令所在的位置,就好像头文件的内容原本就在那里一样。

7.1 本地头文件

在编写程序的过程中,我们能够创建属于自己的头文件,并且可以在源文件中对其进行包含。通常,采用双引号的包含方式,形如:

#include "text.h"

当以双引号来包含头文件时,编译器首先会在源文件所在的目录下进行查找。倘若在该目录中未能找到这个头文件,编译器就会如同查找库函数头文件那样,在标准路径位置继续查找。要是最终还是找不到,就会提示编译错误。

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

/usr/include

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

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include
//这是VS2013的默认路径

7.2 库文件

除了能够创建并包含本地头文件之外,我们还能够包含库文件,例如:<stdio.h><string.h> 。这类库文件常常采用尖括号包含的方式。

在查找这类以尖括号包含的头文件时,编译器会直接前往标准路径进行查找,如果未能找到,就会提示编译错误。

那么,对于库文件,是否也可以使用双引号“ ”的形式来包含呢?

答案是肯定的,可以这样做。然而,如此操作会导致查找的效率降低,并且也不太容易区分所包含的文件究竟是库文件还是本地文件。

6.2 嵌套头文件

我们知道,在预处理阶段,#include指令会展开所包含头文件的内容。如果头文件被多次包含,那么展开的重复信息就会增多,这会给编译带来较大的压力。这种问题在直接编写程序时是可以避免的,但前提是自己独立编写代码。然而,如果是在公司开发的环境中,常常需要与同事共同编写代码,这时就有可能出现你和同事包含相同头文件的情况。 比如说以下情况:

同事一编写test4.c需要借助test1.htest2.h两个头文件,而同事二编写test5.c需要借助test2.htest3.h两个头文件。最后两个同事的文件需要合并成test6.c。结果就会造成test2.h的头文件被重复包含。

img

那如何解决头⽂件被重复引⼊的问题?答案很简单使用条件编译,即每个头⽂件都包含以下内容

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

比如说我们定义一个Add.h的头文件,里面包含一个加法函数的声明。

#ifndef __TEST_H__
#define __TEST_H__
int Add(int x, int y);
#endif //__TEST_H__

这样第一次使用这个头文件时,并没有定义_TEST_H_ifndef条件为真,执行#define_TEST_H_以及头文件包含的内容。然后第二次,第三次…重复包含这个头文件时_TEST_H_已定义,ifndef条件为假,后续内容就不再执行。

当然还有一个办法就是直接在头文件中加:

#pragma once
#pragma once
int Add(int x, int y);

当然预处理指令还不止这些,#error#line等预处理指令大家可以自主学习。

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

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

相关文章

vue中post请求返回二进制流文件下载

1 .调用接口返回的如下图所示,此时看到是一个流文件 2.不管是get请求还是post请求都需要加上 下面这行代码 responseType: "blob", 3.我们自行二次封装的axios可能会导致乱码现象,建议直接用axios调用接口请求 4.关于Excel导出 POI 响应头设置 Content-Type: applica…

Tomcat高可用集群(实例详解)

一.环境准备 虚拟机的版本&#xff1a;VMware-workstation-full-15.5.6-16341506.exe系统镜像版本&#xff1a;CentOS-6.10-x86_64-bin-DVD1.iso&#xff0c;全新安装&#xff0c;桌面版&#xff0c;可上网系统内存大小&#xff1a;1GB系统硬盘大小&#xff1a;20GB连接工具版…

《决胜B端 产品经理升级之路》 知识点总结

什么是b端产品&#xff1f; b端产品是指面向企业或组织的经营管理问题&#xff0c;旨在解决企业规模、成本、效率、品质和风控等方面的产品。这些产品主要帮助企业提高运营效率、降低成本、改善品质和控制风险等。b端产品适用于各种行业和企业类型&#xff0c;可以为企业带来深…

WordPress原创插件:disable-gutenberg禁用古腾堡编辑器和小工具

WordPress原创插件&#xff1a;disable-gutenberg禁用古腾堡编辑器和小工具 disable-gutenberg插件下载:https://download.csdn.net/download/huayula/89616495

【zlm】针对单个设备的音频的编码的设置

目录 结论 原理 测试 结论 为了防止zlm音频配置里设置成opus优先&#xff0c;在国标推流时&#xff0c;调用push时&#xff0c;默认加上codecpcma 如下 https://10.60.100.196:10443/index/api/webrtc?applive&streamtest&typepush&codecpcma 原理 测试 …

ASP.Net Core设置接口根路径的方法

使用asp.net core开发微服务项目&#xff0c;需要给每个服务设置不同的根路径&#xff0c;这样既能使用网关转发请求&#xff0c;又方便对单个服务进行测试&#xff0c;保证请求路径的统一。 设置方法需要使用中间件&#xff0c;在Program.cs添加如下代码 app.UsePathBase(&qu…

量化投资基础(四)之AR、MA、ARMA与ARIMA模型

点赞、关注&#xff0c;养成良好习惯 Life is short, U need Python 量化投资基础系列&#xff0c;不断更新中 1 引言 时间序列经典模型主要有: 自回归模型&#xff08;Auto Regressive&#xff0c;AR&#xff09;移动回归模型&#xff08;Moving Average&#xff0c;MA&…

无线领夹麦克风哪个品牌好,哪款领夹式麦克风性价比高

随着自媒体行业的蓬勃发展&#xff0c;内容创作者对高质量音频设备的需求日益增长。无线领夹麦克风&#xff0c;凭借其便携性、高音质与灵活性&#xff0c;正逐渐成为视频制作、直播互动及日常Vlog记录的标配工具。其兴起不仅反映了创作者对专业录音品质的追求&#xff0c;也体…

Web安全学习

1 计算机网络与协议 1.1 网络基础 1.1.1 计算机通信网的组成 计算机网络由通信子网和资源子网组成。 通信子网&#xff1a;负责数据的无差错和有序传递&#xff0c;其处理功能包括差错控制、流量控制、路由选择、网络互连等。 资源子网&#xff1a;是计算机通信的本地系统环境…

全球油价与棕榈油市场波动

一、油价暴跌与经济衰退担忧 周一&#xff08;8月5日&#xff09;欧盘时段&#xff0c;油价暴跌&#xff0c;两大主要基准油价均下跌逾2%&#xff0c;触及八个月低点。油价的急剧下跌主要是由于人们担心全球最大的石油消费国美国可能陷入衰退。全球疲弱的经济数据加剧了人们对燃…

绿色精益生产新潮流:环保也能成为竞争力!

在当今这个快速变化且竞争激烈的市场环境中&#xff0c;企业面临着前所未有的挑战。为了保持竞争力并实现可持续发展&#xff0c;企业必须不断探索和采用更高效、更灵活的生产管理方式。精益生产&#xff0c;作为一种源自日本丰田汽车公司的生产哲学&#xff0c;凭借其消除浪费…

Animate软件基本概念:组和文本

这里继续介绍Animate软件中的基本概念&#xff0c;组和文本两个概念。 FlashASer&#xff1a;AdobeAnimate2021软件零基础入门教程https://zhuanlan.zhihu.com/p/633230084 FlashASer&#xff1a;实用的各种Adobe Animate软件教程https://zhuanlan.zhihu.com/p/675680471 Fl…

Go - 10. * 值类型和指针类型的差异

目录 一.引言 二.接收者类型 三.代码示例 1.指针接收者 2.值接收者 3.运行结果对比 4.代码修改 5.刨根问底 四.总结 一.引言 go 语言中 func (c *Title) 和 func (c Title) 两个方法的传参差一个 * 号&#xff0c;二者的区别是一个是指针类型&#xff0c;一个是值类型…

scratch水仙花数 2024年6月scratch四级 中国电子学会图形化编程 少儿编程等级考试四级真题和答案解析

目录 scratch水仙花数 一、题目要求 1、准备工作 2、功能实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、解题思路 1、思路分析 2、详细过程 四、程序编写 五、考点分析 六、推荐资料 1、入门基础 2、蓝桥杯比赛 3、考级资料 4、视频课程 5、p…

深度学习入门(一):感知机与输入数据

单层感知机与多层感知机 单层感知机&#xff08;Single-Layer Perceptron&#xff09;和多层感知机&#xff08;Multi-Layer Perceptron&#xff0c;简称MLP&#xff09;是神经网络的基本形式&#xff0c;用于执行各种机器学习任务&#xff0c;包括分类和回归。它们都基于早期…

赚他10个亿...

体育竞技 & 商业价值 虽然昨天遭受了全球黑一&#xff0c;但四年一度的奥运会还是要关注的。 不知道最近大家是否有关注巴黎奥运会&#xff0c;印象深刻的项目又是哪个&#xff1f; 截止于发稿前&#xff0c;目前「金牌榜」上&#xff0c;中国以微弱优势位于第一&#xff0…

CDGA/CDGP数据治理证书:含金量高,职场竞争力提升的明智之选

在当今这个数据驱动的时代&#xff0c;数据已成为企业最宝贵的资产之一&#xff0c;而高效、合规的数据治理则是挖掘数据价值、驱动业务增长的关键。随着企业对数据治理重视程度的不断提升&#xff0c;拥有专业数据治理知识和技能的人才变得尤为稀缺。在此背景下&#xff0c;CD…

win10批量修改文件名,记得保存文件

打开需要修改的文件夹新建文本内容如下 DIR . /B>文件列表.csv 并另存为bat文件,注意编码&#xff0c;双击bat文件生成Excel 打开Excel 填写你的文件名至B列&#xff0c;如我需要在文件名前面都加上spi5_公式D1&A1 C列输入"REN "&"""&q…

【C++BFS算法】2059. 转化数字的最小运算数

本文涉及知识点 CBFS算法 LeetCode2059. 转化数字的最小运算数 给你一个下标从 0 开始的整数数组 nums &#xff0c;该数组由 互不相同 的数字组成。另给你两个整数 start 和 goal 。 整数 x 的值最开始设为 start &#xff0c;你打算执行一些运算使 x 转化为 goal 。你可以…