类和对象(下)

目录

1.初始化列表

1.1 构造函数体内的赋值

1.2 初始化列表

1.对象整体定义和成员变量定义的区别

2.初始化列表的写法

1.3 和C++11的联系

1.4 针对初始化列表的建议

2.静态成员

2.1 静态成员变量

1.概念

2.特性

2.2 静态成员函数

1.概念

2.特性

3.友元

3.1 友元函数

3.2 友元类

4.内部类

4.1 概念

4.2 特性

5.隐式类型转换和explicit关键字

5.1 隐式类型转换

1.支持隐式类型转换的前提

2.隐式类型转换的底层原理

2.1 复习:C语言的隐式类型转换和const引用

2.2 自定义类型的隐式类型转换

3.隐式类型转换的场景

第一种:string类的构造场景

第二种:string对象的传参场景

5.2 explicit

6.匿名对象

6.1 匿名对象的创建

6.2 匿名对象的特点

6.3 匿名对象的场景

1.方便类中成员函数的调用

2.某些传参场景(同上隐式类型转换)

7.拷贝对象时的编译器优化

7.1 连续的构造+拷贝->一次构造

7.2 连续的拷贝+拷贝 ->一次拷贝

7.3 连续的拷贝+赋值 ->不能优化


 

1.初始化列表

1.1 构造函数体内的赋值

以前成员变量的赋值我们都是在构造函数中完成的,但是这种赋值单单是赋值,不是初始化,更不是定义(初始化只有一次,赋值可以多次),成员变量的定义和初始化都不是在函数体中完成的,那是在哪里呢?初始化列表!

函数体中赋值:

class Date
{
public:
    Date(int year, int month, int day)
    {
      _year = year;
      _month = month;
      _day = day;
    }
private:
    int _year;
    int _month;
    int _day;
};

1.2 初始化列表

1.对象整体定义和成员变量定义的区别

当我们写下Date d时,d一个对象被整体定义了,但是d里面的成员定义好了吗?并没有!只有在构造函数调用时的初始化列表中,成员变量才被定义。(比喻:我们把房子建好了,这个房子相当于d对象整体,但是房子里面的各个房间并没有装修好,这些房间也就是各个成员)

2.初始化列表的写法

成员变量的定义和初始化都在初始化列表中完成,那么我们怎么写呢?

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。

初始化列表和函数体内的初始化是可以混着用的。

class Date
{
public:
    Date(int year, int month, int day)
      : _year(year)
      , _month(month)
      , _day(day)
    {}
private:
    int _year;
    int _month;
    int _day;
};

注意几点:

  1. 我们自己不显示写初始化列表时,编译器自己有个初始化列表,它的初始化列表针对成员变量只有定义没有初始化

  2. 针对某些特殊成员,他们必须在定义的时候初始化,比如:引用类型、const类型;如果类中有这些成员而我们没有显示写初始化列表时,这些成员就只有初始化而没有定义,编译就会出错,所以这两种成员我们只能在初始化列表中初始化。

  3. 如果我们不显示写初始化列表:其他的内置类型(除引用、const类型)也会定义,但是只有定义没有初始化,所以这些内置类型会给随机值;而自定义类型,调用它的默认构造。

  4. 如果自定义类型没有可用的默认构造,并且我们没有显示写初始化列表,也会导致编译出错,所以针对这种自定义类型,我们只能在初始化列表中显示调用他的其他构造函数。

  5. 总结:必须用初始化列表来初始化的三种类型:

    • 引用类型

    • const类型

    • 没有默认构造的自定义类型

1.3 和C++11的联系

C++11针对内置类型,在声明处是可以给缺省值的,但是这个为什么叫缺省值?和缺省参数有关系吗?

  • 这里的缺省值就是给初始化列表用来初始化内置类型的!

至于为什么叫缺省值,因为他和缺省参数一样是个备胎,我们显示写初始化列表给了值以后,就不用缺省值了。

  • 初始化列表的定义初始化顺序是按照类中的声明顺序来的

class A
{
public:
    A(int a)
        :_a1(a)
        , _a2(_a1)
    {}
​
    void Print() {
        cout << _a1 << " " << _a2 << endl;
    }
private:
    int _a2;
    int _a1;
};
​
int main() 
{
    A aa(1);
    aa.Print();
}

上面代码的声明次序中,a2在a1前面,所以我们先初始化a2,再初始化a1,所以a1为1,a2为随机值。

1.4 针对初始化列表的建议

其实在绝大多数情况下,我们都建议写初始化列表。因为不管你是否使用初始化列表,对于自定义类型的成员变量,一定会先使用初始化列表初始化。

  • 前面说明,这三种类型必须使用初始化列表初始化:引用类型、const类型、没有默认构造的自定义类型。

  • 但是有默认构造的自定义类型也建议使用初始化列表,因为我们在函数体中不太好显示初始化自定义类型。比如下面代码:

class A
{}
​
class B
{
public:
    B(int a)
    //:_aobj(a) 初始化列表这么写
    {
        A tmp(a);
        _aobj = tmp;
    }
​
private:
    A  _aobj;
}

这种写法显然有点孬,不如初始化列表高效。

  • 还有内置类型也是建议使用初始化列表的,但并不是全部,比如涉及资源申请的指针类型,有时候比较冗余的话还是在函数体中赋值比较正常。

2.静态成员

2.1 静态成员变量

1.概念

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;

2.特性

  1. 静态成员变量不属于任何一个对象,而是属于整个类的,或者说为这个类的所有对象所共享。

  2. 静态成员变量的使用:

    类名::静态成员变量

    对象名.静态成员变量

  3. 静态成员变量的定义:

    • 静态成员变量能在声明位置给值吗?结合前面所学:不可以!

      声明位置给的缺省值是给初始化列表的,而初始化列表是在对象定义完后用来定义初始化成员变量的,但是静态成员变量属于一个对象吗?显然不是,所以静态成员变量不能在类里面定义。

    • 静态成员变量只能在类外定义,声明和定义是要分离的,具体怎么写,如下:

      class A
      {
      public:
          //....
      ​
      private:
          int _a = 1;
          static int _b;//静态成员变量声明
      };
      int A::_b = 0;//静态成员变量定义

      注意几点:

      1. 不要忘记指定类域。

      2. 定义时要加类型,但不要加static。

      3. 这里在声明定义时访问静态成员变量并不受访问限定符的限制,只是定义而已。

  4. 静态成员变量也受访问限定符的影响。

2.2 静态成员函数

1.概念

用static修饰的成员函数,称之为静态成员函数。

2.特性

  1. 静态成员函数不属于任何一个对象,而是属于整个类的,或者说为这个类的所有对象所共享。

  2. 静态成员函数的使用:

    类名::静态成员函数

    对象名.静态成员函数

    class A
    {
    public:
        //....
        static int getStatic()
        {
            return _b;
        }
    ​
    private:
        int _a = 1;
        static int _b;
    };
    int A::_b = 0;
    int main() 
    {
        A a;
        int a = A::getStatic(); 
        int b = a.getStatic(); 
    }
    ```

  1. 静态成员函数不属于任何一个对象,也不需要一个对象来调用,那么它的函数参数有this指针吗?

    显然没有!因此静态成员函数不能访问非静态成员变量、调用非静态成员函数。

  2. 静态成员函数也受访问限定符的影响。

  • 总结:这么理解静态成员变量和静态成员函数更加清晰:

静态成员变量和静态成员函数其实就是受限制的全局变量和全局函数,他们并不在类中,包括静态成员变量(所以只有非静态成员变量是在类中的)。他们受什么限制呢?类域限制和访问限定符限制。

3.友元

针对某些场景我们在类外需要访问类中的私有成员,我们目前有两种方法解决:

  1. 变私有为公有。

  2. 添加这些私有成员的get函数。

方法一过于暴力对封装的破坏性大,方法二又不适合C++(Java经常这么用),这时候就用到了C++的一种突破封装的语法:友元。

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装(但相比直接修改访问限定符,破坏程度较小),所以友元不宜多用。 友元分为:友元函数和友元类

3.1 友元函数

友元函数的经典场景就是operator<<和operator>>的重载。我们尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。

class Date
{
public:
    //...
​
    //友元函数
    friend ostream&  operator<<(ostream& out, const Date& d);//cout << d1
    friend istream&  operator>>(istream& in, Date& d);//cin >> d1
private:
    //...
};
​
ostream&  operator<<(ostream& out, const Date& d) 
{
    out << d._year << ":" << d._month << ":" << d._day << endl;
    return out;
}
​
istream&  operator>>(istream& in, Date& d) 
{
    in >> d._year >> d._month >> d._day; 
    return in;
}

  1. 友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

  2. 友元函数可访问类的私有和保护成员,但不是类的成员函数。

  3. 友元函数不能用const修饰。

  4. 友元函数可以在类定义的任何地方声明,不受类访问限定符限制。

  5. 一个函数可以是多个类的友元函数。

  6. 友元函数的调用与普通函数的调用原理相同。

3.2 友元类

class Time
{
	friend class Date;  //声明Date类为Time类的友元类,则在Date类中就直接访问Time类中的私有成员变量
public:
	Time(int hour = 0, int minute = 0, int second = 0)
		: _hour(hour)
		, _minute(minute)
		, _second(second)
	{}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	void SetTimeOfDate(int hour, int minute, int second)
	{
		// 直接访问Time类私有的成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

  1. 友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

  2. 友元关系是单向的,不具有交换性。(比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。)

  3. 友元关系不能传递。(如果C是B的友元, B是A的友元,不能说明C时A的友元。)

  4. 友元关系不能继承。

4.内部类

4.1 概念

如果一个类定义在另一个类的内部,这个内部类就叫做内部类。

4.2 特性

  1. 内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。

  2. 外部类对内部类没有任何优越的访问权限。

  3. 内部类天生为外部类的友元类,但是外部类不是内部类的友元。

  4. 内部类的实例化要指定外部类的类域。

  5. 内部类可以定义在外部类的public、protected、private限定符中。

  6. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象名/类名。

  7. sizeof(外部类)=外部类,和内部类没有任何关系。

class A
{
private:
	static int k;
	int h;
public:
	class B // B天生就是A的友元
	{
	public:
		void foo(const A& a)
		{
			cout << k << endl;
			cout << a.h << endl;
		}
	};
};
int A::k = 1;

int main()
{
	A::B b; 
	b.foo(A());

	return 0;
}

5.隐式类型转换和explicit关键字

5.1 隐式类型转换

1.支持隐式类型转换的前提

构造函数不仅可以初始化对象,而且对于单参数构造函数(单个参数或者因为缺省参数也可以传单个参数),还具有类型转换的作用。比如Date类:

class Date
{
public:
	Date(int year = 1970,int month = 1, int day = 1)
		:_year(year)
		,_month(_month)
		,_day(_day)
	{}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d = 2023;//针对单参数构造函数,支持隐式类型转换

	return 0;
}

2.隐式类型转换的底层原理

2.1 复习:C语言的隐式类型转换和const引用
  • C语言的隐式类型转换: double b = 3.14; int a = b;

  • 类型转换(强制类型转换和隐式类型转换)在底层上会产生一个临时变量,这个临时变量具有常量的属性。类型转换是并不会真的改变原来变量的类型的。

  • 所以使用引用时,是要加上const的:double b = 3.14;const int& a = b;

2.2 自定义类型的隐式类型转换

因为构造函数的特殊性而让自定义类型也能隐式类型转换,底层也是这个原理:

  • Date d = 2023; 底层上会先使用2023构造一个临时对象Date tmp(2023, 1, 1);然后临时对象tmp拷贝构造给d。(所以这个过程是构造+拷贝,但是编译器会优化成一次构造,具体后面会系统说)

  • 这个临时对象tmp具有常量的属性,所有涉及到引用时,要加const:

    const Date& d = 2023;

3.隐式类型转换的场景

第一种:string类的构造场景

C++有个string类,它的构造函数可以认为是这样的:string(const char* str = "");

一般我们是这么构造string类的:string str("hello world");

但是我们也可以这么构造:string str = "hello world";

这里就是涉及了隐式类型转换。

第二种:string对象的传参场景

还有一种场景,就是string类的传参,拿插入一个string对象为例:

一般来说Push只能这么使用:

vector<string> v;

string str("string");

v.Push(str);

但是函数接口这么写的话就不一样了:void Push(const string& str); 也就是要加上const,以前说过const有两个作用:

  1. 如果函数体中不用修改str对象,起到防止误操作修改str对象的作用。

  2. string类的const对象也可以传进来。

现在又多了一种:可以让 涉及隐式类型转换的临时对象(或者匿名对象)也传进来,也就是这种场景:

vector<string> v;

v.Push("string");

这里传参过程是这么传的:cosnt string& str = "string";中间会产生一个临时对象,临时对象具有常性,所有只能用const引用接收,如果函数接口只是正常的string引用是传不进来的。

5.2 explicit

用explicit修饰构造函数,将会禁止构造函数的隐式类型转换。

6.匿名对象

6.1 匿名对象的创建

//针对A类

//普通对象创建:
A a;
//匿名对象创建:
A();

A aa();//编译器识别成函数声明

注意:无参的匿名对象构造,也是要加()的。

6.2 匿名对象的特点

  1. 没有名字。

  2. 生命周期只有一行,下一行他就会自动调用析构函数。

  3. 使用匿名对象中间会先构造一个临时对象。这里是和隐式类型转换相似的,所以匿名对象也可以这么用:

    string str = string("hello world");

    const string& str = string("hello world");

6.3 匿名对象的场景

1.方便类中成员函数的调用

class Solution 
{
public:
	int Sum_Solution(int n) 
	{
		//...
		return n;
	}
};

int main()
{
	Solution().Sum_Solution(10);
	return 0;
}

这样就可以不创建对象来调用成员函数了。

2.某些传参场景(同上隐式类型转换)

上面我们说过,对于Push函数接口,如果Push的是string类对象,接口这么写:void Push(const string& str);

这样可以接受隐式类型转换的临时对象,但是他也是可以接受匿名对象的,因为我们可以认为匿名对象也有个临时对象

class StringContainer
{
public:
	void Push(const string& str)
	{
		//....
	}
};

int main()
{
	StringContainer sc;
	sc.Push("hello");
	sc.Push(string("world"));


	return 0;
}

匿名对象的生命周期只有一行,但是我们可以认为使用const引用过后可以延长他的生命周期。

7.拷贝对象时的编译器优化

7.1 连续的构造+拷贝->一次构造

场景1

string str = "hello world";

场景2

void fun(string str)
{
	//...
}

f("hello");//隐式类型转换:string str = "hello";

场景3

void fun(string str)
{
	//...
}

f(string("hello"));//匿名对象场景:string str = string("hello");

7.2 连续的拷贝+拷贝 ->一次拷贝

场景:传值返回的函数

string fun()
{
	string str("hello");
	return str;
}

string str1 = fun();

7.3 连续的拷贝+赋值 ->不能优化

string fun()
{
	string str("hello");
	return str;
}

string str1("hello");
str1 = fun();//这里的=是赋值运算符重载 ,不能优化

tmp临时对象的拷贝是拷贝构造,临时对象再赋值给str1对象是赋值运算符重载,两者不能优化成一次拷贝

注意:上述的两种优化都是在同一个表达式中才能优化的,跨表达式不能这样优化。

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

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

相关文章

win10安装pytorch(py39)

cuda≤11.6&#xff0c;观察控制面板 观察torch对应cuda版本 https://download.pytorch.org/whl/torch/ 安装cuda11.6.0 CUDA Toolkit Archive | NVIDIA Developer cmd输入nvcc -V 编辑国内镜像源 .condarc anaconda prompt输入 查看环境 conda env list 安装py3.9…

【初始前后端交互+原生Ajax+Fetch+axios+同源策略+解决跨域】

初始前后端交互原生AjaxFetchaxios同源策略解决跨域 1 初识前后端交互2 原生Ajax2.1 Ajax基础2.2 Ajax案例2.3 ajax请求方式 3 Fetch3.1 fetch基础3.2 fetch案例 4 axios4.1 axios基础4.2 axios使用4.2.1 axios拦截器4.2.2 axios中断器 5 同源策略6 解决跨域6.1 jsonp6.2 其他技…

线程-Thread类及常见方法

目录 一、创建线程 1.继承 Thread 类 2. 实现 Runnable 接口 3.匿名内部类创建 Thread 子类对象 4. 匿名内部类创建 Runnable 子类对象 5. lambda 表达式创建 Runnable 子类对象 二、Thread 类及常见方法 2.1 Thread 的常见构造方法 2.2 Thread 的几个常见属性 2.3 启…

防止应用程序截屏(容器式,防止极域电子教室和录屏软件录制)

核心原理、实现目的 1、使用Panel容器将外部窗口嵌入自己写的程序 2、使用防止截屏的函数来对窗口透明&#xff0c;这可以使本窗口内所有窗口在录屏软件上消失 3、解放&#xff0c;抓取&#xff0c;存储句柄&#xff0c;实现摆脱录屏&#xff08;极域监控&#xff09; 程序…

Error querying database. Cause: java.lang.reflect.InaccessibleObjectException:

最近开发过程中&#xff0c;居然碰到了一个Arrays.asList的错&#xff0c;怎么个场景呢&#xff1f;传参一个用固定符号拼接的字符串&#xff0c;需要转成集合然后再myBatis里in判断。然后就报错了。 一、代码层面 service层面&#xff1a; shortDetailUrlList Arrays.asLi…

2022年03月 Scratch(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch等级考试(1~4级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 以下四个选项中,运行哪个积木块,可能得到523这个数值? A: B: C: D: 答案:B 四个选项都遵循统一的公式:随机数ⅹ10+3=523,因此可以得出随

汽车电子 -- 根据DBC解析CAN报文

采集的CAN报文&#xff0c;怎么通过DBC解析呢&#xff1f;有一下几种方法。 首先需要确认是CAN2.0 还是CAN FD报文。 还有是 实时解析 和 采集数据 进行解析。 一、CAN2.0报文实时解析&#xff1a; 1、CANTest工具 使用CAN分析仪 CANalyst-II&#xff0c;采集CAN报文。 使用…

免费获取GPT-4的五种工具

不可否认&#xff0c;由OpenAI带来的GPT-4已是全球最受欢迎的、功能最强大的大语言模型&#xff08;LLM&#xff09;之一。大多数人都需要使用ChatGPT Plus的订阅服务去访问GPT-4。为此&#xff0c;他们通常需要每月支付20美元。那么问题来了&#xff0c;如果您不想每月有这笔支…

导入PIL时报错

在导入PIL时,报以下错误: 查找原因 参考博客 Could not find a version that satisfies the requirement PIL (from versions: ) No matching distributi-CSDN博客,按照wheel后,安装PIL时,报如下的错误。 查找说是python版本与wheel文件版本不同,确认本机python版本 …

Git仓库瘦身大作战:133M 到 4M 的实战

开局两张图 瘦身前瘦身后 目录 开局两张图前言下载 BFG克隆代码Git 仓库瘦身清理存储库储存库 GC推送仓库 Git 瘦身验证结语开源项目 前言 在进行项目开发的过程中&#xff0c;代码仓库的体积可能会逐渐增大&#xff0c;特别是在版本控制系统中保留了大量的历史提交记录和不必…

【经典小练习】修改文件中的数据

文章目录 &#x1f339;例子&#x1f33a;思路&#x1f6f8;方法一✨报错解决 &#x1f6f8;方法二 &#x1f339;例子 文本文件中有下面的数据 2-1-9-4-7-8 将文件中的数据进行排序&#xff0c;变成下面的数据 1-2-4-7-8-9 &#x1f33a;思路 要对这些数据进行排序&#xf…

堆的实现(堆的插入、堆的删除等)超级全

堆的实现&#xff08;堆的插入、堆的删除等&#xff09;超级全 文章目录 堆的实现&#xff08;堆的插入、堆的删除等&#xff09;超级全一、前期基础知识1.树结构①树的定义②树的相关概念③二叉树④满二叉树和完全二叉树a.满二叉树b.完全二叉树 ⑤二叉树的性质⑥二叉树顺序结构…

【JAVA】SpringBoot + mongodb 分页、排序、动态多条件查询及事务处理

【JAVA】SpringBoot mongodb 分页、排序、动态多条件查询及事务处理 1.引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- mongodb ↓ -->&…

多功能智能灯杆主要功能有哪些?

多功能智能灯杆这个词相信大家都不陌生&#xff0c;最近几年多功能智能灯杆行业发展迅速&#xff0c;迅速取代了传统路灯&#xff0c;那么多功能智能灯杆相比传统照明路灯好在哪里呢&#xff0c;为什么大家都选择使用叁仟智慧多功能智能灯杆呢&#xff1f;所谓多功能智能灯杆着…

如何开发有趣而富有创意的营销小游戏

在数字化时代&#xff0c;企业通过创意而独特的方式与目标受众互动&#xff0c;已成为提高品牌知名度和用户参与度的重要手段之一。其中&#xff0c;设计一款引人入胜的营销小游戏&#xff0c;不仅能吸引用户的眼球&#xff0c;还能有效传达品牌信息。以下是一些建议&#xff0…

人工智能-注意力机制之注意力汇聚:Nadaraya-Watson 核回归

查询&#xff08;自主提示&#xff09;和键&#xff08;非自主提示&#xff09;之间的交互形成了注意力汇聚&#xff1b; 注意力汇聚有选择地聚合了值&#xff08;感官输入&#xff09;以生成最终的输出。 本节将介绍注意力汇聚的更多细节&#xff0c; 以便从宏观上了解注意力机…

canvas高级动画001:文字瀑布流

canvas实例应用100 专栏提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。 canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重要的帮助。 文章目录 示例…

基于官方YOLOv4开发构建目标检测模型超详细实战教程【以自建缺陷检测数据集为例】

本文是关于基于YOLOv4开发构建目标检测模型的超详细实战教程&#xff0c;超详细实战教程相关的博文在前文有相应的系列&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a;《基于yolov7开发实践实例分割模型超详细教程》 《YOLOv7基于自己的数据集从零构建模型完整训练、…

electron windows robotjs 安装教程

Robotjs 安装 前言第一步 : 安装python第二步 : 安装Visual Studio 2022第三步 : 安装robotjs 前言 robotjs可以控制鼠标键盘&#xff0c;获取屏幕内容&#xff0c;配合electron可做很多自动化操作。windows下配置环境有很多坑&#xff0c;很多文章都太旧了。试了很多次发现了…

杰发科技AC7801——Flash模拟EEP内存分布情况

简介 本文记录了在使用AutoChips芯片Flash模拟EEP过程中的一些理解 核心代码如下 #include <stdlib.h> #include "ac780x_sweeprom.h" #include "ac780x_debugout.h"#define SWEEPROM_SIZE (2048UL) /* Ssoftware eeprom size(Byte) */ #define TE…