C/C++的学习
一.总结
C语言的应用十分广泛、灵活性强,同时它也比较基础,很适合一些刚入门的
小白学习,若有了C语言的基础,再去学习其他的语言也会轻松很多。我也是一名
小白,真正入门技术也才半年的时间,这半年的时间里我学习了很多关于技术方面
的知识,也自己尝试或与他人合作设计完成过一些小的项目(日后也会更新到我的
博客中)......
这段时间,我主要在学习C++,由于C语言的基础部分和C++的极其类似,所以
我在学习C++的同时,相当于复习了一遍C语言。我也重新整理了一份笔记,希望我
也可以给大家带来一点帮助。
笔记中有错误的地方,欢迎指出,欢迎大家讨论!
二.C/C++的基础部分
1.C++
的框架:
#include <iostream>
using namespace std;
int main
{
system("pause");
return 0;
}
C语言的框架:
#include <stdio.h>
int main
{
return 0;
}
2.C++
的输入输出:
输出:C++:cout << "a = " << a << endl;
(输出变量a的值)
C语言:printf("a=%d",a);
(输出变量a的值)
输入:C++:cin >> a;
(输入变量a的值)
C语言:scanf("%d",&a);
(输入变量a的值)
3.变量类型:
变量存在的意义:方便我们管理内存空间
整型:
变量类型 | 关键字 | 字节大小 | 取值范围 |
---|---|---|---|
短整型 | short | 2字节 | -215~215 |
整型 | int | 32位 2字节 64位 4字节 | -231~231 |
长整型 | long | Windows 4字节 Linux 32位 4字节 Linux 64位 8字节 | -231~231 |
长长整型 | long long | 8字节 | -263~263 |
浮点型(实型):
变量类型 | 关键字 | 字节大小 | 有效数字位数 |
---|---|---|---|
单精度浮点数 | float | 4字节 | 7位有效数字 |
双精度浮点数 | double | 8字节 | 15~16位有效数字 |
注意:在C++中使用 float
定义一个变量时,往往要加上一个 "f"
,告诉编译器这是一个 float
类型的变量,因为编译器会默认小数类型是 double
的。
如下所示:
float f1 = 3.14f; //使用float定义一个变量f1,3.14后面得带上一个f
double d1 = 3.14; //使用double定义一个变量d1
字符型:(单个字符)
变量类型(关键字) | 字节大小 | 取值范围 |
---|---|---|
char | 1字节 | -128~127 |
使用语法:char ch = 'a';
如何求某个字符对应的 ASCII
码?
char ch = 'a';
cout << (int)ch << endl; //将字符型变量强行转化为整型输出
注意事项:
1.要使用单引号,而不是双引号;
2.单引号里面只能是单个字符,不能是字符串;
3.字符型变量并不是把字符本身放到内存中存储,而是将对应的 ASCLL
码放入到存储单元。
字符串型:
使用语法:C语言:char str[] ="......";
C++:
1.C++
的字符串同样可以使用C语言风格的表示方式;
2.C++
特有的表示方式:string str = "......";
但使用C++风格字符串时,得包含一个头文件 #include <string>
。
布尔类型(bool
):
真:true(本质是"1");
假:false(本质是"0");
bool flag_1 = true;
cout << flag_1 << endl; //输出数字1
bool flag_2 = false;
cout << flag_2 << endl; //输出数字2
布尔类型的变量在输入时,不可以输入 true
或 false
,只可以输入数字。实际上,非0的正数其实都代表真,只有0代表假。
4.++
和 --
的用法
++
:I.前置递增:++a
先让变量+1,后进行表达式的运算;
II.后置递增:a++
先进行表达式的运算,后让变量+1;
int a2 = 10;
int b2 = ++a2*10;
cout << "a2= " << a2 << "b2= " << b2 << endl; //a2=11,b2=110;
int a3 = 10;
int b3 = a3++*10;
cout << "a3= " << a3 << "b3= " << b3 << endl; //a3=11,b3=100;
--
:I.前置递减:–a 先让变量-1,后进行表达式的运算;
II.后置递减:a-- 先进行表达式的运算,后让变量-1;
int a4 = 10;
int b4 = --a4*10;
cout << "a4= " << a4 << "b4= " << b4 << endl; //a4=9,b4=90;
int a5 = 10;
int b5 = a5--*10;
cout << "a5= " << a5 << "b5= " << b5 << endl; //a5=9,b5=100;
5.程序流程结构:顺序结构、选择结构、循环结构;
顺序结构:程序按顺序进行,不发生跳转;
选择结构:根据条件,有选择的执行代码;
单行 if
条件语句:
if (条件)
{
....
}
多行 if
条件语句:
if (条件)
{
....
}
else
{
....
}
多条件 if
语句:
if (条件1)
{
....
}
else if (条件2)
{
....
}
else if (条件3)
{
....
}
else
{
....
}
嵌套 if
语句:
if (条件1)
{
if (条件2)
{
if(条件3)
{
....
}
}
}
三目运算符:. . . . ? . . . . : . . . .
三目运算符相当于一个 if-else
语句,问号前面是条件;问号后面、冒号前面是,条件满足时返回的值;冒号后面是,条件不满足时返回的值;并且在C++中,三目运算符返回的是变量,是可以继续赋值的。
(a > b ? a : b) = 100;
//若a>b,则返回a,即a=100;
//若a<b,则返回b,即b=100;
switch-case
语句:
switch ( /*只可以放整型或字符型*/ )
{
case 1 : ....
break; //若没有break,每个case都会执行一遍
case 2 : ....
break;
case 3 : ....
break;
default : .... //前面的分支都不满足时,走这个default
break;
}
循环结构:根据条件,循环执行某一段代码;
while
循环:
while (循环条件)
{
....
}
在执行循环语句的时候,程序必须提供退出的窗口,否则会陷入死循环。
do-while
循环:
与 while
循环的区别:先执行一次循环语句,再进行循环条件的判断;
do
{
....
}
while (循环条件);
for
循环:
优点:结构清晰,比较常用,并且在数组初始化和数组遍历上用的会比较多;
for (初始化表达式;循环条件;末尾循环体)
{
....
}
嵌套循环:
外层循环执行一次,内层循环执行一周;
int i,j;
for(i=0;i<10;i++) //外层循环
{
for(j=0;j<10;j++) //内层循环
{
....
}
}
6.跳转语句:break、continue、goto
break
常用的地方:1.switch-case
语句;2.跳出某个循环;3.跳出内层循环;
break
:只对它所在的那层循环起作用
continue
:跳过这一轮循环剩下的语句,进入下一轮循环;
goto
:无条件跳转语句;
MENU:....
goto MENU; //goto语法:goto 标记;(标记一般是一个大写的单词)
程序中一般不建议使用 goto
语句,以免造成程序流程混乱;
7.随机数
#include <ctime> //下面使用随机数种子,得用到这个头文件
srand((unsigned int)time(NULL)); //添加随机数种子,利用当前系统时间生成随机数,防止随机数每一次都一样
int random = 0;
random = rand(); //随机生成一个数字,位数也是随机的
random = rand()%50; //生成0~49的随机数
random = rand()%100; //生成0~99的随机数
random = rand()%100+1; //生成1~100的随机数
8.数组:
数组是一个集合,存放了多个相同数据类型的元素,并且它储存在一段连续的内存空间里。
一维数组:
一维数组定义方式:
1.数据类型 数组名[数组长度];
2.数据类型 数组名[数组长度] = {....};
(数组的集成初始化)
如果一开始大括号里面的数据的个数小于数组长度,编译器会用0进行填充;
3.数据类型 数组名[] = {....};
编译器会根据大括号里面有多少个数据来推测数组长度是多少;
int i = 0;
int arr[5] = {1,2,3}; //数组长度为5,大括号里面只有3个数字,即数组中最后两个位置被填充为0
//一维数组遍历
for(i=0;i<4;i++) //数组的下标从0开始,如:arr[5],第一个是arr[0],最后一个是arr[4]
{
cout << arr[i] << endl;
}
一维数组名的用途:
1.统计整个数组在内存中的长度;
2.可以获取数组在内存中的首地址;
//一维数组名用途一:统计整个数组在内存中的长度
int arr[5] = {1,2,3,4,5};
int len = 0; //代表数组的长度
sizeof(arr) //数组在内存中的长度;由于是int类型,占4字节,且数组长度为5,所以sizeof(arr)=4*5=20
sizeof(arr[0]) //第一个(每个)元素在内存中的长度
len = sizeof(arr)/sizeof(arr[0]);
//一维数组名用途二:可以获取数组在内存中的首地址
cout << arr << endl; //可以直接输出数组arr的首地址
cout << &arr[0] << endl; //这种方法也同样可以得到数组arr的首地址,但要带上&(取地址)符号,因为直接arr[0]是输出1的
二维数组: 行x列 矩阵
二维数组定义方式:
1.数据类型 数组名[行数][列数];
2.数据类型 数组名[行数][列数] = {{....},{....}};
3.数据类型 数组名[行数][列数] = {....};
4.数据类型 数组名[][列数] = {....};
可以省去行数,但不可以省去列数;提供了列数和大括号里面的数字,编译器可以帮你数好行数;
//用四种定义方式分别定义一个2行3列的数组
int arr[2][3]; //定义方式一
int arr[2][3] = //定义方式二(最直观的定义方式)
{
{1,2,3},
{4,5,6}
};
int arr[2][3] = {1,2,3,4,5,6}; //定义方式三
int arr[][3] = {1,2,3,4,5,6}; //定义方式四
//二维数组遍历
int i,j = 0;
for(i=0;i<2;i++) //常规的二维数组遍历
{
for(j=0;j<3;j++)
{
cout << arr[i][j] << endl;
}
}
for(i=0;i<2;i++) //按照矩阵的样子打印二维数组
{
for(j=0;j<3;j++)
{
cout << arr[i][j] << " ";
}
cout << endl; //排完一行打印一个换行
}
二维数组名的用途:
1.查看二维数组所占的内存空间;
2.可以获取二维数组在内存中的首地址;
//二维数组名用途一:
int arr[2][3] =
{
{1,2,3},
{4,5,6}
};
int line_num,list_num = 0; //代表二维数组的行数和列数
sizeof(arr) //数组在内存中的长度;由于是int类型,占4字节,且该二维数组为2行3列(2*3=6),所以sizeof(arr)=4*6=24
sizeof(arr[0]) //可以查看二维数组第一行(每一行)所占用的内存空间
sizeof(arr[0][0]) //可以查看二维数组第一个(每一个)元素所占用的内存空间
line_num = sizeof(arr)/sizeof(arr[0]);
list_num = sizeof(arr[0])/sizeof(arr[0][0]);
//二维数组名用途二:
cout << arr << endl; //可以直接输出二维数组arr的首地址
cout << arr[0] << endl; //二维数组第一行的首地址也是二维数组的首地址
cout << &arr[0][0] << endl; //二维数组的第一个元素的地址
数组中的经典案例:元素逆置
int arr[5] = {1,3,2,5,4}; //给定的初始的数组,任务要求是将这个数组的所有元素都逆置,即最后要输出4,5,2,3,1
int len = sizeof(arr)/sizeof(arr[0]); //数组长度
int start = 0; //起始位置
int end = len-1; //末尾位置
int temp = 0; //临时变量
while(end>start) //正常情况下,末尾位置一定大于起始位置,如果起始位置与末尾位置重合,说明已经完成了所有元素的逆置
{
temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
//while的那一段代码还可以换成下面这段代码
int i = 0;
for(i=0;i<len/2;i++)
{
temp = arr[start+i];
arr[start+i] = arr[end-i];
arr[end-i] = temp;
if(start>=end) //再做一次判断,start>=end时退出循环
{
break;
}
}
//遍历打印
for(i=0;i<5;i++)
{
cout << arr[i] << endl;
}
冒泡排序(BubbleSort
):
1.比较相邻的两个元素,如果第一个比第二个大,就交换它们两个;
2.对每一对相邻元素做相同的工作,执行完毕后,找到一个最大值;
3.重复以上的步骤,每次比较的次数减一,直到不再需要进行比较;
总结:排序总轮数 = 数组中的元素个数 - 1;
每轮比较的次数 = 数组中的元素个数 - 排序轮数 -1;
//冒泡排序(BubbleSort):
int arr[9] = {4,2,8,0,5,7,1,3,9}; //给定的初始数组,现在是乱序,经过冒泡排序后会升序打印数字
int i,j =0;
for(i=0;i<9-1;i++)
{
for(j=0;j<9-i-1;j++)
{
if(arr[j]>arr[j+1])
{
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
//遍历打印:
for(i=0;i<9;i++)
{
cout << arr[i] << endl;
}
执行流程和效果:
原来的数组:4 2 8 0 5 7 1 3 9
排序轮数 | 4 2 8 0 5 7 1 3 9 | 对比次数 |
---|---|---|
0 | 2 4 0 5 7 1 3 8 9 | 8 |
1 | 2 0 4 5 1 3 7 8 | 7 |
2 | 0 2 4 1 3 5 7 | 6 |
3 | 0 2 1 3 4 5 | 5 |
4 | 0 2 1 3 4 | 4 |
5 | 0 1 2 3 | 3 |
6 | 0 1 2 | 2 |
7 | 0 1 | 1 |
9.函数
作用:将一段经常用的代码封装起来,较少地重复代码,保证代码质量;
一个较大的项目(程序) ———> 若干个“块” ———> 每一个块用一个函数封装起来
函数定义:
1.返回类型;2.函数名;3.参数列表;4.函数体语句;5.返回值表达式 return
;
返回类型 函数名(参数列表)
{
函数体语句
return 表达式;
}
函数调用:函数名 (参数列表);
调用函数的时候,实参的值会传递给形参;换句话说,调用函数的时候发生的是值的传递,形参发生任何改变,都不会影响实参。
函数常见样式:1.无参无返;2.有参无返;3.无参有返;4.有参有返;
函数声明:返回类型 函数名(参数列表);
1.函数声明要在函数定义上面,且要保证函数声明和函数定义中涉及的返回类型、数据类型保持一致;
2.函数声明可以写多次,但是函数定义只能写一次;
3.若函数太多,定义在 main
函数前面会影响整体代码的结构以及可读性,此时可以用到函数声明;
4.写大型程序时,函数声明往往会放在一个 .h
的头文件,而函数定义往往放在一个 .c
的源文件;
10.指针
作用:通过指针间接访问内存(地址),然后对对应内存地址上的变量做访问或修改的操作;
语法:数据类型 * 指针变量名;
指针前加 *
表示解引用,可以找到指针指向的内存中的数据;
指针所占内存空间:C++
中,32位操作系统,指针变量统一占用4个字节;64位操作系统中,指针变量统一占用8个字节;
int a = 10;
int * p = &a;
cout << sizeof(int *) << sizeof(char *) << sizeof(double *) << endl; //输出均为4(32位操作系统)
空指针:指针变量指向了内存中编码为0的地方
语法:int * p = NULL;
用途:可以用于初始化指针变量;但空指针指向的内存空间是不可以访问的(0~255位为系统占用,均不可以访问);
野指针:指针变量指向了非法的内存空间
没有申请内存空间,就直接让一个指针指向这个地方,或者这个指针指针指向了我们权限之外的地方;
const
修饰指针:
//事先给定的情况
int a = 10;
int b = 10;
int * p = &a;
1.常量指针:const int * p = &a;
(相当于 const
修饰 *p
)
特点:指针的指向可以改;但是指针所指向的值不可以改,换句话说,不可以通过指针去修改那个值(但对变量做赋值操作是可以的);
p = &b; //可以
*p = 20; //不可以
a = 20; //可以
2.指针常量:int * const p = &a;
(相当于 const
修饰 p
)
特点:指针的指向不可以改,但可以通过指针去修改指针所指向的值;
p = &b; //不可以
*p = 20; //可以
3.既修饰指针,又修饰常量:const int * const p = &a;
特点:指针的指向不可以改,也不可以通过指针去修改指针指向应的值;
p = &b; //不可以
*p = 20; //不可以
指针和数组:利用指针访问数组中的元素
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int * p = arr; //一维数组名用途二
int i = 0;
for(i=0;i<10;i++)
{
cout << *p << endl;
p++; //指针往后移一位,指向数组中下一个元素
}
指针和函数:
1.值传递:形参的变化不会引起实参的变化;
2.地址传递:可以通过指针修改实参;
11.结构体
结构体是用户自定义的数据类型,允许用户储存不同的数据类型;
语法:struct 结构体名 {结构体成员列表};
struct 结构体名
{
结构体成员列表
};
创建结构体变量的方法:
//假设创建一个学生的结构体
struct student
{
string name; //姓名
int age; //年龄
double score; //分数
}s3; //第三种:定义结构体时顺便创建变量
struct student s1; //第一种
struct student s2 = {"张三",18,95};
C++
中 struct
关键字可以省略(仅在创建结构体变量时,定义结构体时不可以省略)
结构体数组:struct 结构体名 数组名[元素个数] = {{},{},{}....};
结构体指针:通过指针访问结构体变量
//假设创建一个学生的结构体
struct student
{
string name; //姓名
int age; //年龄
double score; //分数
};
struct student s; //创建结构体变量
struct student * p = &s; //创建结构体指针,并指向结构体变量s
//通过.和->访问结构体的成员
cout << s.name << s.age << s.score << endl; //结构体变量或结构体数组用.访问结构体变量中的成员
cout << p->name << p->age << p->score << endl; //结构体指针用->访问结构体变量中的成员
结构体嵌套结构体
struct student //学生的结构体
{
string name; //姓名
int age; //年龄
double score; //分数
};
struct teacher //老师的结构体
{
int id; //编号
string name; //姓名
struct student stu[5]; //带的5名学生;由于老师的结构体里面嵌套了学生的结构体,因此学生结构体的定义要在老师上面
};
struct teacher t; //创建变量t
t.stu[i].name //访问
结构体中使用 const
的场景:
作用:用 const
防止误操作;
将函数的形参改为指针(形参中:结构体变量修改为其所对应的指针),可以减少内存空间,而且不会复制出很多新的副本数据出来;但地址传递是可以修改实参的,如果我并不想修改实参,却又在函数中有了误操作,且我还想利用指针减少内存,此时就可以用 const
;
....(struct student s);
....(struct student * s);
....(const struct student * s); //若在函数里面一旦有修改操作(针对struct student * s),编译器都会报错
12.内存分区:代码区、全局区、栈区、堆区
代码区:存放函数体二进制代码,由操作系统管理;(代码区两个特点:只读、共享)
全局区:存放全局变量、静态变量以及常量的区域;(该区域的数据在程序结束后由操作系统释放)
栈区:编译器自动分配释放,存放函数的参数值、局部变量等;(不要返回局部变量的地址,因为栈区开辟的数据由编译器自动释放)
堆区:由程序员分配释放,若程序员不释放,程序结束后由操作系统回收;(在 C++
中要利用关键字 new
来开辟内存,用关键字 delete
释放内存,相当于C语言中的 malloc
和 free
)
代码区、全局区程序运行前生成,栈区、堆区程序运行后生成;
内存四区的意义:不同区域存放不同数据,赋予不同的生命周期,给我们更大的灵活编程;
常量:
1.字符串常量
2.const
修饰的变量:I.const
修饰的全局变量;(全局常量)
II.const
修饰的局部变量;(局部常量)
全局区:
1.全局变量、静态变量
2.常量区:I.字符串常量;
II.const
修饰的全局变量(全局常量);
全局区和栈区的区分:
全局区 | 栈区 |
---|---|
全局变量、静态变量 | 局部变量 |
…(中间隔了一段位置,意思即是常量虽然也在全局区里面,但真正的位置和全局变量、静态变量之间还隔了一小段) | 函数形参 |
字符串常量、全局常量 | 局部常量 |
总结:名字中带有“局部”的字眼,无论它是常量还是变量,都存放在栈区中, 其他的按照上面所说的存在相应的内存区中;
C++还在学习中O(∩_∩)O,之后还会更新笔记!