【C++从入门到入门】C++基础:.cpp 基本结构,基本数据类型,引用,指针,函数

C++基础笔记

一、HelloWord.cpp 代码含义

c++中,用于编写代码的文件为 .cpp 文件(类似于.java文件)


// 这句话是预处理的操作,在源代码被编译前,替换文件
// 相当于 Java 种的 import,导入了一个java文件
//  中定义了 C++的输入与输出(相当于IO流)
#include 

// using 是一个编译指令,namespace 用来指定命名空间
// 此命名空间与 Vue 的类似,也相当于 Java 的包名(浅显理解)
using namespace std;

// C++ 的程序必须含有一个 main 函数作为入口
int main() {

        int age = 18;

        // 这里是声明了一个引用 ref,并且将 age 与 ref 绑定起来
        int& ref = age;

        ref += 20;

        // cout 是输出流,cin 是输入流
        // 如果上面么有使用 std 命名空间
        // 这里就可以书写成: std::cout 表示我要使用std里的cout函数
        // << 表示将值传给谁,箭头指向表示值的流向
        // 使用 cin 就是 cin >> ref
        // 上述含义: 接收一个命令行输入的值,赋值给 ref 变量
        cout << ref;
}       

二、基本数据类型

c++中的基本数据类型,有算术类型和空类型

算数类型分为整型和浮点型。

整型

算术类型 字节位 备注
short 至少16位
int 至少与short一样大
long 至少32位 需 ≥int
long long 至少64位 需 ≥long
char 8位
bool 未定义 就是Boolean类型

浮点型

浮点型 字节位 备注
float 至少32位
double ≥48 至少 ≥float
long double ≥double

无符号类型:类型前面加上 unsigned 即可表示无符号类型,无符号类型表示的最大值是有符号类型的两倍。(假设同样的内存空间,有符号类型可表示 -10 到 10。那么不带符号,也就不用表示负数,表示负数的空间就可以用来表示更大的整数,0-20)

char:默认既不是有符号,也不是无符号。(与符号无关)
可以在 c++ 代码中显示设定: signed char jeep; unsigned char bar;

扩展字符型

扩展字符型 字节位 备注
wchar_t ≥16 宽字符
char16_t ≥16 Unicode 字符集,c++11新增
char32_t ≥32 Unicode 字符集,c++11新增

注意项:

JS将 非0,非null,非undefined看成true,Java 中的if()只能接收一个boolean类型。

c++ 将 非0 看作 true。将 true 看作1。

c++ 中的声明与定义


// 对于基本数据类型,c++ 会给 i 一个默认值0,那么也就给 i 分配了一个内存空间
int i;

// 下面代码,只是单纯的声明了 j ,不会给默认值0,也不会给 j 分配内存空间
// 这就是声明而非定义
extern int j;

三、c++ 中的复合类型

c++ 中的符合类型包括:数组,字符串,枚举,结构体,共用体,引用,指针。

数组、字符串、枚举 与 Java 的差不多。

1. 结构体


// 通过 struct 关键字,定义了一个结构体 Cat
// 数组只能存储同一个类型的数据,当我们需要存储不同类型数据的时候就可以使用 结构体
//  c++的结构体和类都可以存储不同类型的数据。
// c++的结构体和类的区别在于某些默认权限修饰符的不同(暂时理解)
struct Cat
{
        char name[20];
        int age;
}

// 如下,定义了一个 Cat 类型的结构体,变量名为 tom
Cat tom = {
        "tom",
        3
};

2. 共用体

共用体的作用:在相同的内存位置存储不同的数据类型。

一个共用体中可定义多个不同类型的成员,但任何时候只能有一个成员含有值,共用体提供了一种使用相同内存位置的有效方式。

和 TS 中的联合类型很相似


// 通过 union 关键字,定义了一个共用体 Animal
union Animal
{
        Cat cat;
        Dog dog;
}

3. 引用

引用相当于变量的别名,是对一个已经存在的对象起的另一个名字。(引用与对象之间是绑定到一起的)


// 定义一个 int 类型的变量 age 赋值 1
int age = 1;

// 定义了一个 int 类型的引用 ref,将 age 与 ref 绑定
int& ref = age;

注意项

  1. 引用不能重新绑定
  2. 引用必须初始化

有啥用呢?

#include 
using namespace std;

// 一个简单的 交换两个数的函数
void transform(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int aa = 10;
    int bb = 20;
    transform(aa, bb);
    cout << aa << endl;
    cout << bb << endl;
    // 实际上输出的结果是 aa=10 bb=20
    // 这是因为 c++ 是值传递
}


// 一个简单的 交换两个数的函数
void transform(int a, int b) {
    // 当调用 transform 时,传入了 aa,bb 的值
    // 使得 a = 10, b = 20,可以理解为初始化了两个新的变量
    // 这两个新变量只是内容和
    int temp = a;
    a = b;
    b = temp;
    // 执行完上述三行代码后,仅仅只是 a 和 b 的值互相交换了,
    // 并不会影响函数外的 aa bb
    cout << "a=" << a << endl;
    cout << "b=" << b << endl;
    // 输出的结果是 a=20 b=10
}


// 将函数声明中的参数列表更改为 (int& a, int& b)
// 这里接收的参数就是引用
// 当我们传入 aa 和 bb 时,就是将 引用 a 和 aa 绑定,引用 b 和 bb 绑定
// 当转换 a 和 b 时,就转换了 aa 和 bb
void transform(int& a, int& b) {
    //xxx
}

Java,JS 也是值传递,Java,JS 执行上述类似代码,结果是一样的,不会更改方法外面 aa bb 的值。

值传递

首先记录一下形参和实参。参数列表上的参数,称作形参(a和b)。传入函数的值,称作实参(aa和bb)。实参是形参的初始值。

什么是值传递? 形参是实参的拷贝,改变形参的值不会影响外部实参的值。

想改变值传递,可以使用引用作为形参,接收实参,操作引用就可达到操作实参的目的。

易混淆点

Java,JS 中的对象作为实参传入到函数的参数列表中,在函数中,调用对象的方法、属性,更改了对象中的某个属性的值。这种操作,函数外面的实参,是会变化的。这和上面描述的场景是两回事!!

调用对象的方法、属性,形参指向的目标不会变。但是通过等号进行的赋值操作,会使形参的指向发生变化。

引用需要注意的点

  • 引用不能重新绑定
  • 引用必须初始化
  • 可以利用引用初始化另一个引用
  • 引用类型的初始值必须是一个对象(变量)(就是说不能直接给一个引用赋值基本类型)
  • 引用的类型要和与之绑定的对象的类型严格匹配

4. 指针

(曾经只会Java的我一直以为指针和引用是一个东西。)

指针也是复合类型变量的一种,英文名为 pointer。与引用类似,指针也实现了对其它对象的间接访问,但是指针和引用相比有一些不同点。

  • 指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内,指针可以先后指向几个不同的对象。
  • 指针无需在定义时初始化。

// 使用 * 定义指针,下面代码就定义了一个 int 类型的指针 ip
int* ip;

// 定义一个 int 类型的变量 temp
int temp = 2;

// 将变量 temp 的地址赋值给指针 ip
// 在这里 & 称作 取地址符
ip = &temp;

// 这里输出的值为 2
// * 在这里使用,被称作 解引用符
// ip 指的是上面那个 叫 ip 的指针,*ip 才是 ip 指针指向的地址(就是temp的地址)
// 所以通过 *ip 就能找到 temp
// 使用 *ip 相当于使用变量 temp
 cout << *ip;

&、*不同用法的含义记录

符号 紧随类型名 出现在表达式中
& 声明引用 取地址符
* 声明指针 解引用符

void类型的指针

  1. 可存任意类型对象的地址
  2. 可存任意类型的指针

引用和指针最大的区别

可以给指针重新赋值,指向一个新对象。
引用不能重新赋值。

指向指针的指针

通过 * 的数量来区分指针的级别。
**p 表示指向指针 *p 的指针
***p 表示指向 (指向指针 *p 的指针**p)的指针

简单了解并没有验证的内容
C 中开辟内存的函数为 malloc()
C++ 中使用 new 关键字即可(也可使用 malloc())。
C++ 中没有垃圾回收机制。(Java 中的GC)


// 开辟了一个可以存放 int 类型变量的内存空间
int* pn = new int;
// 释放该空间
delete pn;

// 开辟了一个可以存放 int 类型数组的内存空间
int* pn = new int[10];
// 释放 pn 数组的空间
delete[] pn;

注意项
使用指针的时候,要注意使用 * 解引用符得到真正的值。

5. const

const 是 Java 中的保留字,不是关键字。在 Java 中并没有使用到 const 的地方,只是也不让我们用而已,这种 Java 不用也不让我们用的,叫做保留字。 java 用了,不让我们用的,叫关键字。

C++ 中 cosnt 修饰的变量不能更改,且必须初始化。(最近用 Vue3 + TS,猛写 const

const 修饰的变量不能更改,const 修饰的类、结构体、其成员也不能改变。


struct TestStruct
{
    char name[20];
    int age;
};

void test() {
    const TestStruct temp = {
        'name',
        20
    };
    // 下面这句会报错
    // 因为 temp 被 const 修饰,所以不可更改 age 属性
    temp.age = 30;
    
    // 因为变量 temp 被 const 修饰,则指向变量 temp 的指针也应该用 const 修饰
    const TestStruct* pT = &temp;
    
    // 指针pT 调用 name 属性的话,使用 ->
    
}

6. 函数

函数分为两类:

  1. 没有返回值的函数(Void函数)
  2. 有返回值的函数

C++ 中的代码是从上到下顺序执行的。


int main() {
    test();
}

void test() {
    int a = 20;
    cout << a;
}

上述代码会报错,找不到 test() 函数,因为当 main 函数执行时,test 函数还未被加载。

解决方式,可以将 test 函数写在 main 函数上方,也可以使用 函数声明

函数声明
函数声明也叫做函数原型,函数声明和函数的定义类似,唯一的区别是函数声明不需要函数体。
(一直不太清楚前端TS中有时会需要写 .d.ts 文件是在干嘛。)


// 这里就是函数的声明
void test();

int main() {
    test();
}

void test() {
    int a = 20;
    cout << a;
}

函数声明一般都放在头文件 .h 中,而不是放在源文件 .cpp

// func.h 文件中内容
void test();

// 使用 func.h 需要先 include
#include "func.h"

int main() {
    test();
}

void test() {
    int a = 20;
    cout << a;
}

默认参数

默认参数指的是函数调用时省略了实参时,自动使用的参数。

  • 默认参数必须从右边开始赋值。
  • 如果函数同时有声明和实现,默认参数只能放在函数声明中。

内联函数

使用 inline 关键字修饰函数的声明或实现,可以使其变为内联函数。

内联:将函数的调用直接展开为函数体,作为代码插入调用位置。

inline 关键字:向编译器申请,将该函数内联,但是编译器会做出检测,判断该函数是否可以内联,若检测出该函数体量大,流程繁琐,则不会内联该函数。递归就不会被内联。

什么是函数调用的开销?

当我们调用了一个函数,会先开辟栈空间,当调用完毕后,该空间被回收。从开辟空间到回收空间的过程的消耗,称为函数调用的开销。多次调用某一函数时,就会频繁的开辟,回收。将函数置为内联函数,就可以避免这部分开销。

内联函数使用场景:适合用于优化那些规模较小、流程直接、频繁调用的函数。
优缺点:内联函数可以避免函数调用的开销,但是会增加代码行数。

你可能感兴趣的:(从入门到入门,c++,开发语言)