皓月星空

导航

C++基础

C++

一、C++简介

1、C++语法特性?
C语言是一种面向过程的语言,任何一个大事件都可以拆分为多个小事件。
C++是一种面向对象的语言,任何一个事件都是某一个对象上的行为/属性。

2、C++语法。
C++是一种全新语法,除了继承了C语言的语法之外,还拓展关于数据类型、继承、多态等新语法。

3、C++函数接口。
C语言即使是相同的功能,也可以写出多个接口。
C++语法提供了标准模板库(STL),它里面提供了一些通用的接口,全世界的人都使用同一个接口。

二、书写第一个C++程序。

1、创建一个C++工程文件。
C++工程文件格式:xxx.cpp

2、编辑代码。
1)C++兼容C语言,也是从main函数开始执行,从main函数结束。

2)如果要使用C++语法,那么就必须在文件开头包含标准头文件以及对应标准的命名空间。
#include -> 标准头文件
using namespace std; -> 标准命名空间(C++中,有很多工具都是放在这个命名空间中,如果想在C++代码中直接使用这些工具,那么就必须使用标准命名空间)
(如果不使用命名空间,那么使用工具时,就必须在工具前面加上作用域符号)

3)C++中使用cout这个工具来输出数据。

4)endl相当于是\n

#include <iostream>
using namespace std;

int main(int argc,char *argv[])
{
	cout << "helloworld" << endl;   //先输出helloworld,然后再换行。
	return 0;
}

3、编译代码。

C语言: gcc编译器 gcc my_first.c -o my_first
C++: g++编译器 g++ my_first.cpp -o my_first

4、执行代码。
gec@ubuntu:/mnt/hgfs/GZ2119/07 C++/01/code$ ./my_first
helloworld

三、变量。

1、变量的作用?
给一段指定的内存空间起名字,方便操作这段内存。

2、如何在C++程序中输出变量的值?

#include <iostream>
using namespace std;

int main(int argc,char *argv[])
{
	cout << "helloworld" << endl;   //先输出helloworld,然后再换行。

	int a = 10;
	cout << "a = " << a << endl;

	return 0;
}

四、常量。

1、常量的作用?
用于记录程序中不可修改的数据。

2、C++定义常量方法有两种:
1)宏常量。

#include <iostream>
using namespace std;

#define DAY 7

int main(int argc,char *argv[])
{
	DAY = 8;
	cout << "一个星期有:" << DAY << "天" << endl;
	return 0;
}

2)const修饰的变量。

#include <iostream>
using namespace std;

int main()
{
	const int month = 12;   //变量
	//month = 13;
	cout << "一年有:" << month << "月" << endl;
	return 0;
}

五、字符串。

1、字符串作用?
可以表示一串字符。

2、在C++中有两种风格来表示字符串。
C风格字符串:
语法: char 数组名[] = "字符串值"

C++风格字符串:
语法: string 字符串名 = "字符串值"

#include <iostream>
using namespace std;

int main()
{
	//1. C风格字符串
	char str[] = "helloworld";
	cout << str << endl;

	//2. C++风格字符串
	string str1 = "appletree";
	cout << str1 << endl;

	return 0;
}

六、转义符号。

常用转义符号:
'\n' -> 换行
'\t' -> 水平制表,跳到下一个Tab键的位置

#include <iostream>
using namespace std;

int main()
{
	//1. 换行符
	cout << "helloworld\n";

	//2. 水平制表
	cout << "aaa\thelloworld" << endl;
	cout << "a\thelloworld" << endl;
	cout << "aaaaa\thelloworld" << endl;

	return 0;
}

七、数据的输入。

1、作用?
从键盘获取一些数据。

2、关键词。
cin

3、语法。
cin >> 变量

#include <iostream>
using namespace std;

//语法:
//cin >> 变量

int main()
{
	/*
	//1. 整型
	int a = 0;
	cout << "请输入一个整型数据:" << endl;
	cin >> a;
	cout << "a = " << a << endl;

	//2. 浮点型
	double d = 0;
	cout << "请输入一个浮点型数据" << endl;
	cin >> d;
	cout << "d = " << d << endl;

	//3. 字符串
	string s = "helloworld";
	cout << "请输入一个字符串" << endl;
	cin >> s;
	cout << "s = " << s << endl;
	*/

	int a;
	char b;

	cin >> a;
	cin >> b;

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;

	return 0;
}

八、递增递减运算符。

1、作用:变量自身+1/-1。
2、符号:++/--

#include <iostream>
using namespace std;

int main()
{
	//1. 前置递增。
	int a = 10;
	++a;
	cout << "a = " << a << endl;  //11

	//2. 后置递增。
	int b = 20;
	b++;
	cout << "b = " << b << endl;  //21

	//3. 前置递增与后置递增有什么区别?
	//前置递增:先让变量+1,然后再进行表达式运算。
	int a2 = 10;
	int b2 = ++a2 * 10;
	cout << "a2 = " << a2 << endl; //11
	cout << "b2 = " << b2 << endl; //110

	//后置递增:先进行表达式运算,再让变量+1。
	int a3 = 10;
	int b3 = a3++ * 10;
	cout << "a3 = " << a3 << endl; //11
	cout << "b3 = " << b3 << endl; //100

	//4. 连续递增。
	int a4 = 10;
	++(++a4);
	cout << "a4 = " << a4 << endl; //12

	int a5 = 10;
	(a5++)++;   //在C++中,不支持连续的后置递增
	cout << "a5 = " << a5 << endl;

	return 0;
}

九、比较运算符。

== 相等于 != 不等于
< 小于 > 大于
<= 小于或者等于 >= 大于或者等于

#include <iostream>
using namespace std;

int main()
{
	/*
	int a = 10;
	int b = 20;

	cout << (a==b) << endl;
	cout << (a!=b) << endl;
	cout << (a<b) << endl;
	cout << (a>b) << endl;
	cout << (a<=b) << endl;
	cout << (a>=b) << endl;
	*/

	string s1 = "helloworld";
	string s2 = "helloappletree";

	cout << (s1==s2) << endl;
	cout << (s1!=s2) << endl;
	cout << (s1>s2) << endl;
	cout << (s1<s2) << endl;
	cout << (s1>=s2) << endl;
	cout << (s1<=s2) << endl;

	return 0;
}

结论:
字符串比较大小:按照ascii码的大小来一一对比。

十、逻辑运算符。

1、作用:
根据表达式真假,返回真值或者假值。

2、符号。
! -> 非 !a 如果a为真,结果为假,如果a为假,则结果为真。
&& -> 与 a&&b 如果a与b都为真,结果就为真,否则为假。
|| -> 或 a||b 如果a与b有一个为真,则结果为真,二者都为假时,则结果为假。

#include <iostream>
using namespace std;

int main()
{
	int a = 10;
	int b = 10;
	int c = 0;
	int d = 0;

	cout << !a << endl;   //0
	cout << !!a << endl;  //1

	cout << (a&&b) << endl;  //1
	cout << (a||b) << endl;  //1

	cout << (a&&c) << endl;  //0
	cout << (a||c) << endl;  //1

	cout << (c&&d) << endl;  //0
	cout << (c||d) << endl;  //0

	return 0;
}

十一、内存分区模型。

1、在C++程序执行时,将内存大方向可以划分为4个区域。
1)代码区:存放函数的二进制代码,由操作系统来进行管理。
2)全局区:存放着全局变量、静态变量、以及常量。
可以理解为: rodata区、bss段、data段统一划分到全局区。
3)堆区: 由工程师手动分配与释放,若不释放,程序结束时由操作系统来回收。
4)栈区: 由编译器自动分配与释放,存放的是函数形式参数,局部变量。

2、内存四区意义?
不同区域存放不同的数据,赋予不同的生命周期,给我们更大的灵活编程。

十二、程序运行前。

在程序编译后,生成一个可执行程序,未执行这个程序之前分为两个区域:
1、代码区:
存放CPU执行的机器指令 -> 其实就是指0101这种数据。

代码区是共享的,共享的目的是对于频繁被执行的代码,只需要在内存中有一份代码即可。
无论执行了多少次这段代码,都是使用同一个代码。

代码区是只读的,使其只读的原因是防止程序意外地修改它的指令。

2、全局区:
全局变量与静态变量存放在此。
全局区还包含了常量区、字符串常量、其他常量(const修改的变量)在存放在此。
该区域的数据在程序结束之后由操作系统来释放。

#include <iostream>
using namespace std;

//2. 创建全局变量。
int g_a = 10;
int g_b = 20;

//4.2 const修饰的全局变量 -> 全局常量
const int c_g_a = 10;
const int c_g_b = 20;

int main()
{
	//1. 创建局部变量。
	int a = 10;
	int b = 20;

	cout << "&a = " << &a << endl;
	cout << "&b = " << &b << endl;       //局部变量: 栈区

	cout << "&g_a = " << &g_a << endl;
	cout << "&g_b = " << &g_b << endl;   //全局变量: 全局区

	//3. 创建静态变量。
	static int s_a = 10;
	static int s_b = 20;

	cout << "&s_a = " << &s_a << endl;
	cout << "&s_b = " << &s_b << endl;   //静态变量: 全局区

	//4. 常量区
	//1>. 字符串常量。
	cout << "string addr = " << &"helloworld" << endl;   //字符串常量: 全局区

	//2>. const修饰变量。
	//const修饰的全局变量  -> 全局常量    
	cout << "&c_g_a = " << &c_g_a << endl;
	cout << "&c_g_b = " << &c_g_b << endl;   //全局常量: 全局区
	
	//const修饰的局部变量  -> 局部常量
	const int c_l_a = 10;
	const int c_l_b = 20;

	cout << "&c_l_a = " << &c_l_a << endl;
	cout << "&c_l_b = " << &c_l_b << endl;   //局部常量: 栈区

	return 0;
}

运行结果:
gec@ubuntu:/mnt/hgfs/GZ2119/07 C++/01/code$ ./space
&a = 0x7ffe3f008ff8
&b = 0x7ffe3f008ffc
&g_a = 0x601070
&g_b = 0x601074
&s_a = 0x601078
&s_b = 0x60107c
string addr = 0x400c47
&c_g_a = 0x400c04
&c_g_b = 0x400c08
&c_l_a = 0x7ffe3f009000
&c_l_b = 0x7ffe3f009004

结论:
不在全局区:
局部变量
使用const修饰的局部变量(局部常量)

在全局区:
全局变量
静态变量
常量:
字符串常量
使用const修饰的全局变量(全局常量)

十三、程序运行后。

栈区: 由编译器自动分配释放,存放函数的形式参数值,局部变量等。
注意: 不要返回局部变量的地址,栈区开辟的数据由编译器自动释放。

#include <iostream>
using namespace std;

//栈区: 形参、局部变量。
//注意: 不要返回局部变量的地址。

int *fun(int b)  //b就是形式参数,存放在栈区
{
	int a = 10;  //a是局部变量,是存放在栈区中。    --> 开始申请a的空间
	return &a; 
						       --> 开始释放a的空间 
}

int main()
{
	int *p = fun(1);
	cout << "p = " << p << endl;    //&a   0/NULL    -> 到这里,a的空间已经释放完了,所以不要再访问a的空间
							 -> 为了防止你再次访问,编译器就不会返回&a给你,返回NULL。
	cout << "*p = " << *p << endl;  //10   段错误

	return 0;
}

堆区:由工程师分配和释放,若不释放,程序结束时由操作系统来回收。
在C++中主要利用new在堆区开辟空间。

#include <iostream>
using namespace std;

int *func()
{
	//利用new关键词,可以将数据开辟到堆区。
	int *a = new int(10);   //在堆区申请一个int类型数据大小的空间,并且将10赋值给这个空间。
	return a;
}

int main()
{
	int *p = func();
	cout << "p = " << p << endl;
	cout << "*p = " << *p << endl;   //10

	return 0;
}

运行结果:
gec@ubuntu:/mnt/hgfs/GZ2119/07 C++/01/code$ ./new
p = 0x1d35c20
*p = 10

结论:
在函数返回时,堆区空间不会释放。

十四、new操作符。

C++中利用new操作符在堆区开辟数据。
堆区中开辟的数据,由工程师手动开辟,手动释放、释放利用操作符delete。
语法:new 数据类型
利用new创建的数据,会返回该数据对应类型的指针。

1、 堆区的基本语法示例。

#include <iostream>
using namespace std;

int *func()
{
	//在堆区中创建整型数据。
	int *p = new int(10);
	return p;
}

int main()
{
	int *p = func();
	cout << "*p = " << *p << endl;  //10

	//堆区的数据由用户主动释放。
	delete p;
	p = NULL;

	cout << "*p = " << *p << endl;  //段错误

	return 0;
}

2、在堆区利用new开辟数组。

#include <iostream>
using namespace std;

int main()
{
	//创建10个整型数据在堆区。
	int *arr = new int[10];   //10代表数组有10个元素。

	int i;
	for(i=0;i<10;i++)
	{
		arr[i] = i + 100;  //给10个元素赋值 100~109
	}

	for(i=0;i<10;i++)
	{
		cout << arr[i] << endl;
	}

	//释放数组时,需要加[]才可以。
	delete[] arr;

	return 0;
}

十五、引用的基本使用。

1、引用作用?
给变量起别名。

2、语法。
数据类型 &别名 = 原名

#include <iostream>
using namespace std;

//引用作用:给变量起别名。
//引用语法:数据类型 &别名 = 原名

int main()
{
	int a = 10;

	//创建引用
	int &b = a;

	cout << "a = " << a << endl;  //10
	cout << "b = " << b << endl;  //10

	b = 100;

	cout << "a = " << a << endl;  //100
	cout << "b = " << b << endl;  //100

	return 0;
}

十六、引用注意事项。

1、引用必须初始化。
2、引用在初始化后,不可以改变。

#include <iostream>
using namespace std;

//引用注意事项。
//1、 引用必须初始化。
//2、 引用在初始化之后,不可以改变。

int main()
{
	//1. 引用必须初始化。
	//int &c;   //编译出错,引用必须初始化,必须要引用一块合法并且存在的空间。
	
	//2. 引用在初始化之后,不可以改变。
	int a = 10;
	int b = 20;

	//创建引用。
	int &c = a;   //一旦初始化之后,就不可以更改。(c永远都是a的别名)

	c = b;  //这是赋值操作
		//不是让c去做b的别名

	cout << "a = " << a << endl;  //20    
	cout << "b = " << b << endl;  //20    
	cout << "c = " << c << endl;  //20    

	return 0;
}

十七、引用做函数参数。

作用:函数传参时,可以利用引用的技术让形参修饰实参。
优点:可以简化指针修饰实参的方法。

#include <iostream>
using namespace std;

//1. 值传递
/*
void mySwap01(int a,int b)
{
	int temp = a;
	a = b;
	b = temp;
}
*/

//2. 地址传递
/*
void mySwap02(int *a,int *b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}
*/

//3. 引用传递
void mySwap03(int &a,int &b)  //别名  //  int &a = a   int &b = b
							  //形式a虽然是一个形参,但是实际上代表也是实参。
{
	int temp = a;  //所以这样调换,操作就是实参。
	a = b;
	b = temp;
}

int main()
{
	int a = 10;
	int b = 20;

	//mySwap01(a,b);   不能交换 -> 因为交换的是形式参数的,形式参数的修改并没有影响到实参,所以实参依然是10和20。
	//mySwap02(&a,&b); 能交换   -> 因为通过形参来解引用访问到实参,所以修改的是实参的值,所以可以更换。
	mySwap03(a,b);     能交换   -> 因为使用引用方式来传递,实参是原名,形参是别名,所以修改形参的值,其实就是修改实参的值。

	cout << "a = " << a << endl;  //10   20    20
	cout << "b = " << b << endl;  //20   10    10

	return 0;
}

十八、引用做函数返回值。

作用:引用是可以作为函数返回值存在的。

注意事项:
1、 不要返回局部变量的引用。
2、 函数的调用可以作为左值存在。

#include <iostream>
using namespace std;

//引用作为函数返回值
//1、 不要返回局部变量的引用。
//2、 函数的调用可以作为左值的存在。

//int    --> 返回的是10(本体的值)
//int&   --> 返回的是a (本体)

int& test01()
{
	//1、不要返回局部变量的引用。
	//int a = 10;  //局部变量
	static int a = 10;
	return a;   //其实就是返回a的本体。   
}

int main()
{
	int &ret = test01();
	cout << "ret = " << ret << endl;  //10

	//2. 函数的调用可以作为左值的存在。
	test01() = 1000;   //a = 1000
	cout << "ret = " << ret << endl; //1000

	return 0;
}

十九、引用的本质。

本质:引用本质在C++中内存实现其实就是一个指针常量。

解析:
情况一:

int a = 10;
int b = 20;
const int *ref = &a;   等价于    int const *ref = &a;    

结论:
可以再赋值给别的地址给ref,但是不可以通过ref去修改ref指向的值。
ref = &b; -> 正确
*ref = 20; -> 错误

情况二:

int a = 10;
int b = 20;
int * const ref = &a;   (ref一旦赋值了&a,那么ref就永远等于&a)    -> 指针常量

结论:
不可以再赋别的地址的值给ref,但是可以通过ref去修改ref指向的值。
ref = &b; -> 错误
*ref = 30; -> 正确

#include <iostream>
using namespace std;

void func(int &ref)   // int &ref = &a
{
	ref = 100;  //  *ref = 100
}

int main()
{
	int a = 10;

	//创建引用,ref是别名,a是原名
	//系统看到这句话之后,会自动转换成 int * const ref = &a;
	//ref是一个指针常量,一旦把&a的这个地址赋值给ref这个指针之后,ref就不能指向别的东西了。
	//这也解析了为什么引用初始化了值之后,不能修改。
	int &ref = a;

	//系统看到这句话之后,发现ref是引用来的,就会自动帮我们转换成: *ref = 20;
	ref = 20;

	cout << "a = " << a << endl;
	cout << "ref = " << ref << endl;

	func(a);
	
	cout << "a = " << a << endl;  //100
	cout << "ref = " << ref << endl;

	return 0;
}
///////
a = 20
ref = 20
a = 100
ref = 100

二十、常量引用。

作用:常量引用主要用于修饰形参,防止误操作。
在函数形参列表中,可以使用const修饰形参,防止形参改变实参。

#include <iostream>
using namespace std;

//常量引用
//一般使用场景:修饰形式参数。

void show_value(const int &val)   //  const int &val = height;
{
	//加了const之后,val就可以打印,但是不可以修改。
	//val = 160;
	cout << "val = " << val << endl;
}

int main()
{
	//int &ref = 10;  //错误
	//引用必须引用一块合法的空间,如果这样写可以的话,意味ref=20也是成立,但是这样做,意味着10=20,就相当于是给常量赋值。
	 /*(1)只有一个const,如果const位于*左侧,表示指针所指数据是常量,不能通过解引用修改该数据;指针本身是变量,可以指向其他的内存单元。

  (2)只有一个const,如果const位于*右侧,表示指针本身是常量,不能指向其他内存地址;指针所指的数据可以通过解引用修改。*/
	//const int &ref = 10;  //正确    -> 只看得到别名,看不到原名
	//系统看到这句话之后,会自动帮我们转换成以下的几句话:
	/*
	int temp = 10;                  -> 既看得到原名,也看得到别名。
	const int &ref = temp;
	*/

        //ref = 20;  //编译出错,ref是只读的。

	int height = 170;
	show_value(height);

	return 0;
}

posted on 2021-11-22 23:05  皓月星空  阅读(88)  评论(0编辑  收藏  举报