C++语法汇总(上)

第1章 基础知识

1.1 第一个C++程序

#include<iostream>
using namespace std;
int main()
{
	//单行注释
	/*
	多行注释
	*/
    cout << "Hello, C++! " << endl;
    system("pause");
    return 0;
}

1.2 数据类型

//整型
short a; //2字节,-2^15~2^15-1
int a; //4字节,-2^31~2^31-1
long a; //Windows、Linux32位为4字节,Linux64位为8字节,-2^31~2^31-1
long long a; //8字节,-2^63~2^63-1
//实型
float a; //4字节,7位有效数字(需要在小数末尾加上f)
double a; //8字节,15~16位有效数字(默认)
//字符型
char c = '字符'; //1字节
//字符串型
char str[] = "字符串"; //C语言风格
string str = "字符串"; //C++语言风格(需要添加头文件#include<string>)
//布尔型
bool a; //1字节,取值只能是true或false

1.3 数组

C++的数组初始索引值为0。

int arr[5] = {1,2,3,4,5}; //数组长度和元素个数对应
int arr[5] = {1,2,3,4}; //1,2,3,4,0
int arr[] = {1,2,3,4,5}; //需要编译器自行推断数组的长度

C++的多维数组的储存原则是行主原则(Row Major)。

//二维数组初始化方法一
int arr1[2][3];
arr1[0][0] = 1;
arr1[0][1] = 2;
arr1[0][2] = 3;
arr1[1][0] = 4;
arr1[1][1] = 5;
arr1[1][2] = 6;
//二维数组初始化方法二
int arr2[2][3] = {{1,2,3},{4,5,6}};
//二维数组初始化方法三
int arr3[2][3] = {1,2,3,4,5,6};
//二维数组初始化方法四
int arr4[][3] = {1,2,3,4,5,6};

冒泡排序算法:

for(int i = 0;i < sizeof(arr)/sizeof(arr[0]) - 1;i++)
{
    for(int j = i + 1;j < sizeof(arr)/sizeof(arr[0]);j++)
    {
        if(arr[j] < arr[i]) //升序排序
        {
            int temp = arr[j];
            arr[j] = arr[i];
            arr[i] = temp;
        }
    }
}

1.4 其他知识

#define PI 3.1415 //宏常量定义,一般放在主函数之前
const double PI = 3.1415; //第二种定义方法
cin >> a; //用a接收从键盘输入的值
a % b; //取余
a != b; //a不等于b
a += b; //相当于a=a+b
a&&b; //a与b
a||b; //a或b
!a; //非a
++i; //先对i进行加1操作,再对i进行运算
i++; //先对i进行运算,再对i进行加1操作

第2章 流程控制

2.1 分支结构

逻辑运算 ? 表达式1 : 表达式2; //逻辑运算为真,执行表达式1,反之执行表达式2
if(逻辑运算)
{
    执行语句;
}
if(逻辑运算)
{
    执行语句1;
}
else
{
    执行语句2;
}
if(逻辑运算1)
{
    执行语句1;
}
else if(逻辑运算2)
{
    执行语句2;
}
...
else if(逻辑运算n-1)
{
    执行语句n-1;
}
else
{
    执行语句n;
}
switch(表达式) !只能是整型或字符型
{
    case1:
        执行语句1;
        break;
    case2:
        执行语句2;
        break;
    ...
    case 值n-1:
        执行语句n-1;
        break;
    default:
        执行语句n;
        break;
}

2.2 循环结构

for(起始表达式;条件表达式;末尾循环体)
{
    循环语句;
}
while(逻辑运算)
{
    循环语句;
}
do
{
    循环语句;
}
while(逻辑运算);

2.3 其他流程控制语句

break; //跳出循环、switch语句
continue; //跳过本次循环,执行下一次循环
exit; //退出程序

//无条件跳转
goto flag;
flag:

2.4 流程控制的应用

例:编写一个在0~100范围内猜数字的程序。

a + ( rand()%(b-a) ); //生成[a,b)的随机整数
a + ( rand()%(b-a+1) ); //生成[a,b]的随机整数
a + ( rand()%(b-a) ) + 1; //生成(a,b]的随机整数
#include<iostream>
#include<ctime> //记录当前时间的头文件
using namespace std;
int main()
{
    int guessNumber = 0; //记录猜的数字
    int guessTime = 0; //记录猜的次数
	srand((unsigned int)time(NULL)); //添加随机数种子
    int answer = 0 + ( rand()%(100 - 0 + 1) ); //生成随机数[0,100]的随机整数
    while(true)
    {
        if(guessTime > 10) //只有10次机会
        {
            cout << "Game Over!" << endl;
            break;
        }
        cout << "请输入您要猜的数字:" << endl;
        cin >> guessNumber;
        if (guessNumber > answer)
        {
            cout << "猜测过大!" << endl;
        }
        else if(guessNumber < answer)
        {
            cout << "猜测过小!" << endl;
        }
        else
        {
            cout << "恭喜你猜对了!" << endl;
            break;
        }
        guessTime++;
    }
    system("pause");
    return 0;
}

第3章 函数&指针&引用

3.1 函数

//无参无返函数
void func()
{
    cout << "Hello!" << endl;
}
func(); //调用方法举例

//有参无返函数
void func(int a)
{
    cout << "Hello!" << endl;
}
func(10); //调用方法举例

//无参有返函数
int func()
{
    cout << "Hello!" << endl;
    return 10;
}
int a = func(); //调用方法举例

//有参有返函数
int func(int a)
{
    cout << "Hello!" << endl;
    return a;
}
int b = func(a); //调用方法举例

C++函数的参数传递属于值传递,传进函数的实参和函数中的形参的内存地址是不一样的,因此对形参的任何改变都不会影响到实参的值,下面这个swap函数也就达不到交换的目的:

void swap(int a,int b)
{
    int temp = a;
    a = b;
    b = temp;
}

当函数写在主函数后面时,主函数在调用该函数之前需要在主函数前对其进行声明:

#include<iostream>
using namespace std;

int max(int a,int b);

int main()
{
    int a = 1;
    int b = 2;
    cout << "最大值为:" << max(a,b) << endl;
    system("pause");
    return 0;
}

int max(int a,int b)
{
    return a > b ? a : b;
}

接下来讲一下函数的分文件编写(以上面的swap函数为例)。分文件编写的思路是把函数声明函数实现分开,分别写在头文件(.h)和源文件(.cpp)中。

//将函数声明写在头文件swap.h中
void swap(int a,int b);
//将函数实现写在源文件swap.cpp中
#include<iostream>
using namespace std;
void swap(int a,int b)
{
    int temp = a;
    a = b;
    b = temp;
}
//主函数需要包含swap函数的头文件才能调用swap函数
#include"swap.h"
#include<iostream>
using namespace std;
int main()
{
    int a = 1,b = 2;
    swap(a,b);
    cout << "a = " << a << "\tb=" << b << endl;
    system("pause");
    return 0;
}

在C++中,函数的形参列表中的形参是可以有默认值的,这叫做函数的默认参数

int func(int a,int b = 20,int c = 30)
{
    return a + b + c;
}
func(10); //60
func(10,30); //70

如果某个位置已经有了默认参数,从这个位置往后,从左到右都必须有默认值:

int func(int a,int b = 20,int c,int d)
{
    return a + b + c + d;
}
func(10); //不合法
func(10,20,30,40); //合法

函数的声明和实现只能其中一个有默认参数:

#include<iostream>
using namespace std;

int func(int a,int b = 10); //声明

int main()
{
    func(10,20);
    system("pause");
    return 0;
}

int func(int a,int b) //实现
{
    return a + b;
}

在某些时候,函数也可以有一个占位参数

void func(int){}
func(10); //调用时必须传入一个int型的参数
void func(int = 10){} //占位参数还能有默认参数
func(); //调用时可以不传入参数

在函数名相同并且在同一个作用域(全局作用域,即在main函数外)的情况下,函数参数的类型不同或者个数不同或者顺序不同时可以实现函数重载

void func(){}
void func(int a){}
void func(double a){}
func(); //调用第一个函数
func(1); //调用第二个函数
func(1.0); //调用第三个函数
//引用参数作为函数重载
void func(int &a){}
void func(const int &a){}
int a = 10;
func(a); //调用第一个函数
func(10); //调用第二个函数
//函数重载遇到默认参数
void func(int a,int b = 10){}
void func(int a){}
func(10); //这个时候有二义性,会报错,要尽量避免这种情况

3.2 指针

编译器在存放一个数据时,是把这个数据放在一个用16进制数表示的内存里面,称为地址,所以想要访问这个数据(或者说这段内存),就必须知道其地址值,但是由于地址值不方便记忆,所以才有了定义变量的概念。
指针可以用来保存某个内存的地址(即指针就是一个地址),提供了一种间接访问内存的方法。不管是什么数据类型,在32位操作系统下,指针都占用4个字节,64位操作系统下都占用8个字节。
空指针:指针变量指向内存中编号为0的空间。可以用于初始化指针变量。空指针指向的内存是不可以访问的(0~255之间的内存编号是被系统占用的)内存。
野指针:指针变量指向非法的内存空间。

#include<iostream>
using namespace std;
int main()
{
    int a = 10;
    int* p; //定义可以指向整型数据的指针变量p
    p = &a; //p指向a这块内存(&是取址符)
    *p = 100; //*作用于指针变量,代表指针指向这块内存的数据(指针的解引用)
    cout << "a = " << a << endl; //a = 100
    system("pause");
    return 0;
}
//常量指针(“常量的指针”,指针的指向可以修改,但是指针指向的值不可以修改)
const int* p = &a;
//指针常量(“指针的常量”,指针的指向不可以修改,但是指针指向的值可以修改)
int* const p = &a;
//常量指针常量(指针的值和指向均不可修改)
const int* const p = &a;

用指针技术访问数组元素时必须找到这个数组的首地址,以此为参照才能访问其他元素:

#include<iostream>
using namespace std;
int main()
{
    int arr[10] = {1,2,3,4,5};
    int* p = arr; //数组名代表数组的首地址
    cout << "第一个元素:" << *p << endl; //输出1
    p++;
    cout << "第二个元素:" << *p << endl; //输出2
    system("pause");
    return 0;
}

我们可以利用指针技术,在定义函数的时候用指针变量定义其形参,使函数参数传递方式变成地址传递,这样swap函数就达到了交换的目的:

void swap(int* a,int* b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

另外,地址传递(只传递地址,占4或8个字节)相对于值传递(完全拷贝一份实参的数据给型参)会有更高的效率,这一点在结构体作为传递参数的时候更能充分体现。当有了地址传递后,如果我们不希望函数内部对传入的实参进行修改,这时候我们可以有意识地将函数的形参定义成常量指针的形式:

void func(const int* a)
{
    //a= 100; //会报错 
    cout << "a = " << a << endl;
}

最后来看一个封装一个可以用冒泡排序对数组进行升序排序的函数的程序。

#include<iostream>
using namespace std;

void bubbleSort(int* arr,int len)
{
    for (int i = 0;i < len - 1;i++)
    {
        for (int j = i + 1;j < len;j++)
        {
            if (arr[j] < arr[i])
            {
                int temp = arr[j];
                arr[j] = arr[i];
                arr[i] = temp;
            }
        }
    }
}

void printArr(int* arr,int len)
{
    for (int i = 0;i < len;i++)
    {
        cout << arr[i] << "\t";
    }
    cout << endl;
}

int main()
{
    int arr[] = {2,1,3,5,4};
    int len = sizeof(arr)/sizeof(arr[0]);
    bubbleSort(arr,len); //传入的arr代表数组的首地址
    printArr(arr,len);
    system("pause");
    return 0;
}

3.3 引用

引用就是给变量起别名,并且别名和原名的数据类型必须的一致。

数据类型 &别名 = 原名;

引用必须初始化,并且初始化后不可以改变,以下语法都是错的:

//引用没有初始化
int &b;
//初始化后不能改变
int &b = a;
int &b = c;

引用的本质是指针常量(指针指向不能改变,所指的值可以改变)。

int a = 10;
int &b = a; //相当于int* const b = &a;
b = 20; //相当于*b = 20;

所以引用传递也可以让swap函数达到交换的目的:

void swap(int &a, int &b)
{
    int temp = a;
    a = b;
    b = temp;
}

和地址传递一样,当有了引用传递后,如果我们不希望函数内部对传入的实参进行修改,这时候我们可以有意识地将函数的形参定义成常量引用的形式:

void func(const int &a)
{
    //a= 100; //会报错 
    cout << "a = " << a << endl;
}

3.4 内存分区模型

C++在执行程序时,将内存划分为四个区,在程序运行前有代码区全局区,在程序运行时有栈区堆区,不同区域存放的数据,赋予不同的生命周期。

(1)代码区:存放函数体的二进制代码,由操作系统进行管理,可共享和只读。
(2)全局区:存放全局变量、全局常量、静态变量、字符串常量,由操作系统进行管理。

#include<iostream>
#include<string>
using namespace std;
int global_variable = 1; //全局变量(在函数体外创建的普通变量)
const int global_const = 1; //全局常量(在函数体外创建的常量)
int main()
{
	int local_variable = 1; //局部变量(在函数体内创建的普通变量)
    const int local_const = 1; //局部常量(在函数体内创建的常量)
	string string_variable = "Hello!"; //字符串变量
	static int static_variable = 1; //静态变量
    cout << "全局变量的地址为:" << (int)&global_variable << endl; //4309020
    cout << "全局常量的地址为:" << (int)&global_const << endl; //4298816
    cout << "局部变量的地址为:" << (int)&local_variable << endl; //17823700
    cout << "局部常量的地址为:" << (int)&local_const << endl; //17823688
    cout << "字符串变量的地址为:" << (int)&string_variable << endl; //17823648
    cout << "字符串常量的地址为:" << (int)&"Hello!" << endl; //字符串常量,4308920
    cout << "静态变量的地址为:" << (int)&static_variable << endl; //4309024
    system("pause");
    return 0;
}

(3)栈区:由编译器自动分配和释放,存放函数的参数值、局部变量等。
(4)堆区:由程序员分配和释放(先进后出),若程序员不释放,程序结束时由操作系统回收。

不允许返回局部变量的地址,因为处于栈区的局部变量在函数执行完后会被摧毁(释放地址),这时候再访问它们的地址就是非法操作。若要保留栈区的局部变量的地址不被系统释放,可以用new关键字在堆区开辟内存之后用delete关键字释放该内存。

#include<iostream>
using namespace std;

int* func() //函数的返回值是指针
{
    int* p = new int(10); //在堆区开辟一个存放整型数10的内存
    return p;
}

int main()
{
    int* p = func();
    cout << *p << endl; //输出10
    delete p; //在主函数执行完之前释放掉p在堆区开辟的内存
    system("pause");
    return 0;
}

有时候会在堆区开辟一个数组,它的开辟和释放方法如下:

int* arr = new int[10]; //在堆中开辟一个存放int型长度为10的数组给arr
delete[] arr; //告诉编译器释放的是数组而不是一个数

同理,不允许返回局部变量的引用。可以用static关键字将局部变量变为静态变量存放在全局区,这样在整个程序执行完后该变量才会被释放。staticnew的区别是new是针对内存(地址),static是针对变量。并且如果函数的返回值是引用,那么这个函数调用可以作为左值。

#include<iostream>
using namespace std;

int& func() //函数的返回值是引用
{
    static int a = 10; //静态变量在全局区,程序执行结束才会释放
    return a;
}

int main()
{
    int &b = func(); //用引用类型接收函数返回值
    cout << "b = " << b << endl; //输出10
    func() = 100; //函数做左值
    cout << "b = " << b << endl; //输出100
    system("pause");
    return 0;
}

第4章 结构体

4.1 结构体创建

#include<iostream>
#include<string>
using namespace std;

//结构体一般创建在主函数之前
struct Student
{
	//结构体的默认访问权限为公共
    string name;
    int age;
};

int main()
{
    struct Student s1; //struct可以省略
    //第一种初始化方式
    s1.name = "张三";
    s1.age = 18;
    //第二种初始化方式
    Student s2 = {"李四",19};
    system("pause");
    //结构体数组的初始化
    Student sArr[] =  { {"张三",18},{"李四",19},{"王五",20} };
    sArr[2].name = "赵六"; //修改第三个学生的名字
    return 0;
}

4.2 结构体指针

结构体指针(或类的对象指针)要用->符号访问成员(或类的对象的属性和行为)。

#include<iostream>
#include<string>
using namespace std;

struct Student
{
    string name;
    int age;
};

int main()
{
    Student s = {"张三",18};
    Student* p = &s;
    cout << "姓名:" << p->name << "\t年龄:" << p->age << endl;
    system("pause");
    return 0;
}

4.3 结构体嵌套

#include<iostream>
#include<string>
using namespace std;

struct Student
{
    string name;
    int age;
};
 
struct Teacher
{
    string name;
    int age;
    Student s;
};

int main()
{
    Teacher t = {"王老师",40,{"小明",18}};
    system("pause");
    return 0;
}

4.4 结构体参数

#include<iostream>
#include<string>
using namespace std;

struct Student
{
    string name;
    int age;
};

void studentPrint1(Student s) //值传递
{
    s.age = 18;
    cout << "姓名:" << s.name << "\t年龄:" << s.age << endl;
}

void studentPrint2(Student* s) //地址传递
{
    s->age = 19;
    cout << "姓名:" << s->name << "\t年龄:" << s->age << endl;
}

int main()
{
    Student s = {"张三",18};
    studentPrint1(s);
    studentPrint2(&s);
    system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值