C++入门全集(1):初窥门径

一、前言

C++是一种计算机高级程序设计语言,它在C语言的基础上进行了进一步的扩充和完善,并增加了许多有用的库,是一种面向对象的程序设计语言。

所以,C++是兼容C语言语法的。

我打算把所有C++入门需要学习的知识整合成一个全集,方便各位,也能方便自己复习。

本文主要讲解C++相对C语言的查漏补缺和优化部分,也为后续学习类和对象打基础。

二、C++关键字

C语言有32个关键字,而C++有63个关键字,不过这些关键字无法一次学完,我们这里先混个脸熟

asmdoifreturntrycontinue
autodoubleinlineshorttypedeffor
booldynamic_castintsignedtypeidpublic
breakelselongsizeoftypenamethrow
caseenummutablestaticunionwchar_t
catchexplicitnamespacestatic_castunsigneddefault
charexportnewstructusingfriend
classexternoperatorswitchvirtualregister
constfalseprivatetemplatevoidtrue
const_castfloatprotectedthisvolatilewhile
deletegotoreinterpret_cast

三、命名空间

在C语言中,命名冲突问题时常存在,例如我们无法定义一个名为rand的变量,因为在stdlib.h中已经有函数取名为rand了

如果我们将所有的变量名、函数名和类名都存放在全局作用域中,就可能导致命名冲突,所以C++中出现了命名空间这一概念。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染。

3.1 命名空间的定义

要定义一个命名空间,需要使用到关键字namespace,然后给命名空间取个名,再用大括号括起来,大括号中即为命名空间的成员

namespace test
{
    int rand = 10;
}

除了变量,命名空间中还可以定义函数或者结构体类型

另外,命名空间还可以嵌套,如:

namespace test1
{
    namespace test2
    {
        int rand = 10;
    }
}

如果命名空间也同名了怎么办呢?同一个工程的不同文件中可能出现多个同名的命名空间,此时编译器会将它们合并为同一个命名空间。

3.2 命名空间的使用

当我们学会在命名空间中定义变量、函数和类型后,该如何使用它们呢?

(1)使用作用域限定符

所谓作用域限定符就是两个冒号 "::"

像这样,就可以访问到两个不同命名空间的变量a了

(2)使用using引入命名空间成员

变量b是命名空间test1的成员,我们可以通过using引入它,所以下面main函数中我们访问变量b就不需要使用作用域限定符了

(3)使用using namespace

像这样,我们使用using namespace引入命名空间test1,所以没有使用作用域限定符的a就访问到了test1中的a,而下面使用了作用域限定符就会访问test2中的a

在后面学习C++的输入和输出中,我们会学到cout和cin,二者的定义是放在名为std的一个C++标准库的命名空间中的,C++将标准库的定义实现都放在这个命名空间中。

如果不使用using namespace,每次使用的时候就要用到作用域限定符,也就是变成std::cout和std::cin,十分的麻烦。

所以我们在使用它们的时候一般会先输入“using namespace std;”,避免每次要输入和输出的时候都使用作用域限定符。

四、C++的输入和输出

听说每一个程序员的第一个程序都是hello world,我们来使用C++实现一下

#include <iostream>

using namespace std;

int main()
{
    cout << "Hello world" << endl;
    return 0;
}

从这一段简短的代码中,我们可以总结如下信息:

  • cout和cin是全局的流对象,endl是特殊的C++符号,表示换行,它们都包含在<iostream>头文件中
  • <<是流插入运算符,>>是流提取运算符

实际上,cout和cin分别是ostream和istream类型的对象,而<<和>>在C语言中原本是位运算符,在C++却变了,这里也涉及到运算符重载的知识,在后续我们还会对其进行深入的学习

注意:早期的标准库将所有功能都在全局域中实现,并声明在以.h为后缀的头文件中,使用时也只需要包含对应头文件即可。而后来则将其实现在std命名空间下,为了和头文件区分,规定C++的头文件不带.h。一些旧编译器可能还支持<iostream.h>的格式,不过还是推荐使用<iostream>+std的方式

使用cout和cin的好处在于更方便,不需要像printf和scanf一样手动控制格式

缺点在于打印一串数据的情况会比较繁杂

所以我们根据实际情况选择更优的方式

在日常练习中,我们当然可以使用using namespace std,怎么方便怎么来。

但是这样做,整个标准库就全部暴露出来了,此时如果我们定义了和库中重名的类型/对象/函数,就会出现冲突问题。所以在项目开发中不推荐使用全局展开,我们可以指定命名空间访问,例如using std::cout和using std::cin来展开常用的命名空间成员

五、缺省参数

缺省参数就是我们声明或定义函数时可以为函数的参数指定一个值,在调用函数的时候没有指定的实参,就使用这个预先设定的默认值

缺省参数又分为全缺省参数和半缺省参数

关于缺省参数,我们需要注意几点:

(1)半缺省参数必须从右到左依次给出,不能间隔着给

(2)缺省参数不能同时出现在函数的定义和声明中

像这样,函数的定义和声明中同时出现缺省参数,而两个位置设置的值不同,编译器就无法确定该使用哪个缺省值

(3)缺省值必须是常量或者是全局变量

六、函数重载

C++允许在同一作用域中声明几个功能类似的同名函数,前提是这些同名函数的形参个数/类型/类型顺序不同。

例如我们以前在C语言中想实现Add函数,但是int和double类型的数据不能用同一个函数处理,每处理一种类型的数据就要写一个函数,函数间还不能同名

但是在C++中针对这个问题进行了优化,编译器会有一套自己的函数名修饰规则来修饰不同形参个数/类型/类型顺序的同名函数

#include <iostream>
using namespace std;

//参数类型不同
int Add(int x, int y)
{
    return x + y;
}

double Add(double x, double y)
{
    return x + y;
}

//参数个数不同
void f(int a)
{
    cout << "void f(int a)" << endl;
}

void f(int a, int b)
{
    cout << "void f(int a,int b)" << endl;
}

//参数类型的顺序不同
void f(int a, char b)
{
    cout << "void f(int a, char b)" << endl;
}

void f(char a, int b)
{
    cout << "void f(char a, int b)" << endl;
}

int main()
{
    Add(1, 2);
    Add(1.1, 2.2);

    f(1);
    f(1, 2);

    f(10, 'a');
    f('a', 10);

    return 0;
}

C++支持函数重载的原理就是——名字修饰(name Mangling)

名字修饰是一种在编译过程中,将函数、变量的名称重新改编的机制。简单来说就是编译器为了区分多个同名函数,规定了一个新的规则来对原本的名字进行修饰

为什么C语言不支持函数重载,是因为它的名字修饰规则过于简单,只是在函数名前面添加了下划线

拓展:在C++的函数前加上extern "C" 就可以让函数按照C语言的风格编译

这里可以看到,Add函数按照C语言的风格编译后名字变为了_Add

而在C++中,修饰规则得到了完善,所以可以支持函数重载

不过不同的编译器有自己的函数名修饰规则,上面的就是在Windows下vs的修饰规则,有点过于复杂了,有兴趣的同学可以自行深入学习。

接下来我们展示g++的修饰规则,它会比前者更加的简单易懂

#include <iostream>
using namespace std;

int Add(int x, int y)
{
    return x + y;
}

double Add(double x,double y)
{
    return x + y;
}

int main()
{
    Add(1, 2);
    Add(1.1, 2.2);
    return 0;
}

上面的是源文件代码,我们在终端中输入"g++ -S test.cpp -o test.s"来查看其汇编代码

可以看到,这两个就是上面int类型的Add函数和double类型的Add函数重载后的名字了。

其中_Z后跟着的数字就是原函数名的长度,后面的 "ii" 和 "dd" 就是参数的类型。

通过这些,我们就能理解为什么C语言不支持函数重载,而C++通过函数名的修饰规则可以区分同名函数了,只要参数个数/类型/类型顺序不同,修饰后的名字就不同,也就可以区分了。

需要注意的是,如果两个函数的函数名和参数个数/类型/类型顺序都相同,也是无法区分的。

七、引用

引用就是给已经存在的变量取一个别名,编译器不会为引用变量开辟内存空间,而是和它引用的对象共用同一块内存空间,引用的符号和取地址符号&一样。

需要注意的是,引用变量必须和被引用的对象是同一类型的

引用变量也可以作为被引用的对象

引用还可以代替指针的传址调用,例如以前我们要实现Swap函数,需要指针传地址才能成功交换,现在可以换成引用,此时形参是实参的别名。

当然,指针也可以进行引用

7.1 引用的特性

(1)引用在定义时必须初始化

(2)一个变量可以有多个引用,也就是取多个别名

(3)一旦引用变量已经引用过了某个对象,就再不能引用其他对象

可以看出,引用和指针还是有区别的,像在链表这些地方我们还是选择使用指针,而在一些输出型参数,也就是形参的改变要影响实参的地方我们可以选择使用引用

7.2 常引用

(1)如果被引用的对象被const修饰,而引用变量没有被const修饰会报错

(2)引用变量如果没有被const修饰,不能将常量作为引用对象

(3)引用变量没有被const修饰且和被引用对象不是同一类型时会报错

7.3 引用的使用场景

(1)引用作为函数参数,就是上面提到过的Swap

(2)引用作为函数返回值

int& Add(int a, int b)
{
	int c = a + b;
	return c;
}

int main()
{
	int& ret = Add(1, 2);
	Add(3, 4);

	cout << "Add(1, 2) is :" << ret << endl;
	cout << "Add(1, 2) is :" << ret << endl;

	return 0;
}

上面的代码会输出什么结果?

实际上,结果是未定义的。

第一次调用Add函数的时候,函数栈帧创建完毕,局部变量c(此时值为3)保存在Add的栈帧中。函数运行结束后,栈帧销毁,内存空间被系统回收,此时变量c已经没有意义了,所以ret引用了一块已经被释放的空间。

第二次调用Add函数和第一次一样,只不过局部变量c的值变为了7。

需要注意的是,虽然空间被回收,里面的东西却都还在。就像去住酒店,退房后里面的东西在没打扫前都是一直保持原样的,而不是说退房后里面的东西就都没了。

所以,当我们第一次输出ret的值是7,第二次就变为了随机值

因此,如果出了函数作用域后返回对象没有销毁(static,malloc等),则可以使用引用返回,否则必须使用传值返回

另外的,引用返回还可以修改返回的对象

#include <iostream>

using namespace std;

int& Func(int* a, int i)
{
	return a[i];
}

int main()
{
	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	for(int i = 0 ;i<10;i++)
	{
		Func(a, i) = i * 10;
		cout << a[i] << " ";
	}
	return 0;
}

7.4 传值和引用返回的效率比较

以值作为函数参数或返回值类型的时候,函数并不会直接传递实参或者将变量本身直接返回,而是会创建一个临时变量作为“中间商”,因此会影响效率,我们以传值返回为例测试一下

#include <iostream>
#include <time.h>
using namespace std;

struct A 
{ 
	int a[10000];
};

A a;

// 值返回
A TestFunc1()
{
	return a;
}
// 引用返回
A& TestFunc2()
{ 
	return a;
}

int main()
{
	// 以值作为函数的返回值类型
	size_t begin1 = clock();
	for (size_t i = 0; i < 100000; ++i)
		TestFunc1();
	size_t end1 = clock();

	// 以引用作为函数的返回值类型
	size_t begin2 = clock();
	for (size_t i = 0; i < 100000; ++i)
		TestFunc2();
	size_t end2 = clock();

	// 计算两个函数运算完成之后的时间
	cout << "TestFunc1 time:" << end1 - begin1 << endl;
	cout << "TestFunc2 time:" << end2 - begin2 << endl;
	return 0;
}

可以看到,传值返回比引用返回效率低了许多

7.5 引用和指针的区别

从底层来看,引用还是按照指针的方式来实现的,我们可以观察一下汇编代码

二者的不同点在于:

  • 从概念上来说,引用是定义了一个变量的别名,指针是存储了变量的地址
  • 引用在定义时必须初始化,指针可以不用
  • 引用在初始化时引用了一个对象后,就不能再引用其他对象,而指针可以随时改变指向
  • 没有空引用,但是有空指针
  • 从sizeof来说:引用的sizeof结果是引用类型的大小,但指针始终是地址所占字节个数
  • 引用的++是被引用的对象+1,而指针++是指针向后偏移一个类型的大小
  • 多级指针之间的含义不同,但多级引用指向的是同一块空间
  • 访问对象的方式不同,指针需要解引用,而引用是由编译器自己处理
  • 引用比指针使用起来更安全

八、内联函数

在C++中,为了解决一些频繁调用的小函数大量消耗栈内存的问题,引入了inline修饰符

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开函数,不用调用函数建立栈帧。 

其作用很像宏函数,不过相比宏函数的缺点,它可以调试,有类型的检查,不容易出错。

inline int Add(int x, int y)
{
	return x + y;
}

inline是一种比宏更简单更好的方式,并且不降低效率。

如果不用inline修饰,在汇编代码中我们会看到用call指令去调用函数的操作

但是在Add函数前增加inline将其修改为内联函数,在编译期间编译器会用函数体替换函数的调用。

我们可以在release模式下查看汇编代码中是否存在call Add的指令

如果想在debug模式下查看,需要进行一些设置,因为在debug模式中默认情况下编译器不会对代码进行优化

在项目中点击属性

在属性中修改这两项

​​​​​​​

然后我们在debug模式下查看内联函数的汇编代码

可以看到,已经没有call指令了。

inline是一种以空间换时间的做法,这里的空间指编译出来的可执行文件的大小,内联函数会直接在程序中展开,越长的函数调用越多次就会使文件大小暴增。

inline只是向编译器发出的一个请求,编译器可以选择忽略该请求

假如有一个1000行的函数,我们要在程序中调用1000次,如果不用inline修饰的话就是1000+1000行代码,如果用inline修饰的话就是1000*1000行代码,换做是你你觉得哪个更好?

可以看到,太长的函数尽管用了inline修饰,编译器也会忽略掉,选择call调用函数

一般建议将函数规模较小、非递归且调用次数较多的函数使用inline修饰

需要注意的是,inline不建议声明和定义分离,会导致链接错误,对于内联函数最好直接在头文件中定义

此时在test.cpp中,编译时展开了Func.h的内容,而里面只有Func函数的声明,只能等链接的时候在符号表中寻找对应函数,也就是通过call指令调用函数,而内联函数是不会生成call指令的

所以我们直接在头文件中定义内联函数即可

九、auto关键字(C++11)

随着我们不断深入学习C++,程序越来越复杂,使用的类型也会越来越复杂,不仅难于拼写,还容易出错。

除了typedef,我们还有另一个选择就是auto,它可以帮助我们自动推导类型

需要注意的是,使用auto定义变量时必须对变量进行初始化,因为在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。

因此,auto并非是一种类型的声明,而是一个占位符,编译器在编译时会将auto替换为变量实际的类型 

9.1 使用auto的注意事项

(1)如果auto后加上*就限定了赋值的对象必须是指针

(2)我们使用auto时,也可以在同一行定义多个变量,前提是这些变量必须是相同的类型

(3)用auto声明引用类型时还是要加上&的

#include <iostream>
using namespace std;

int main()
{
	int a = 10;
	auto& b = a;

	return 0;
}

9.2 不能使用auto的场景

(1)auto不能在函数的参数中使用

(2)auto不能用于声明数组

十、范围for(C++11)

现代C++倾向于让各种繁杂的操作变得简洁,因此诞生了许多语法糖,范围for算是其中的典型。

在C++98/03中,不同的容器和数组遍历的方式有很多,不够统一,也不够简洁。

而C++11出现了基于范围的for循环,可以更简洁的去遍历容器和数组,也更方便我们使用了。

以前我们遍历数组的方式如下:

int main()
{
	int array[] = { 1,2,3,4,5 };
	for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++)
	{
		cout << array[i] << " ";
	}
	cout << endl;
	return 0;
}

对于一个有范围的集合而言,由程序员来声明循环的范围未免太多余,还容易出错。接下来我们来使用范围for遍历数组:

for循环的括号中由冒号":"分为两部分,左边是范围内用于迭代的变量,右边表示被迭代的范围

这里也用到了前面的auto关键字,如果我们想对范围内的元素进行修改,还可以用到引用&

和普通循环一样,范围for中也可以使用continue和break。

需要注意的是,范围for迭代的范围必须是确定的。对于数组而言,就是数组第一个元素到最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。

void Func(int array[])
{
	for (auto x : array)
		cout << x << endl;
}

像这种情况就不能使用范围for,因为传参到函数中时传递的不是一整个数组而是数组指针,此时for的范围不确定。

十一、指针空值nullptr(C++11)

在过去,我们给一个没有指向的指针进行初始化的时候会使用NULL,而NULL实际上是一个宏。

我们在C语言中使用NULL没有问题,但是在C++中就会出现问题,为什么呢?

在传统的C头文件stddef.h中,可以看到如下代码:

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

可以看到,在C++中NULL被定义为0,这样会造成什么麻烦呢?

可以看到,就算传递的参数为NULL,程序还是会调用int类型的Func,而不是int*的Func,这违背了我们的目的。

因此出现了指针空值nullptr来填补这个bug,使用nullptr时不需要包含头文件,因为它是C++11作为新关键字引入的。为了提高代码的健壮性,我们后续表示指针空值时最好都使用nullptr。

有人会想,为什么不直接把这个bug修改了呢?因为语言有一个向前兼容的原则,也就是已经出现的东西即使有问题也不能修改,如果贸然去修改了可能会导致以前的代码无法运行,可能会造成巨大的损失。

到这里,我们就算对C++有了一个简单的了解了,下一篇的C++入门我们就会开始学习类和对象。

如果觉得本文对你有帮助就点个赞吧

完.

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

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

相关文章

DNS 域名系统——应用层

目录 1 域名系统 DNS 1.1 域名系统 1.2 互联网的域名结构 1.2.1 顶级域名 TLD(Top Level Domain) (1) 国家顶级域名 nTLD (2) 通用顶级域名 gTLD (3) 基础结构域名 (infrastructure domain) 1.3 域名服务器 1.3.1 域名服务器的四种类型 &#xff08;1…

bpmn.js一个基于Bpmn 2.0的前端工作流展示和绘制工具

bpmn.js是由开源工作流引擎camunda内部组织BPMN.IO组织开发的一款基于BPMN 2.0的工作流展示、编辑的web端工具库。由于工作流引擎activiti、flowable、camunda属于同宗分流&#xff0c;其工作流定义格式大致相同&#xff0c;所以我们可以使用bpmn.js完美融合其中任一工作流引擎…

使用 openpyxl 操作 Excel

由于单位有任务&#xff0c;需要按照名册制作多个工作表。手动复制和修改内容太费事了&#xff0c;所以使用python完成此项工作&#xff0c;为之后的此类工作提供一个通用脚本。 安装依赖库 pip install openpyxl lxml我们需要用到openpyxl。在官方文档中提到&#xff0c;如果…

SpringCloud-微服务概述、SpringCloud入门概述、服务提供与消费

1.学习前言 1.1 学习前提 熟练使用SpringBoot 微服务快速开发框架了解过Dubbo Zookeeper 分布式基础电脑配置内存不低于8G 1.2 文章大纲 Spring Cloud 五大组件 服务注册与发现——Netflix Eureka负载均衡&#xff1a; ​ 客户端负载均衡——Netflix Ribbon ​ 服务端负载…

前端JavaScript篇之对this对象的理解

目录 对this对象的理解1. 函数调用模式&#xff1a;2. 方法调用模式&#xff1a;3. 构造器调用模式&#xff1a;4. apply、call和bind调用模式&#xff1a; 对this对象的理解 在JavaScript中&#xff0c;this关键字是一个非常重要的概念&#xff0c;它用于指向当前执行上下文中…

【从Python基础到深度学习】2. Ubuntu及插件安装

本期所有软件安装包&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1UVEYm-12FivAnrE5NUXevg?pwdum60 一、安装Ubuntu 1.1 软件安装包&#xff1a;下载 VMware Workstation Pro | CN 一直点下一步即可 1.2 双击运行软件&#xff1a; 输入密钥 1 、VMware 15密钥 …

香港倾斜模型3DTiles数据漫游

谷歌地球全香港地区倾斜摄影数据&#xff0c;通过工具转换成3DTiles格式&#xff0c;将这份数据完美加载到三维数字地球Cesium上进行完美呈现&#xff0c;打造香港地区三维倾斜数据覆盖&#xff0c;完美呈现香港城市壮美以及维多利亚港繁荣景象。再由12.5米高分辨率地形数据&am…

双指针和单调栈

双指针 用于解决一类基于子段的统计问题 子段就是&#xff1a;数组中连续的一段 可以用一个闭区间来表示数组中的连续一段 这个方法核心就是优化&#xff1a;两种循环的枚举 也就是枚举左端点l和右端点r的所有可能优化关键就是&#xff1a;去除枚举中的冗余部分 具体优化策略…

eslint报错文档大量红色报错符号 不自动修正

确保eslint在工作 控制台大量报错信息 确保setting.json 开了保存的时候自动格式化代码 这个时候保存的时候代码可以自动被格式化 但是 文档中和控制台中仍然有大量的报错 信息 此时此刻说明 格式化文档的文件不是按照eslint 格式化的 可以网上找找现成可用的setting.json抄…

【linux温故】CFS调度

写在前面 网上关于CFS 调度器的文章多如牛毛&#xff0c;没必要自己写。很多文章写的都非常好。 很多文章里&#xff0c;关键的技术点&#xff0c;都是一样的&#xff0c;只是各个文章说法不一样。 掌握了核心的&#xff0c;关键的&#xff0c;其他的&#xff0c;如果工作中…

堆排序----C语言数据结构

目录 引言 堆排序的实现**堆的向下调整算法** 对排序的时间复杂度建堆的时间复杂度&#xff1a;排序过程的时间复杂度&#xff1a;总体时间复杂度&#xff1a; 引言 堆排序&#xff08;Heap Sort&#xff09;是一种基于比较的排序算法&#xff0c;利用堆的数据结构来实现。它的…

【数据结构与算法】力扣刷题记之 稀疏数组

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《数据结构与算法&#xff1a;初学者入门指南》&#x1f4d8;&am…

LLM应用开发与落地:流式响应

一、背景 最近智能客服产品给到一个游戏客户那边&#xff0c;客户那边的客服负责人体验后认为我们产品回答的准确率是还是比较高的。同时&#xff0c;他反馈了几个需要改进的地方&#xff0c;其中一个就是机器人回复慢。机器人回复慢有很多原因&#xff0c;也有优化方式&#…

接口测试常见问题

1.接口测试的流程 测试计划与方案 --> 接口用例设计 --> 接口测试执行 --> 缺陷报告与结果分析 2.接口工具的流程 脚本的设计&#xff0c;数据用例的设计&#xff0c;断言&#xff08;预期结果的设计&#xff09;&#xff0c;执行 3.测试计划与方案&#xff1a; …

如何手机搜大学生答案? #笔记#微信

今天我就分享几款搜题软件和搜题网站给大家&#xff0c;每一款都能轻松搜索题目&#xff0c;让大家快速找到精准的答案&#xff0c;有需要的小伙伴快点赞收藏起来&#xff0c;防止需要的时候找不到啦。 1.试题猪 这个是公众号 涵盖初大学&#xff0f;专升本&#xff0f;考研…

猫头虎分享已解决Bug || 缓存溢出解决方案:CacheOverflowException 或 CacheOutOfMemoryError

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

自然人如何代开发票

1&#xff1a;登录国家税务总局深圳市电子税务局 地址&#xff1a;国家税务总局深圳市电子税务局 2&#xff1a;个人所得税APP 扫描登录 或 身份证登录 3&#xff1a;选择 自然人代开增值税电子普通发票 4&#xff1a;申请代开 5&#xff1a;人脸识别 6&#xff1a;画框的…

那些 C语言指针 你不知道的小秘密 (4)

本篇会加入个人的所谓‘鱼式疯言’ ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 我会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人能…

C# 字体大小的相关问题

设置字体大小无法这么写&#xff0c; button1.Font.Size 20&#xff1b; 这个是只读属性&#xff1b; 把字体大小改为16&#xff0c; button2.Font new Font(button2.Font.Name, 16); 程序运行的时候先看一下窗体和控件的默认字体尺寸&#xff0c;都是9&#xff1b;然后点b…

【JAVA WEB】盒模型

目录 边框 内边距 基础写法 复合写法 外边距 基础写法 复合写法 块级元素的水平居中 弹性布局 设置行内元素的属性 &#xff0c;span 每一个HTML元素就相当于是一个矩形的“盒子” 这个盒子由以下这几个部分构成&#xff1a; 1.边框 border 2.内容 content 3.内边…