C++基本语法查缺学习

64 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情

在大二的时候学过一段时间C++的语法,但当时学的很模棱两可,再加上现在已经忘得差不多了,所以最近再跟着菜鸟教程过一遍,每篇博客的开始我都会列出这章的所讲内容,因为之前有C语言的基础再加上好多语法已经过完一遍了,所以博客内容只记录了我忘记的或没学过的,根本目的是自用,如果能帮助到您万分荣幸。

序章

  • 对象:具有状态行为。例:狗的状态:颜色,品种.....,行为:吃,跑....。对象是类的实例
  • 类:描述对象的行为或状态的模板/蓝图
  • 方法:一个方法表示一种行为,一个类可以包含多个方法。可以在方法里写入逻辑,操作数据以及执行所有的动作
  • 即时变量:每个对象独特的即时变量,对象的状态是由这些即时变量的值创建的
#include<iostream>
using namespace std;
​
int main() {
    cout << "haaha";
    return 0;
​
}

对于上面的代码:

  • C++ 语言定义了一些头文件,这些头文件包含了程序中必需的或有用的信息。上面这段程序中,包含了头文件
  • using namespace std:告诉编译器使用std命名空间

数据类型

  • typedef声明:使用typedef为一个已有的类型取一个新的名字
typedef int haha;//haha是int的另一个名字
​
haha a;//创建一个int型变量a

枚举类型

  • 之前学习的时候没有接触过,大概意思就是自己定义一个数据类型。服务对象:一个变量有几种可能的值。
enum 自定义的数据类型{
    枚举1,
    枚举2,
    枚举3
}枚举变量名;
​
​
enum Color{
    red,
    green,
    blue
}c;
  • 如果像上面这样枚举的值没给出初始化,就默认从0开始,依次加一,即red=0;green=1;blue=2
  • 如果把上面的green=6,则red=1,green=6,blue=7;

变量声明

  • 作用:就是使用多个文件的时候,我只在其中一个文件里定义了这个变量,那么变量声明就让编译器知道你这个变量是定义过的,在编译器不需要知道变量完整细节就可以进一步编译。
  • 声明方法:用extern声明
//.h文件
#include <iostream>
using namespace std;
​
int a=10, b=20, c=30;
​
//源文件
#include <iostream>
#include"l.h"
using namespace std;
​
extern int a, b;//声明变量
extern int c;
​
int main()
{
​
    cout << c+a << endl;
    
    return 0;
}
//输出结果为40.

左值和右值

  • 左值就是指向内存位置的表达式,可以出现在赋值号的左边或右边
  • 右值就是存储在内存中某些地址的数值,右值只能出现在赋值号的右边

常量之定义常量

在c++中,有两种定义常量的方式

//#define 预处理器
#define a 100//const 关键字
const int a = 6;

修饰符类型

修饰符就是在int,char,double这些数据类型前面加一些东西,加什么东西呢?下面列出数据类型修饰符

  • signed(有符号)
  • unsigned(无符号)
  • long
  • short

那这些数据类型修饰符有没有什么使用限制呢?

  • int型:可以使用全部的数据修饰符
  • char型:只能 使用signed和unsigned
  • double型:只能使用long型

signed和unsigned也可以作为long或short的修饰符

signed long int a;
unsigned short int b;

类型限定符

constconst类型的对象在程序执行期间不能被改变
volatile告诉编译器不需要优化volatile类型的变量,直接从内存中读取变量。
restrictrestrict修饰的指针唯一一种访问它指向的对象的方式

存储类

存储类的作用:定义变量或函数范围和生命周期,这些说明符放在所修饰类型之前,C++程序的可用存储类一共就六个

auto存储类(从 C++ 17 开始,auto 关键字不再是 C++ 存储类说明符)

  • auoto声明变量时根据变量初始化表达式自动判断变量类型
  • 声明函数时函数返回值的占位符
  • auto定义局部变量,离开作用域会被自动释放。
auto a =1.2;
auto s = ("hello");

register存储类

  • 功能:定义存储在寄存器中而不是RAM中的局部变量
  • 变量的最大尺寸即寄存器的大小
  • 寄存器:用于需要快速访问的变量,eg:计数器。
  • 注意:使用register并不意味着变量将被存储在寄存器中,只是意味着可能存储在寄存器中,这取决于硬件条件
{
    register int m;
}

static存储类

  • 编译器在程序的生命周期内保持局部变量的存在,不需要每次进入作用域后进行创建和销毁
  • 即:用static修饰局部变量可以在函数调用之间保持局部变量的值
static int count =10;

extern存储类

  • 功能:提供一个全局变量的引用,全局变量对所有的程序文件都是可见的

mutable

thread_local(C++11)

位运算符

符号描述运算规则
&两个位都是1,结果才是1
两位有一个是1,结果就是1
异或两个位相同是0,相异是1
~取反0变1,1变0
<<左移每个位左移,高位丢弃,低位补0
>>右移每个位右移,低位丢弃,高位正数左补0,负数左补1

逗号运算符

  • 作用:执行一系列用逗号隔开的运算
#include <iostream>
using namespace std;
int main()
{
    int i = 10, a = 20, b = 30;
    i = b, a;
    cout << i;
    i = (b, a);
    cout << i;
    return 0;
}
//输出为30,20

无限循环

for( ; ; )

#include <iostream>
using namespace std;
int main()
{
    for (; ; ) {
        cout << "这是一个无限循环";
    }
    return 0;
}
//可以按Ctrl C停止循环

判断之switch语句

每次学每次都忘,可能是因为自己不常用吧,再拿来记一遍。

基本格式:

switch(表达式)//直接写表达式 不用双引号
{
case 常量表达式1 :语句/程序块1;break;
case 常量表达式2 :语句/程序块2;break;
……. 
case 常量表达式n :语句/程序块n;break;
Default:语句/程序块n+1//上述case没有符合的,执行default
}

必须用break跳出每一个case,不然好会一直执行

函数之函数声明

int max(int a ,int b);

参数的名称并不重要,类型是必须的,因此也可以写成

int max(int int);

一些有用的c++内置数学函数

需要引用数学头文件 cmath

函数描述
sin( )返回弧度角(double型)的正弦
cos( )返回弧度角的余弦
tan( )返回弧度角的正切
log( )返回参数的自然对数(ln)
pow(x,y)返回x的y次方
sqrt ( )返回参数的平方根
hypot(x,y)假设x,y分别是两个直角边,函数返回斜边
int abs(a)返回a的绝对值
double abs (b)返回浮点数b的绝对值
floor(c)返回一个<=c的最大整数
#include<iostream>
#include<cmath>
using namespace std;
​
int main() {
    int a = 10, b = 20, c = 3;
    float d = -2.3, e = 3.4, f = 1.7;
​
    cout << "sin(f)" << sin(f) << endl;
    cout << "log(a)" << log(a) << endl;
    cout << "pow(b,c)" << pow(b, c) << endl;
    cout << "sqrt(a)" << sqrt(a) << endl;
    cout << "hypot(3,4)" << hypot(3, 4) << endl;
    cout << "abs(d)" << abs(d) << endl;
    cout << "floor(e)" << floor(e) << endl;
    
    return 0;
}

输入输出

必须的头文件 iostream 定义了 cin、cout、cerrclog 对象,分别对应于标准输入流、标准输出流、非缓冲标准错误流和缓冲标准错误流。

标准输出流cout

  • cout是和流插入符<< 结合使用的
  • endl用于在末尾添加一个换行符
#include<iostream>
using namespace std;
​
int main() {
    char str[] = "Hello World!";
​
    cout << "str的值是: " << str << endl;
    
    return 0;
}

标准输入流cin

  • 流提取运算符>> 结合使用
#include<iostream>
using namespace std;
​
int main() {
    char str[20];
    
    cout << "请输入你想输入的东西: " << endl;
​
    cin >> str;
​
    cout << "你输入的是:" << str << endl;
​
    return 0;
}
  • 流提取运算符 >> 在一个语句中可以多次使用,如果要求输入多个数据
cin >> a >> b;
//就相当于:
cin >> a;
cin >> b;

标准错误流cerr(一般用来显示错误消息)

  • cerr 对象是非缓冲的,且每个流插入到 cerr 都会立即输出
  • 也是与流插入运算符 << 结合使用的
char str[] = "Unable to read....";
 
cerr << "Error message : " << str << endl;

标准日志流clog(一般用来显示日志消息)

  • clog对象是缓冲的,每个流插入到clog都会先存储在缓冲区,直到缓冲区填满缓冲区刷新才会输出
 char str[] = "Unable to read....";
 
 clog << "Error message : " << str << endl;

C++字符串

C++提供了两种类型的字符串表示形式

  • C风格的字符串
  • C++引入的string类类型

C风格的(以null结尾)

char  str[5]={'i','k','u','n','\0'}//是使用 null 字符 \0 终止的一维字符数组
//可以简写为:
char str[]="ikun";

C++有大量函数来操作以null结尾的字符串,使用前需加头文件 cstring

函数功能
strcpy(s1,s2)复制字符串s2到s1
strcat(s1,s2)连接字符串s2到s1末尾 (注意s1的空间要足够大)
strlen(s1)返回字符串s1的长度
strcmp(s1,s2)s1=s2返回0;s1<s2返回值<0,s1>s2返回值>0
strchr(s1,ch)返回一个指针,指向s1中字符ch第一次出现的位置
strstr(s1,s2)返回一个指针,指向字符串s1中字符串s2第一次出现的位置
#include<iostream>
#include<cstring>
using namespace std;
​
int main() {
    char s1[100] = "ikun";
    char s2[] = "yufenfen,jiugulicaomushenikun";
    char s3[10];
    int len;
​
    //复制s1到s3
    strcpy_s(s3, s1);
    cout << s3 << endl;
​
    //连接s1和s2
    strcat_s(s1, s2);
    cout << s1 << endl;
    
    //返回s1的长度
    len=strlen(s1);//咱也不知道为什么这个不用加_s
    cout << len << endl;
​
    return 0;
}

C++ 中的 String 类

注意使用时包含头文件 string

string a = "google";
string b = "baidu";
string c;c = a + b;//连接操作,这个在C字符串也能用
​
cout << c << endl;

指针篇

什么是指针?

指针就是一个变量,和其他变量一样,你在使用之前,需要对它进行声明/定义。

int *p;
double *q;
char *r;

既然指针是变量,那么它的值是什么呢?答案是另一个变量的地址。所以在定义完指针后,我们需要把另一个变量的地址赋值给指针

#include<iostream>
#include<string>
using namespace std;
​
int main() {
    int* p;
    int a = 10;
​
    p = &a;
​
    cout << *p << endl;//最终输出10return 0;
}

C++ NULL指针

我们上面说到,指针的值是另一个变量的地址,那么如果没有确切的值赋给指针,该怎么办??解决方法就是为指针赋一个NULL值,被赋予NULL值的指针就被叫做空指针。NULL指针是一个定义在标准库了值为0的常量

int main() {
​
    int* p = NULL;
​
    cout << p << endl;//注意这里是p不是*preturn 0;
}

需要注意的是,大多操作系统都不允许访问地址为0的内存。但地址为0的内存有着重要意义,它表明该指针指向一个不可访问的内存位置,但如果指针是NULL,我们一般会说假定这个指针不指向任何东西

指针的运算

  • 算术运算

    • 因为指针的值是一个用数值表示的地址,假设我们有指针p,指向地址1000,那么p++后,指针指向1004,p--后,指针指向0996。
  • 指针的比较

    • 可以用关系运算符< = >来比较两个指针,前提是两个指针指向两个相关的变量,比如一个数组的两个元素。
#include<iostream>
#include<string>
using namespace std;
​
int main() {
    int a[5] = { 1,2,3,4,5 };
    int* p;
    p = &a[0];
    int* q;
    q = &a[2];
​
    cout << *p << endl;//1
    cout << *q << endl;//3
​
    *p++;
    *q++;
​
    cout << *p << endl;//2
    cout << *q << endl;//4for (p; p <= &a[5]; p++) {
        cout << *p << endl;//如果p的地址<=a[5]的地址,就输出*p
    }
​
    return 0;
}

指针和数组

一个指向数组首地址的指针,可以通过指针的算术运算来访问数组(本质就是一个指针指向一个数组首地址)

#include<iostream>
#include<string>
using namespace std;
const int MAX = 5;
​
int main() {
    int a[MAX] = { 1,2,3,4,5 };
​
    int* p;
​
    p=a;//默认p指向数组a的首地址
        //也可以写成*p=a[0];cout << *p << endl;
​
    cout << *(p + 1) << endl;//指向a[2];
​
​
    return 0;
}

指针数组 : 可以定义用来存储指针的数组。

在上面已经学会了用一个指针指向数组首地址,然后根据指针运算最终可以访问到全部数组。那么有没有一种方法,不需要指针运算,就可以使用指针访问数组捏?那就是指针数组,定义一个数组,里面存放若干指针,使用一个for循环,使每一个指针都指向一个数组内的元素,就ok了。本质是让数组内的每个指针指向需要指向的数组内的元素,并不是指针数组指向另一个数组

#include<iostream>
#include<string>
using namespace std;
const int MAX = 5;
​
int main() {
    int i;
    int a[MAX] = { 1,2,3,4,5 };
​
    for (i = 0; i < 5; i++) {
        cout << "a[" << i << "]的值是" << endl;
        cout << a[i] << endl;
    }
​
    int* p[MAX];//指针数组,数组里面有5个指针
    for (i = 0; i < 5; i++) {
        p[i] = &a[i];//给每个指针赋予数组元素的地址
​
    }
    
    for (i = 0; i < 5; i++) {
        cout << "p[" << i << "]的值是" << endl;
        cout << *p[i] << endl;
    }
    return 0;
}

传递数组给函数

C++传数组给一个函数,数组类型会自动转换成指针类型,因而实际传的是地址

假设我们要传一个一维数组到函数里,共有三种方法

  • 法1:形式参数是指针
void max (int *p)
  • 法二:形式参数是一个已经定义大小的数组
void min (int a[10])
  • 法三:形式参数是一个 未定义大小的数组
void max (int a[])

实例

#include<iostream>
#include<string>
using namespace std;
const int MAX = 5;
​
int sum(int* ptr) {
    int i = 0, sum = 0;
    for (i; i < MAX; i++) {
        sum = sum + *(ptr + i);
    }
    cout << sum << endl;
    return sum;
​
}
​
int main() {
    int a[MAX] = { 1,2,3,5,4 };
    int* p;
    p = a;
    sum(p);
​
    return 0;
}
​
//法二
int sum(int a[MAX]) {
    int i = 0, sum = 0;
    for (i; i < MAX; i++) {
        sum = sum + a[i];
    }
    cout << sum << endl;
    return sum;
​
}
//法三
int sum(int a[]) {
    int i = 0, sum = 0;
    for (i; i < MAX; i++) {
        sum = sum + a[i];
    }
    cout << sum << endl;
    return sum;
​
}

从函数返回数组

C++是不允许直接返回一整个数组作为函数的返回值的,所以我们用一个指针指向这个数组,返回这个指针即可

int * sum ( ){
    
}

实例

注意:C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。

#include<iostream>
#include<ctime>
#include<cstdlib>
​
using namespace std;
const int MAX = 5;
​
int* getrandom() {
    static int a[10];//C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。
    
    srand((unsigned)time(NULL));
    for (int i = 0; i < 10; i++) {
        a[i] = rand();
        cout << a[i] << endl;
    }
    
    return a;
}
​
int main() {
    int* p;
    p = getrandom();
​
    for (int i = 0; i < 10; i++) {
        cout << *(p + i) << endl;
​
    }
​
    return 0;
}

传递指针给函数

形参写上指针类型,直接传就行。如果指针指向的是一个数组,就相当于传一个数组给函数,即传递数组给函数的法一

int max (int *p){

从函数中返回指针

从函数中返回数组的方法C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static变量。

int *p max ( ){

指向指针的指针

指针的指针就是将自身的地址存到另一个指针里面。

int main() {
    int a = 100;
    int* q;
    int** p;q = &a;p = &q;
​
    cout << **p << endl;//100
​
    return 0;
}

引用&

什么是引用

引用是给已存在的变量取了一个别名,通过引用变量名称可以指向变量

格式

//类型 &引用变量名 = 已定义的变量名
int a = 1;
int &c = a;//即c是初始化为a的变量的引用

引用和指针的区别

通过上面我们可以知道,通过引用也可以指向一个变量,这和指针的效果是相同的,那么二者有什么区别呢?

  • 不存在空引用,即引用必须指向一块合法的内存;而指针可以是一个空指针
  • 一旦引用被初始化指向一个对象,它就不能指向另一个对象了;而指针可以随时变化指向的对象
  • 引用必须在创建时被初始化,指针可以在任何时候被初始化

实例

#include <iostream>
using namespace std;
​
int main (){
    int i = 3;
    int j = 4;
    int &a = i;
    int &b = j;
    cout << a << endl;
    cout << b << endl;
    return 0;
}//输出为3 4

把引用作为函数的参数 ⭐

#include <iostream>
using namespace std;
​
void swap(int &a,int &b){
    int t;
    t = a;
    a = b;
    b = t;
}
int main (){
    int a = 20;
    int b = 10;
    swap(a,b);
    cout << "a=" << a << endl;
    cout << "b=" << b << endl;
    return 0;
}//a=10,b=20

把引用作为函数的返回值

  • C++返回一个引用,方式与返回一个指针类似
  • 当返回一个引用时,则返回一个指向返回值的隐式指针,这样函数就可以放在赋值语句的左边
#include <iostream>
using namespace std;
​
int a[5] = {1,2,3,4,5};
int & setelem(int i){
    int& b = a[i];
    return b;
}
​
int main (){
    for(int i=0;i<5;i++){
        cout << a[i] << endl;
    }
    setelem(2) = 20;//改变第二个元素
    setelem(3) = 30;//改变第三个元素
​
    for(int i=0;i<5;i++){
        cout << a[i] << endl;
    }
    return 0;
}