C++之基础语法

127 阅读4分钟

c语言基础知识自行解决 ----- 基本数据类型,基本语句,变量类型,枚举,函数,数组,字符串,结构体,指针,指向数组的指针,指向函数的指针,指向结构体的指针。

1.语法须知

  • C++的源文件扩展名是:cpp(c plus plus的简称)。
  • C++程序的入口是main函数(函数即方法,一个意思)。
  • C++完全兼容C语言的语法。C++之前也叫做C with classes。

2. cin、cout

  • C++中常使用cin、cout进行控制台的输入、输出
#include <iostream>

using namespace std;

int main(int argc, char *argv[])

{

   int age;
   cin >> age;
   cout << "age is" << age << endl;
    return 0;
}
  • cin用的右移运算符>>,cout用的是左移运算符<<
  • endl是换行的意思

3. 函数重载

3.1 规则

  • 函数名相同
  • 参数个数不同,参数,类型不同,参数顺序不同
void display(){

    cout << "display()"  << endl;

}

void display(int a,int b){

    cout << "a is" << a << endl;

}

void display(int a){

    cout << "a is" << a << endl;

}

3.2 注意

  • 返回值类型与函数重载无关

  • 调用函数时,实参的隐式类型转换可能会产生二义性

3.3本质

  • 采用了name mangling或者叫name decoration技术

  • C++编译器默认会对符号名(比如函数名)进行改编、修饰、

  • 重载时会生成多个不同的函数名,不同编译器(MSVC、g++)有不同的生成规则

4.默认参数

4.1 C++允许函数设置默认参数,在调用时可以根据情况省略实参。规则如下:

如果函数的实参经常是同一个值,可以考虑使用默认参数

  • 默认参数只能按照右到左的顺序
  • 如果函数同时有声明、实现,默认参数只能放在函数声明中
  • 默认参数的值可以是常量、全局符号(全局变量、函数名)
int age = 33;

void test(){

    cout << "test()" << endl;

}

void display(int a = 11,int b = 22,int c = age, void(*func)() = test){

    cout << "a is" << a << endl;

    cout << "b is" << b << endl;

    cout << "c is" << c << endl;

    test();

}

int main(int argc, char *argv[])

{

  display();

    return 0;

}
  • 函数重载、默认参数可能会产生冲突、二义性(建议优先选择使用默认参数)
void display(int a,int b = 20){

    cout << "a is" << a << endl;

}

void display(int a){

    cout << "a is" << a << endl;

}
int main(int argc, char *argv[])

{

    display(10);//报错main.cpp:24:3: error: call to 'display' is ambiguous。不知道哪个display

    return 0;

}

5. **extern “C” **

5.1 被extern "C"修饰的代码会按照C语言的方式去编译

extern "C" void func(){

    cout << "func()" << endl;

}

extern "C" void func(int age){//c语言不能进行函数重载,会报错。main.cpp:9:17: error: conflicting types for 'func'

    cout << "func(int age)" << age << endl;

}

//或

extern "C" {

    void func(){

        cout << "func()" << endl;

    }

    void func(int age){//c语言不能进行函数重载,会报错。main.cpp:9:17: error: conflicting types for 'func'

        cout << "func(int age)" << age << endl;

    }

}

5.2 如果函数同时有声明和实现,要让函数声明被extern "C"修饰,函数实现可以不修饰

extern "C" void func();

extern "C" void func(int age);//c语言不能进行函数重载,会报错。main.cpp:9:17: error: conflicting types for 'func'

//或

extern "C" {

    void func();

    void func(int age);////c语言不能进行函数重载,会报错。main.cpp:9:17: error: conflicting types for 'func'

}

void func(){

    cout << "func()" << endl;

}

void func(int age){

    cout << "func(int age)" << age << endl;

}

5.3  由于C、C++编译规则的不同,在C、C++混合开发时,可能会经常出现以下操作

  • C++在调用C语言API时,需要使用extern "C"修饰C语言的函数声明
//sum.h

#ifndef SUM_H // 防止重复引用

#define SUM_H 

int sum(int a,int b);

#endif // SUM_H

//sum.c

#include "sum.h"

int sum (int a, int b){

    return a+b;

}

//main.cpp

#include <iostream>

using namespace std;

extern "C"{

    #include "sum.h"

}
  • 有时也会在编写C语言代码中直接使用extern “C” ,这样就可以直接被C++调用。通过使用宏__cplusplus来区分C、C++环境
// sum.h

#ifndef SUM_H // 防止重复引用
#define SUM_H
#ifdef __cplusplus

extern "C" {

#endif //相当于如果c++ 文件引入,自动添加extern "C"{ }

int sum(int a,int b);

#ifdef __cplusplus

     }

#endif  //相当于如果c++ 文件引入,自动添加extern "C"{ }

#endif // SUM_H

//sum .c

#include "sum.h"

int sum (int a, int b){

    return a+b;

}

//main.cpp

#include <iostream>

using namespace std;

#include "sum.h"

6. #pragma once

  • #pragma once可以防止整个文件的内容被重复包含

  • 我们经常使用#ifndef、#define、#endif来防止头文件的内容被重复包含

  • #ifndef、#define、#endif受C\C++标准的支持,不受编译器的任何限制

  • 有些编译器不支持#pragma once(较老编译器不支持,如GCC 3.4版本之前),兼容性不够好

  • #ifndef、#define、#endif可以针对一个文件中的部分代码,而#pragma once只能针对整个文件

7. 表达式

C++的有些表达式是可以被赋值的

void test(){

    int a = 1;

    int b = 2;

    (a = b) = 3;//赋值给了a

    (a < b ?  a : b) = 4;//赋值给了b

}

8. const

  • const是常量的意思,被其修饰的变量不可修改
  • 如果修饰的是类、结构体(的指针),其成员也不可以更改
  • const修饰的是其右边的内容

9. 引用

  • 引用的本质就是指针,只是编译器削弱了它的功能,所以引用就是弱化了的指针。一个引用占用一个指针的大小。
  • 在C语言中,使用指针(Pointer)可以间接获取、修改某个变量的值。在C++中,使用引用(Reference)可以起到跟指针类似的功能。
    int age = 10;

    int &rage = age; //rage就是一个引用
  • 注意点

    • 引用相当于是变量的别名(基本数据类型、枚举、结构体、类、指针、数组等,都可以有引用)

    • 对引用做计算,就是对引用所指向的变量做计算

    • 在定义的时候就必须初始化,一旦指向了某个变量,就不可以再改变,“从一而终”

    • 可以利用引用初始化另一个引用,相当于某个变量的多个别名

    • 不存在【引用的引用、指向引用的指针、引用数组】 

  • 引用存在的价值之一:比指针更安全、函数返回值可以被赋值

  • 常引用,数组的引用。遇到的时候再看。

10. x64常用汇编简介

10.1 一般的规律

  • R开头的寄存器是64bit的,占8字节。RAX,RBX,RCX
  • E开头的寄存器是32bit的,占4字节。EAX,EBX,ECX

10.2 常用要点总结

  • 将src的内容赋值给dest,类似于dest = src
 mov dest, src

 mov dword ptr [ebp-8],ecx #将右侧地址(ecx)中存储的东西取出来赋值给左侧地址(ebp-8)所在的存储空间
  • 中括号[ ]里面放的都是内存地址。 [ 地址值 ] 

  • word是2字节,dword是4字节(double word),qword是8字节(quad word) 

  • 调用函数

call 函数地址

call 003B141F #调用函数,后面跟函数地址
  • 将地址值赋值给dest,类似于dest = 地址值
 lea dest, [ 地址值 ] 

 lea eax,[ebp-14h] #将右侧的地址值(ebp-14h)赋值给左侧eax
  • 函数返回
ret
  • 将op1和op2异或的结果赋值给op1,类似于op1 = op1 ^ op2
 xor op1, op2
  • 类似于op1 = op1 + op2
 add op1, op2
  • 类似于op1 = op1 - op2
 sub op1, op2
  • 自增,类似于op = op + 1
 inc op
  • 自减,类似于op = op – 1 
 dec op
  • 跳转到某个内存地址去执行代码
 jmp 内存地址
  • j开头的一般都是跳转,大多数是带条件的跳转,一般跟test、cmp等指令配合使用 *  权威参考:Intel白皮书 software.intel.com/en-us/artic…

\