Fork me on GitHub

C++:菜鸟教程-AI工具、乱码问题、数据类型、定义和声明、进制、跨行等杂七杂八术语

本文对应菜鸟教程 AI编程助手 C++常量 的内容

 

安装AI工编程助手FItten Code,发现真牛逼啊,尤其那个注释生成代码,感觉我之前刷题就像在农耕时代,这样人不会变傻吗?用多了人容易傻掉

进而试了下感觉太崩溃了,世界观崩了,我把Oulipo的题目描述发给他,直接给我一个代码,提交后居然真的AC了,而且响应速度超级超级巨快啊,这也太逆天了吧。

以后面试不直接太多人作弊了吗?什么各种leetcode周赛,这太容易骗分骗名次了吧。

把昨天的codeforces A题复制发个他,真tm给了个一个代码,响应速度也太快了吧,1ms级别感觉是,而且还有讲解。

这也太容易搅乱刷题算法竞赛圈了啊。

这JB还用啥程序员啊

 

Q:VS和Vs Code区别

A:Vs是一个功能非常全面的开发工具集,提供强大的调试功能、集成数据库管理工具、支持UML建模等等。这些功能对于大型项目、企业级开发来说,可是非常有用的。

而VS Code是轻量级的源代码编辑器,虽然也能通过插件扩展一些功能,但在一些方面还是比不上VS的。如:VS Code的调试功能可能就不如VS那么强大,特别是在处理复杂项目和多线程调试的时候。

总的来说:

如果你正在开发大型、复杂的企业级项目,Visual Studio会是一个更好的选择;

如果你是一个个人开发者,或者正在进行小型项目的开发,追求开发效率和轻量体验,那么Visual Studio Code会是一个更理想的选择。

 

Question:

类和结构体有啥区别

百度技术大牛的GPT回答Answer:

访问权限:

  • 类(class)的成员默认是private(私有的),这意味着它们只能在类内部被访问。
  • 结构体(struct)的成员默认是public(公开的),这意味着它们可以在任何地方被访问。

你可以把类想象成一个“高级盒子”,这个盒子里的东西(成员)默认是藏起来的,别人看不到也摸不着(private)。如果你想让别人看到或摸到盒子里的某个东西,你得明确地说出来(把成员声明为public)。

而结构体呢,就像是一个“透明盒子”。盒子里的东西默认是大家都能看到的(public)。如果你想藏起某个东西,你得特意去声明(把成员声明为private)。

再来说说继承。如果一个“高级盒子”里又放了一个“高级盒子”(类继承类),那么里面的东西默认还是藏起来的,除非你特别说明。但如果是“透明盒子”里放了一个“盒子”(不管是高级的还是透明的,结构体继承结构体或类),那么里面的东西默认还是大家都能看到的。

至于用途嘛,类通常表示有复杂行为和属性的东西,比如人、动物之类的。

而结构体则更常用于表示一些简单的数据结构,比如点、线、面之类的,但这只是我们的一种习惯用法,并不是C++语言强制规定的。

Q:有点别扭,为什么1里放2,是1继承2,不是2继承1呢

A:

想象一下,你有一个工具箱(父类),里面放满了各种工具(成员)。现在,你想打造一个新的工具箱(子类),但这个新工具箱不想从零开始,而是想基于现有的工具箱进行扩展和定制。于是,你把旧工具箱里的工具都拿出来,放到新工具箱里,然后再添加一些新的工具或改进一些现有的工具。

在这个过程中,新工具箱(子类)继承了旧工具箱(父类)的所有工具(成员),并且还可以添加或改进这些工具。但反过来想,旧工具箱(父类)可不会去继承新工具箱(子类)里的任何东西,因为新工具箱是在旧工具箱的基础上创建的,而不是相反。

所以,回到你的问题上,“高级盒子1里放了一个高级盒子2”被解释为高级盒子1(子类)继承了高级盒子2(父类)的特性和行为,是因为子类是在父类的基础上进行扩展和定制的。子类可以获取父类的所有public和protected成员,并可以添加或重写这些成员来实现自己的特定行为。

Q:类和结构体访问权限不同有什么必要性吗?就用结构体不行吗

A:暂时理解不了,学多态的时候再看看

 1 这个问题问得好!在C++中,类和结构体访问权限的不同设计确实有其必要性,这主要体现在封装、隐藏内部实现和保护数据等方面。
 2 
 3 首先,封装是面向对象编程的一个核心概念。它允许我们将数据和操作数据的函数组合在一起,形成一个独立的实体。通过封装,我们可以隐藏对象的内部实现细节,只暴露必要的接口给外部使用。这样不仅可以提高代码的安全性,还可以减少外部代码对内部实现的依赖,从而降低维护成本。
 4 
 5 在C++中,类提供了很好的封装机制。类的成员默认是私有的(private),这意味着它们只能在类内部被访问。如果你想让外部代码访问某个成员,你需要显式地将其声明为公有的(public)。这种设计有助于我们更好地控制对数据的访问,从而保护数据的完整性和安全性。
 6 
 7 相比之下,结构体的成员默认是公有的(public)。虽然结构体也可以用于封装数据和函数,但由于其成员默认是公开的,因此它提供的封装性相对较弱。这可能会导致外部代码能够直接访问和修改结构体的内部状态,从而破坏数据的完整性和安全性。
 8 
 9 当然,你可能会说:“那我用结构体的时候,都显式地把成员声明为私有的不就行了?”确实,这样做可以在一定程度上模拟类的行为。但是,结构体缺乏类所提供的一些更高级的功能和灵活性。特别是当涉及到继承和多态时,类的优势就更加明显了。
10 
11 继承允许我们基于现有的类创建新的类,并继承其属性和方法。通过继承,我们可以实现代码的重用和扩展。而多态则允许我们使用统一的接口来调用不同实现的方法。这有助于我们编写更加灵活和可扩展的代码。
12 
13 综上所述,虽然结构体在某些情况下可以替代类使用,但类提供了更高级的功能和灵活性。特别是当涉及到封装、继承和多态时,类通常是更好的选择。
View Code

 

注释:

Q:

#if 0 ... #endif 是啥
百度技术大牛文心GPTAnswer: 

这个问题嘛,其实挺简单的!#if 0 ... #endif是C++中的预处理指令,它的作用是在编译时把包含在其中的代码段排除掉,也就是说,这些代码不会被编译器编译成可执行代码。

这通常用于临时禁用一段代码,或者在某些条件下才编译特定的代码段。比如,你可能在调试的时候想禁用某个功能,但又不想完全删除那段代码,这时候就可以用#if 0把它包起来。等调试完了,再把#if 0改成#if 1,这样代码就又可以被编译了。

举个例子:

#if 0 // 这段代码不会被编译
std::cout << "Hello, I'm not compiled!" << std::endl;
#endif
int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

 

 

 

中文乱码:

纯纯死妈玩意

真难受,一个乱码,全网都找不到对的,一下午就搞这个问题了

一直没解决,VS里ctrl+f5没问题,cygwin里就乱码,且是输出cpp文件就乱码,本身诸如ls -la的中文就正常

打算看看windows的cmd里输出是啥情况,结果也是乱码,证明不是cygwin的问题

又用codeblock新建了个cpp,fn+f9可以正常输出中文,cmd和cygwin里也正常。        

检查cmd和codeblocks的编码格式都是windows936,即GBK

936代表简体中文GBK编码,65001代表UTF-8编码,

在简体中文环境下使用936编码时,命令提示符可以正确显示中文字符;

而在UTF-8编码下,无论是显示还是输入中文字符都可以正常进行。

此外,执行一些文件操作时,如文件名含有中文字符,若使用936编码有可能会出现乱码现象,而使用UTF-8编码则几乎不会出现该问题。

但是对于codeblock的cpp文件,cygwin里设置成UTF-8没问题,GBK就乱码。

VS2022查看编码格式:

VS2022没有高级保护”,改成了“另存为”,右下角保存里可以看编码格式

 

尝试很多都不行,用户home下:C:\cygwin64\home\GerJCS岛下的.bash、.bashrc、,inputrc,都翻来覆去改了个遍,看说windows可能不认zh_CN.UTF-8的这种写法,加不加双引号,大写小写,试了十几遍

cygwin的右键option也更改了文本下的字体和本地Locale字符集,各种组合又试了十几遍都不行,VS的AI又说按照sudo命令包,尝试安装也没整懂感觉走入误区了,又去倒腾说代码改成这样

 1 #include <iostream>
 2 #include <locale>
 3 using namespace std;
 4 
 5 int main() {
 6     // 设置本地化环境为简体中文
 7     locale loc("chs");
 8     cout.imbue(loc);
 9 
10     cout << "第三方" << endl;
11     return 0;
12 }
View Code

但ctrl+f5可以,可是cygwin里exe文件报错,无语崩溃死

VS的AI又说试试更改时区:

控制面板选择类别,进入时钟区域,管理,把那个beta勾上重启就好了, 

cygwin是

但codeblock的fn+f9又不行了,过了一会又莫名其妙的好了。

但cmd和cygwin执行都不行

VS自己ctrl+f5也乱码

然后自己改乱了,由于没-o指定,所以改完总是去执行./main,但其实应该是./a

最后codeblock也好了,setting里的Editor,第一个,Encoding setting选择utf-8

发现codeblock无论怎么改,只会影响自己fn+f9的是否乱码,而cmd和cygwin里始终是对的,而且codeblock里改成错的即时生效,但改成正确的需要重启生效,但codeblock的控制台的目录又乱码了。真玄学,把时区改回去吧,codeblock好了。cmd和cygwin也都可以。

VS里Ctrl+f5第一次不行,我修改几个其他汉字又行了,难道没变化是不编译吗??好诡异啊这软件

现在又回到了最开始的问题,cygwin和cmd乱码,VS里面ctrl+f5正常

最后吃饭的时候无奈刷了下手机用Chrome查了下,第一个文章就解决了我的问题,搜索完整关键词是“cygwin执行g++VS里cpp文件输出中文乱码”

直接

g++ -finput-charset=UTF-8 -fexec-charset=GBK your_program.cpp -o your_program.exe

因为windows是GBK,而VS是UTF-8。

完美解决。

最后总结下:

win10系统

cmd编码格式:属性石936GBK,其实也没法改

cygwin编码格式啥都行,我是zh_CN utf-8 consolas

codeblock的setting的Editor,Encoding setting是WINDOWS 936(只能管自己IDE里面的事),fn+f9正常。cmd运行exe文件正常。cygwin运行exe正常

VS编码格式默认UTF-8 65001(只能管自己IDE里的),ctrl+f5正常,cmd执行用上面的语句正常,cygwin里用上面的语句执行正常

注意:搞过的再输入直接就是正常的,如果其他cpp每次都需要这句话:g++ -finput-charset=UTF-8 -fexec-charset=GBK your_program.cpp -o your_program.exe,并不是默认永远都是UTF-8读取,GBK输出了

在测试发现:只需要-fexec-charset=GBK 也可以。但如果将cygwin里改成字符集是GBK就不行不许fexec弄

原理:

  1. 编译时选项 vs 运行时终端设置:

    • -fexec-charset=GBK:这是编译器选项,影响编译器如何在生成的目标文件中存储字符串字面量的编码格式。
    • 命令提示符设置:这是运行时终端的设置,影响命令提示符如何处理和显示程序的输出字符。
  2. 具体区别:

    • -fexec-charset=GBK
      • 编译阶段:编译器将 UTF-8 编码的字符串转换为 GBK 编码,存储在目标文件中。
      • 运行阶段:程序输出 GBK 编码的字符串,需要 GBK 编码的终端来正确显示。
    • 命令提示符设置:
      • 终端阶段:命令提示符以 GBK 编码显示程序的输出字符。
      • 输入阶段:命令提示符以 GBK 编码处理输入的字符。

我的理解:-fexec-charset=GBK 是生成目标文件的编码,一般读不会出错,都是utf-8,就是生成目标文件的时候会弄错,所以需要GBK说明一下,比如utf-8方式读取的888对应GBK编码就是999,那目标文件如果按照utf-8来搞,最后就会弄错,按照GBK来搞就没事。

而cmd或者cygwin里设置的显示字符集的编码格式感觉也没啥用,只是显示问题,但显示好像都兼容,目标文件才是关键。不打算研究太深,强行解释的通自圆其说就这么滴吧。

 

标识符就是变量名

enum WeekDay {//WeekDay是枚举名
    Monday,//枚举成员
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
};
WeekDay today = Monday;//声明WeekDay类型的变量,today是存储枚举类型值的变量

today就是一个枚举变量,它的类型是WeekDay,存储的值是Monday

开始用豆包

ASC:

7位编码,2^7=128种不同的状态,足够用来编码所有的英文字母(大写和小写)、数字(0 - 9)、标点符号和一些常用的控制字符。

  • 在计算机存储和传输过程中,ASCII 码通常会占用一个字节(8 位)。最左边(高位)的一位常常被用作奇偶校验位(在早期通信和数据存储中用于简单的错误检测)或者直接设置为 0。例如,字符‘A’存储时可能是 01000001(高位为 0),这样使用 8 位字节存储主要是为了方便计算机存储系统的组织,因为计算机存储单元通常是以字节为基本单位的。
  •  “A” 的十进制编码值是 65。二进制表示是 01000001,十六进制是 0x41。在计算机存储和处理文本信息时,当遇到编码值为 65(或其对应的二进制、十六进制等形式)的字符数据时,就会将其识别为 “A”。

 

Unicode 字符集:

最熟知的就是UTF-8,最早期电脑只有英文拉丁文的字符,即ASC,后来有支持中文字符的GB2313,支持日文的某编码但都互不兼容,于是有了Unicode,代表就是UTF-8

  1. 定义
    • Unicode 是一个字符集标准,它为世界上所有的字符都分配了一个唯一的数字编号,这个编号称为码点(Code Point)。例如,英文字母 “A” 在 Unicode 中的码点是 U+0041,汉字 “中” 的码点是 U+4E2D。它的目的是实现全球各种文字符号在计算机中的统一编码和表示,涵盖了各种语言的字母、数字、标点符号,还包括许多特殊字符和符号,如表情符号、数学符号、货币符号等。
  2. 发展历程
    • Unicode 的发展是为了解决不同字符编码系统之间的混乱局面。在早期,计算机主要使用 ASCII 码(美国信息交换标准代码),它是一种 7 位编码,可以表示 128 个字符,主要用于英语和一些基本的控制字符。但随着计算机在全球的普及,不同国家和语言对字符编码的需求增加,出现了许多如 GB2312(中文编码)、Shift - JIS(日文编码)等本地化的编码标准。这些编码之间互不兼容,导致在跨语言文本处理等场景中出现了很多问题。Unicode 应运而生,它从 1991 年开始发布,经过不断的更新和扩展,如今已经成为全球通用的字符集标准。
  3. 编码方式
    • UTF - 8:这是一种变长编码方式,是目前互联网上使用最广泛的 Unicode 编码形式。它可以使用 1 到 4 个字节来表示一个 Unicode 码点。对于 ASCII 码中的字符(码点范围是 U+0000 - U+007F),UTF - 8 只用 1 个字节表示,和 ASCII 码完全兼容。例如,字符 “A”(U+0041)在 UTF - 8 中仍然表示为 0x41。对于其他一些常用字符,如欧洲语言中的一些带重音符号的字母,通常用 2 个字节表示;对于更复杂的字符,如中文、日文等东亚语言的字符,一般用 3 个字节表示;而一些非常用的特殊字符可能会用到 4 个字节。
    • UTF - 16:这是一种定长或变长的编码方式。在 UTF - 16 中,大部分常用字符用 2 个字节表示,但对于一些非常用的辅助平面(Surrogate Plane)中的字符,需要用 4 个字节表示。UTF - 16 在处理一些文本处理软件内部的字符存储等场景中有一定的应用。
    • UTF - 32:这是一种定长编码方式,每个 Unicode 码点都用 4 个字节表示。这种编码方式简单直接,但会占用较多的存储空间,在实际应用中相对较少使用,不过在一些对字符编码处理要求严格按照固定长度的系统中可能会用到。
  4. 应用场景
    • 操作系统:现代操作系统如 Windows、macOS 和 Linux 等都广泛支持 Unicode。这使得用户可以在操作系统中同时使用多种语言的软件,查看和编辑不同语言的文档。例如,在 Windows 操作系统中,用户可以在同一台电脑上安装中文、英文、法文等多种语言的软件,并且系统能够正确地显示这些软件的界面文字。
    • 网页开发:网页中大量使用 Unicode 编码。HTML5 默认使用 UTF - 8 编码来处理文本内容,这使得网页能够正确地显示各种语言的文字。在网页的 HTML 代码中,可以通过<meta charset="UTF - 8">这样的标签来指定网页的字符集为 UTF - 8。这样,当网页包含中文、日文、阿拉伯文等多种语言的内容时,浏览器能够正确地解析和显示这些内容。
    • 编程语言:几乎所有的现代编程语言都支持 Unicode。

 

宽字符:

  1. 定义
    • 宽字符是一种字符编码类型。在计算机中,为了表示世界上各种语言的字符,需要对字符进行编码。普通字符(窄字符)通常占 1 个字节(8 位),而宽字符一般占 2 个字节(16 位)或更多字节,用于表示那些不能用单字节编码的字符,如 Unicode 字符集中的大量字符。
  2. 用途
    • 国际化支持:许多语言包含了 ASCII 码之外的字符,像中文、日文、韩文等。例如,汉字的数量众多,单字节编码无法满足其表示需求。宽字符编码(如 UTF - 16)可以很好地处理这些复杂语言的字符表示,使得软件能够方便地进行国际化和本地化。如果一个程序想要在不同国家和语言环境下正确显示文本,采用宽字符编码是很有必要的。
    • 统一字符表示:宽字符编码能够提供一个统一的字符表示方式。例如,Unicode 是一种广泛使用的宽字符编码标准,它为世界上几乎所有的字符都分配了一个唯一的编号,这样不管是在 Windows、Linux 还是其他操作系统中,只要支持 Unicode,就能够正确地处理和显示各种字符。
  3. 示例
    • 在 C/C++ 语言中,当使用宽字符时,字符类型为wchar_t。例如,定义一个宽字符数组来存储一个中文字符 “好”:
     
    #include <stdio.h>
    #include <wchar.h>
    int main()
    {
        wchar_t wide_char[] = L"好";
        wprintf(L"%ls", wide_char);
        return 0;
    }
    

    • 在这个例子中,L前缀表示这是一个宽字符常量。wchar_t类型用于存储宽字符,wprintf函数用于输出宽字符。
  4. 与窄字符对比
    • 存储容量:窄字符一般只能表示 256 种不同的字符(因为 1 字节有 8 位,能表示种状态),这对于英语等基于拉丁字母的语言可能足够,但对于其他语言远远不够。宽字符可以表示更多的字符,如 UTF - 16 可以表示2^16=65536种不同的字符,UTF - 32 更是可以表示2^32种种不同的字符。
    • 处理效率:窄字符在处理简单文本(如纯英文文本)时,由于占用空间小,处理速度可能会更快。但宽字符在处理包含多种语言字符的复杂文本时更具优势,因为它能够完整地表示所有需要的字符,不过可能会占用更多的内存和处理资源。
 
 
 
关于变量的声明、定义、初始化:
上一个博客说过函数的声明和定义(剧本)
 
在 C 和 C++ 中,int a;既是声明也是定义。
  1. 从定义角度来看
    • 当你写int a;时,编译器会为变量a分配内存空间。具体来说,它会根据a的类型(int类型)在内存中预留足够的空间来存储一个整数。在大多数常见的系统和编译器环境下,int类型通常占用 4 个字节的内存空间。这个过程就是定义的过程,因为它为变量分配了实际的存储空间。
  2. 从声明角度来看
    • 同时,int a;也向编译器声明了一个名为a的变量,其类型是int。这符合声明的特征,即告诉编译器变量的类型和名称。在简单的单文件程序中,这种双重性质(既是定义又是声明)可能不太容易区分其区别。

但在多文件程序环境下,这种区别就比较明显了。如果在一个文件中已经定义了int a;,在另一个文件中想要使用这个变量,就需要通过extern int a;来进行声明,这里的extern int a;只是声明,不会再为变量a分配内存空间。

  1. 定义(Definition)
    • 变量定义
      • 含义:变量定义是为变量分配存储空间的过程,同时也可以指定变量的类型。在定义变量时,编译器会根据变量的类型为其预留相应大小的内存空间。例如,在 C/C++ 中,int a;就是一个变量定义语句,它为一个整数类型的变量a分配了足够存储一个整数的内存空间(通常是 4 个字节,具体取决于编译器和系统)。
      • 特点:一个变量只能被定义一次(在一个程序的编译单元内),因为多次定义会导致编译器为同一个变量分配多个存储空间,这会引起冲突。定义变量的同时可以进行初始化,但不是必须的。
    • 函数定义
      • 含义:函数定义包括函数头(函数返回类型、函数名和参数列表)和函数体。函数体包含了实现函数功能的语句。例如,在 C/C++ 中,int add(int a, int b) { return a + b; }就是一个函数定义,它指定了函数add的返回类型是整数,有两个整数类型的参数ab,并且在函数体中实现了将两个参数相加并返回结果的功能。
      • 特点:函数定义只能有一个(对于同名函数在一个程序的编译单元内),因为函数定义包含了函数功能的具体实现,多个相同函数定义会导致编译错误(函数重载除外,在 C++ 中有函数重载的概念,它允许在同一个作用域内有多个同名函数,但是参数列表不同)。
  2. 声明(Declaration)
    • 变量声明
      • 含义:变量声明是告诉编译器变量的类型和名字,但不分配存储空间。它主要用于在使用变量之前,让编译器知道这个变量的存在。例如,在一个多文件的 C/C++ 项目中,在file1.c中有一个全局变量int global_var;(这是定义),在file2.c中如果要使用这个变量,就需要先声明extern int global_var;。这个声明告诉编译器global_var是一个外部定义的整数类型变量。
      • 特点:一个变量可以被多次声明,只要声明是一致的。声明主要用于跨文件引用变量或者在变量使用之前提前告知编译器,避免编译错误。
    • 函数声明
      • 含义:函数声明是告诉编译器函数的返回类型、函数名和参数列表,让编译器在编译调用该函数的代码时能够进行类型检查等操作。例如,在 C/C++ 的头文件中可以有int add(int a, int b);这样的函数声明。它表示有一个名为add的函数,返回类型是整数,有两个整数类型的参数ab
      • 特点:函数可以被多次声明,并且在不同的文件中通过包含头文件来共享声明。函数声明使得编译器能够在调用函数的代码处检查参数类型是否正确等信息,即使函数的具体定义在其他文件中。
  3. 初始化(Initialization)
    • 变量初始化
      • 含义:变量初始化是在定义变量时或者在之后的某个时刻,为变量赋初始值的过程。例如,在 C/C++ 中,int a = 5;是在定义变量a的同时进行初始化,将a的值初始化为 5。也可以在定义之后初始化,如int b; b = 3;
      • 特点:初始化可以确保变量在使用时有一个确定的值,避免使用未初始化变量带来的错误(在很多编程语言中,使用未初始化的变量会导致不可预测的结果)。对于一些编程语言,如 C++,还支持对象的初始化,通过构造函数来完成复杂对象的初始状态设置。
    • 函数初始化(在某种特定语境下)
      • 在一些面向对象的编程语言中,对象的方法(函数)可以在对象创建时通过构造函数进行初始化相关的操作。例如,在 Java 中,一个类的构造函数可以初始化对象的属性,这些属性可能会被其他方法(函数)所使用。public class MyClass {private int value; public MyClass() { value = 10; } }在这个例子中,构造函数MyClass()初始化了属性value为 10。
 
这些琐碎的狗屁术语是真JB烦啊艹!!!!!

 

变量多次被声明,但不可以多次被定义:

在单个文件内多次声明变量, 源文件example.cpp,其中定义了一个全局变量int global_variable;(这是定义)。

// 全局变量定义
int global_variable;

// 函数1,在函数内部声明全局变量
void function1() {
    extern int global_variable;
    global_variable = 10;
    // 在这里,通过在函数内部声明全局变量,
    // 告知编译器这个变量是在外部定义的,然后使用它
}

// 函数2,同样在函数内部声明全局变量
void function2() {
    extern int global_variable;
    int local_variable = global_variable + 5;
    // 再次声明全局变量,并基于它进行计算
}
在这个例子中,global_variable在文件开头进行了定义,然后在function1function2这两个函数内部又分别进行了声明。这样的声明是合法的,因为它只是告诉编译器这个变量是在外部定义的,并且每次声明的类型(int)和名称(global_variable)都与定义保持一致。

而如果

int a;
int a;

就不行,这个叫做变量多次被定义 

  • 变量定义主要是为变量分配内存空间并确定其类型。例如,当你写int a;时,编译器会根据int类型的大小(通常在 32 位系统下是 4 字节)为变量a预留内存空间。这个空间用于存储a的值。
  • 还可以在定义变量的同时进行初始化。例如int b = 10;,这里不仅为变量b分配了内存,还将初始值 10 存储到了为b分配的内存空间中。

 

变量声明的主要目的是告诉编译器变量的类型和名称,但不分配内存空间。

总结:

 int a; 这叫变量的定义,分配内存,只能一次

 int a = 10; 这叫变量的定义+初始化,分配内存

 extern int global_variable; 这叫变量的声明,不分配内存,可以多次

 

关于函数的声明和定义:

函数头

在 C++ 里,格式为 “ 返回值类型 函数名 (参数列表);”。比如int add(int a, int b);int是返回值类型,add是函数名,(int a, int b)是参数列表。

函数体

跟在函数头后,用{}括起来,里面是实现功能的代码语句。例如:
int add(int a, int b) {
    return a + b;
}
{}return a + b;就是函数体内容
函数的定义:函数头+函数体;,即剧本。只能定义一次。
函数的声明:函授头; 即打招呼。可以多次声明,
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
void vvv();//声明
void vvv();//声明
void vvv()//定义
{
    cout<<"E"<<endl;
}
int main()
{
    vvv();
}

这个是合法的,有点类似变量的定义,但函数这玩意叫声明。

在一个cpp文件里也是有意义的

// 第一次声明add函数
int add(int a, int b);
int main() {
    int result = add(3, 4);
    return 0;
}
// 第二次声明add函数
int add(int a, int b);
  • 这里两次声明的意义在于,假设这个文件很长,中间可能经过了很多其他代码的编写或者修改。后面再次声明add函数可以提醒程序员函数的参数和返回类型要求,确保后续对add函数的调用(如果有的话)符合第一次声明的规范。

具体说下多文件:

  1. 函数可以多次声明
    • 在 C++ 以及许多编程语言中,函数可以被多次声明。函数声明的主要目的是告诉编译器函数的返回类型、函数名和参数列表等基本信息。这样,在调用该函数的代码处,编译器能够进行类型检查等操作。
    • 例如,假设有一个项目包含多个源文件(.cpp文件)和头文件(.h文件)。在一个头文件math_functions.h中可以声明函数int add(int a, int b);。然后,在多个源文件(如main.cpputilities.cpp)中可以通过#include "math_functions.h"来包含这个头文件,这就相当于在每个源文件中都进行了一次函数声明。
  2. 声明一致性要求
    • 虽然函数可以多次声明,但这些声明必须是一致的。也就是说,每次声明的返回类型、函数名和参数列表都要相同。
    • 例如,如果在一个头文件中声明int add(int a, int b);,在另一个地方不能声明为double add(int a, int b);或者int add(double a, double b);等不一致的形式。否则,编译器会产生错误,因为它会对函数的类型信息感到困惑。
  3. 头文件的作用与函数声明
    • 头文件是函数声明的常用载体。在一个大型项目中,通常会将函数声明放在头文件中,然后在需要调用这些函数的源文件中包含相应的头文件。这样可以方便地实现代码的模块化和共享。
    • 例如,对于一个图形库,可能有一个graphics.h头文件,其中声明了各种图形绘制函数,如void drawCircle(int x, int y, int radius);void drawRectangle(int x, int y, int width, int height);等。不同的源文件(如main.cpp用于主程序逻辑,shapes.cpp用于实现具体形状绘制逻辑)可以通过包含graphics.h头文件来共享这些函数声明,从而在各自的文件中合法地调用这些函数,而函数的具体定义可以放在其他的源文件或者库文件中

 

关于声明和定义,菜鸟教程里搜“ C++ 中的变量声明 ” ,解释的相当棒感觉。

 ~~~~(>_<)~~~~好挣扎,这些琐碎知识点╮(╯▽╰)╭,好想继续去刷算法题,但感觉阅读一些相关的东西更清晰了

哎,我tm如果先学的是这些破玩意真容易被劝退,好恶心啊这些基础。还好先刷算法题建立了自信。之前博客说的第一章魔咒劝退大招

虽然安了VS,但还是一直用codeblock顺手快速,必须用VS的时候才去用。

 

豆包解释的很棒

文件中static了个东西,只能在这个文件里用

类里static了个变量,叫做静态成员变量,其他文件只要能正确包涵这个类的定义,就可以用里面的定义

作用域解析运算符 ::

把变量作用域里的

#include <iostream>

class MyClass {
public:
    static int class_var;  // 类作用域变量
};

int MyClass::class_var = 30;

int main() {
    std::cout << "类变量: " << MyClass::class_var << std::endl;
    return 0;
}
View Code

发给豆包解释,

有点理解类和结构体区别了,结构体其实比较单一,类可以有封装有选择性的访问,且结构体没有继承并做修改的这种功能

访问结构体的成员变量:结构体名.成员变量

访问类的      :类名::成员变量

但对于protect不太懂,知道public和private了,多态也不太懂,继承也有点懵

这样学的好处就是没开始讲我就通过AI解读代码,提前了解到了很多还没讲的东西,但追问之后还是不懂,先了解下随着时间推移,真正看菜鸟教程那对应继承讲解的时候做铺垫

 

0xFee:14*16^0+14*16^1+15*16^2(不区分大小写)

0b二进制

0八进制

没前缀十进制

0x十六

 

u无法符号,l长整型

 

浮点常量:(感觉菜鸟教程写的一坨屎,仿佛是屁股写的)

由 整数+小数点+小数+指数 组成

怎么表示?

小数表示,

即小数部分+整数部分:

3.14159

指数表示

即小数点+指数

314159E-5L,输出3.14159

其中e或E表示乘以十的多少次方,

 

 cout<<"\a"<<endl; 

这个太有趣了哈哈

 

 cout<<"#\ #"<<endl; 会警告,想表示一个普通的空格,直接敲一个空格就可以了,而不是使用转义序列形式

空格的 ASCII 码是 32,八进制表示就是 \040 

 

豆包说:反斜杠后应该加符合转义字符规则的字符,单独一个反斜杠后面接回车不符合语法要求

但实际测试可以

 

代码 cout<<"#\\ #"<<endl; 输出 #\ # 

 

关于跨行:

错误的跨行:str、str1、str2都是错的

#include <iostream>
using namespace std;

int main() {
    string str = "This is a long string
                   that should be written in multiple lines";  // 这种写法错误,换行后没有正确续接字符串
  string str1 = "A
  ";
  string str2 = "A
";
  cout << str << endl;
  cout << str1 << endl;
  cout << str2 << endl;
  return 0; 
}

 

正确的跨行,输出ABC,注意这段代码里可能包涵shift+空格的全角空格,所以会报错\241,删掉重新敲回车即可,详情见下面的惊天细节

#include <iostream>
using namespace std;

int main() {
    string str = "A"
                 "B"

          "C
"; // 这样每行的字符串内容会自动拼接成一个完整字符串 cout << str << endl; return 0; }

 

正确的跨行

  • 在代码行末尾(续行情况):
    在 C++ 里,当一行代码太长需要换行书写的时候,可以使用反斜杠 “\” 放在行尾,然后回车在下一行接着写代码,编译器会把这两行当作一行来处理。例如: 
    string str = "This is a long string\
that should be written in multiple lines";

输出 This is a long stringthat should be written in multiple lines 

 

正确的跨行,即续行

string s="s\
    s";

输出 s s

 

正确的跨行

string s="s\
s";

输出ss

 

但注意:两个 '\' 就不行,

 string str = "A\\
    ";

会输出A,但会有警告 '\040' ,040是空格的八进制,像这种,要么表示续行,单独一个 '\' 后啥也别加,直接加内容

string str = "A\
内容";

输出 “A内容”

要么想表示输出单个 '\' ,你就把回车去掉

string str = "A\\内容";

输出 “A\内容”

 

 

发现惊天细节

我用的是掌心输入法,shift+空格,切换全角,按空格会占两个字节的宽度,或者是两个空格的宽度,导致全角状态下的空格,会报错 error: stray '\241' in program ,转义字符要么\n\t这些,要么八进制数字范围是 “\000” 到 “\377”。而241虽然在范围内,但不符合预期,

 

如果字符串双引号内是完整的在一行,那怎么敲回车都没影响了

 

正确引号的输出

cout << "He said, \"Hello!\"" << endl;

输出 He said, "Hello!" 

 

错误的输出

string str ="s\"          //错在了编译器会将第一个双引号后的反斜杠(\)和紧接着的双引号视为一个转义序列,即在 C++ 中,\"用于在字符串中表示双引号字符本身,下一行的s就没正确衔接
s";
string str ="s\"      //错在下一行的第一个",就代表str输入结束的,但这后面又有个s,是错误点一。且又是"另起一行没衔接,是错误点二
"s";

正确写法

  • string str ="s""s";这个语句中,C++ 编译器会自动将相邻的字符串字面量拼接在一起。这是 C++ 语言的一个特性,它允许你把一个长字符串拆分成多个较短的字符串字面量,在编译时它们会被组合成一个完整的字符串。
  • 所以,在这种情况下,编译器会将 "s" 和 "s" 拼接成一个完整的字符串 "ss" 并赋值给str,这是完全符合语法规则的。

 

 

关于const

  • 这表示MAX_VALUE是一个常量,其值不能被修改。如果在程序中试图对MAX_VALUE重新赋值,如MAX_VALUE = 200;,编译器会报错。这有助于提高程序的安全性和可维护性,因为可以避免不小心修改了不应该被修改的值

跟#define区别,豆包回答:

    1. 预处理阶段与编译阶段的区别(C/C++)
      • #define
        • #define是一个预处理指令,在预处理阶段就进行文本替换。例如,#define MAX_VALUE 100,在预处理阶段,程序中所有出现MAX_VALUE的地方都会被直接替换成100。这种替换是简单的文本替换,没有类型检查
      • const
        • const是在编译阶段处理的。它是一个真正的语言特性,有类型检查。编译器会检查const变量的类型是否正确使用。
        • 例如,如果有const int num = 100;,试图把一个非整数类型的值赋给num(如num = 3.14;),编译器会报错。
    2. 作用域的区别
      • #define
        • #define定义的常量没有严格的作用域限制。它从定义的地方开始,到文件末尾有效(除非被#undef取消定义)。在多个文件中,如果一个#define在头文件中定义,并且头文件被多个源文件包含,那么这个#define在所有包含它的源文件中的效果是一样的。
        • 例如,在一个头文件header.h中定义#define DEBUG_MODE 1,然后在source1.cppsource2.cpp中都包含了header.h,那么DEBUG_MODE在这两个文件中都被定义为1
      • const
        • const变量有明确的作用域,和普通变量一样。它可以是局部变量、全局变量或者是类的成员变量等。
    3. 内存分配的区别(C/C++)
      • #define
        • #define定义的常量在预处理阶段就被替换掉了,不会占用内存空间。因为它只是简单的文本替换,不是真正的变量。
      • const
        • const变量通常会占用内存空间。对于全局const变量或者静态const变量,它们会被分配到只读数据段。对于局部const变量,会在栈上分配内存(和普通局部变量类似)。不过,有些编译器可能会对const变量进行优化,比如对于一些简单的const变量(如const int num = 10;),编译器可能会直接将使用num的地方替换为10,而不实际分配内存来存储num,但这是编译器的优化行为。
    4. 可调试性的区别
      • #define
        • 由于#define是预处理指令,在调试时可能会带来一些麻烦。调试器通常看不到#define定义的常量,因为在调试阶段看到的是已经替换后的代码。
      • const
        • const变量可以被调试器识别。在调试过程中,可以查看const变量的值,这对于查找程序中的错误非常有帮助。例如,在调试一个函数时,可以检查传入的const参数的值是否符合预期。

 

备注:

###:贴吧ZXP4大佬回复说,官方题解tutorial

###:关于菜鸟教程差评(逐渐读发现翻译真的差劲,之前阅读的时候还一度怀疑自己)推荐浙大翁恺老师、郝斌老师等视频课程优达学城

###:新发现个GPT:offer收割机 (优点:可以回答C++不相关的问题,之前技术大牛GPT不行。缺点是问完问题后如果我不理解想再继续问,没法像技术大牛文心GPT那样很好的衔接或者说记住我之前的问题)。但两个都不如FittenCode Chat(有时候也挺智障的),切换页面也可以回答,而百度的GPT必须把GPT放在最前面不能浏览其他页面,有时候手机查Chrome也会有一些意想不到的解决方案。(更新:现在VS的FittenCode Chat好慢 )

###:之前IntelliCode的设置

###:L雪T

###: 昨天贴吧有人说豆包,之前查FittenCode Chat的时候看到过,北邮贴吧提到过,昨天codeforces贴吧有人回复说豆包翻译不错,试试豆包网页版,文心GPT受不了太恶心了,没停止按钮了。豆包速度快解答还棒

###:看视频没法自己控制,只能按部就班,而看文字到处自己摸索,前期很慢,但提高了学习能力,且后期会很快可以自己控制学习速度,跟着视频学东西就是垃圾,始终很慢且没啥搜索找资料的能力,也没啥学习能力

###:菜鸟教程就好像二手市场淘来的一个全是问题但车里有导航地图的破车,开1km就出点毛病,但由于有导航知道目的地,能带你去目的地,路上遇到的一切问题都得你自己解决

###:贴吧又有人回复我说deepl翻译不错

posted @ 2024-12-31 02:12  GerJCS  阅读(84)  评论(0)    收藏  举报