C语言学习笔记
系列文章目录
1.元件基础
2.电路设计
3.PCB设计
4.元件焊接
5.板子调试
6.程序设计
7.算法学习
8.编写exe
9.检测标准
10.项目举例
11.职业规划
文章目录
- 前言
- C的面向过程和C++的面向对象
- 1.C++的数据类型与常量
- 2.C++中的变量
- 1、什么是变量?
- 2、变量名字的命名规则:
- 3、命名建议:
- 4、const常量与宏定义区别
- 3、C++中的运算符
- 4、赋值运算符和赋值表达式
- 5、C++的语句与输入输出
- 1、语句:
- 2、输入与输出:
- 3、兼容C语言的输入与输出:
- 6、if 语句实现逻辑运算与冒号表达式
- 1、逻辑运算符的运算
- 2、冒号表达式:
- 3、冒号表达式的规则:
- 7、用switch语句实现多分支选择结构
- 1、为什么会有switch语句?
- 2、switch语句的格式:
- 3、实例题目:
- 8、while 循环语句
- 1、while 循环语句的格式:
- 2、do-while 循环语句的格式:
- 9、for循环语句及break和continue的作用
- 1、for循环语句的格式:
- 2、用 break 语句提前结束循环过程并跳出:
- 3、用 continue 语句提前结束本次循环:
- 4、举例实现求出100~200之间的素数并打印出来:
- 10、水仙花数
- 11、函数简介
- 1、定义函数的形式:
- 2、函数的简单调用:
- 3、形参与实参:
- 4、函数的前置声明
- 5、递归调用
- 11、局部变量和全局变量
- 1、多文件共同访问一个全局变量:
- 2、static 静态类型变量:
- 12、数组与一维数组
- 1、如何定义数组?
- 2、如何访问数组中的元素:
- 3、一维数组的初始化:
- 4、案例
- 5、小作业
- 12、字符数组
- 13、使用字符串处理函数操作字符数组
- 1、目的:
- 2、字符串连接函数:strcat
- 3、字符串拷贝函数:strcpy
- 4、字符串比较函数:strcmp
- 5、字符串求长度函数:strlen
- 6、字符串的长度和占用字节数的区别:
- 7、小作业:
- 14、地址与指针
- 1、变量与指针:
- 2、指针变量的定义及初始化:
- 15、数组与指针
- 16、结构体类型
- 1、自定义数据类型:
- 2、定义结构体的必要性:
- 3、结构体类型的声明:
- 4、结构体类型变量的定义及初始化:
- 5、结构体类型变量成员的访问:
- 17、结构体数组与指针
- 1、结构体类型和数组配合使用:
- 2、指向结构体变量的指针:
- 18、枚举类型及定义新的类型名字
- 1、枚举类型:
- 2、枚举类型的声明:
- 3、枚举类型举例:枚举类型和结构体类型结合:
- 4、用 typedef 类型声明新的类型名字:
- 5、typedef与宏定义:
- 19、引用及new和delete的使用
- 1、何为变量的引用?
- 2、引用的注意事项:
- 3、使用new和delete动态分配内存:
- 4、new 出来的变量/内存的生命周期:
前言
送给大学毕业后找不到奋斗方向的你(每周不定时更新)
【牛客网】构建从学习到职业的良性生态圈
中国计算机技术职业资格网
上海市工程系列计算机专业中级专业技术职务任职资格评审
C的面向过程和C++的面向对象
C的面向过程和C++的面向对象
1.C++的数据类型与常量
C++的数据类型与常量学习资料
整型呢,一般用来表示整数,没有小数点的,浮点型一般用来表示有多位小数点的数,字符型呢,一般表示一个一个的字符,比如字母 a,b,c 之类的。
类型呢,又可分为 有符号的 和 无符号的,有符号的可以表示负数,无符号的只能表示正数。但是无符号的最大正数会比有符号的最大正数大很多。
整型、浮点型、字符型使用重点:要兼顾扩展性与占用空间两方面的需求。小了不够用,容易溢出越界,大了呢既占用空间,又不利于传输(速度)。
常量示例代码:
#include <iostream>
using namespace std;
#define price 30
int main()
{
cout << "张三要去买苹果了。。。。。。。\r\n";
int jin = 5;
cout << "张三要买" << jin << "斤苹果,苹果的价格是:" << price << "元\\斤\r\n";
cout << "张三总共要付:" << jin * price << "元。" << endl;
return 0;
}
本节要求,自己编写代码实现输出单引号和双引号!
2.C++中的变量
C++中的变量
1、什么是变量?
变量,顾名思义,就是在程序的运行过程中值是可以改变的。
一个变量包括变量的类型、名字和变量的值。
2、变量名字的命名规则:
C++规定变量的名字只能由数字、字母、下划线这么三种字符组成。而且第一个字符不能是数字,必须是字母或者下划线。
举几个例子:
Sum, add, total, helloWorld, user1, _Pass, my_num_1 等等,但是 3gx 这种是错误的,不能是数字开头。
注意:大小写是区分的,Sum 和 sum 以及 SUM、suM 都认为是不同的变量。
3、命名建议:
①、最好以变量的用途来命名:
例如:
求和:sum
学生:student
苹果:apple
另外,大家也可以百度一下 “匈牙利命名法”,在变量前面加一个字母来表示变量的类型:
iSum,cSex 等等,i 表示 整型的 int,c 表示字符型的 char 等等。
这样程序更易于维护,如果代码量比较大的话时间久了就容易忘了,如果变量的名字起的让人一看就知道是干嘛用的,这不是能节省很多时间么。而且以后来说可能代码不是你一个人看,还有别的人看呢!
②、最好不要用中文来命名,如果你英文不是很好的话,用拼音也比用中文专业。
③、命名长度:C++中没有强制规定命名标识符的长度,但各个具体的编译器厂商一般都有限制。有的不能超过32个字符等等。反正也没必要那么长,尽量能标识该变量的用途就可以了。
4、const常量与宏定义区别
const常量与宏定义区别
C/C++中宏定义和常变量的区别
const型变量和#define宏定义的区别
(1)编译器处理方式不同
define宏是在预处理阶段展开。
const常量是编译运行阶段使用。
(2) 类型和安全检查不同
define宏没有类型,不做任何类型检查,仅仅是展开。
const常量有具体的类型,在编译阶段会执行类型检查。
(3) 存储方式不同
define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。
const常量会在内存中分配(可以是堆中也可以是栈中)。
(4)const 可以节省空间,避免不必要的内存分配。
const定义的常量在程序运行过程中只有一份拷贝,而 #define定义的常量在内存中有若干个拷贝。
(5) 提高了效率。
编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
使用注意
根据以上分析,如果宏定义仅仅是使用形如:
#define PI 3.14159
这样的方式来声明常量,那么不如使用:
const float PI = 3.14159;
更能避免错误。
还有一点顺便要提到的,既然宏定义是直接做替换,若是没有注意到这一特性,就容易出现偏离编程意愿的结果,来看一个例子:
#define Square(x) x*x
这里试图声明一个宏定义Square来计算某数的平方,可能会出现这样的情况:
int x = 1, y = 2;
int result = Square(x+y);
结果应该为3的次方也就是9,那么实际上结果是:
x+y*x+y = 5
故而结论是,使用小括号 括起来就行了,像下面这样
#define Square(x) ((x)*(x)) //为防止类似冲突,将外面也整个括起来
3、C++中的运算符
C++中的运算符
基本的算术运算符
+:加法运算符或正值运算符:2+3,+3;
-:减法运算符或负值运算符:5-2,-3;
*:乘法运算符:3 * 5;
/:除法运算符:5/3;这个算出来等于1,求商;
%:求余运算符:5%3,要求两侧均为整数。这个算出来等于2,求余;
知识点一:
5/3 大家知道结果是多少吗?猜一猜,这里不说5/3了,简单点,就说5/2,很多学员肯定马上就回答了:2.5,到底结果是不是 2.5 呢,我们到VS2013中来测试一下。详情见视频语音讲解。
int x = 5, y = 2;
cout << "5 / 2 = " << x / y << endl;
所以,这里面的 / 是求商的意思。5除以2,商=2,余数是1,这个大家小学的时候应该学过。同理,5/3=1,商是1,余数2;
4/2=2,商是2,余数是0;那我要问了,3/5呢?3/5=0,余数是3;
% 就是求余的功能,我们不妨来试一下
那么有的会问了,我想正确的得到 5除以2等于2.5,怎么办呢?别着急,不是还有浮点类型的数嘛!
float x = 5.0, y = 2.0;
cout << "x / y = " << x / y << endl;
再问大家一个问题:
float x = 5.0;
int y = 2;
cout << "x / y = " << x / y << endl;
这个结果是什么呢?大家可以自己动手试试!答案是:2.5
知识点二:
知识点三:
不管是 a++ 还是 ++a执行完之后,a变量自己的值都进行了+1,但是针对整个 a++或者++a的值就不一样了。a++执行之后,这个表达式的值还是a+1之前的值,++a执行之后,这个表达式的值是a+1之后的值。
4、赋值运算符和赋值表达式
赋值运算符和赋值表达式
1、赋值运算符:
总结一个原则:多的给少的会丢失,少的给多的没事儿。
例如:int x = 3.5; 赋值完了之后 3.5 这个浮点类型的值肯定丢失小数点部分,变成了整形的3;
float y = 2; 赋值完了之后 2 的值不会有任何丢失,不过变成了浮点类型的数:2.0;
另外,不仅仅有小数点部分的丢失,取值范围有可能越界,例如:
short 能表示的范围是:-32768~+32767,最大值也才3万多,那我要是这样赋值呢?
short q = 50000; 大家可以把这句话放到 vs2013 中实践一下,发现复制完成后,q的值变负数了,说白了也就是越界了。为什么会变成负数呢,这里涉及到补码的东西,以后会给大家讲解。
一个水桶容量就2升,你非要装4升,那桶还不撑坏了呀?
另外,把一个负数赋值给一个无符号的数也会发生问题的,例如:
unsigned int x = -2; 这样的问题也不应该发生。
总结:赋值运算的时候要注意的两点:精度的丢失,取值范围的越界。
2、复合赋值运算符:
上节课给大家讲解了:
int x = 0;
x = x + 1; //可以替换为 x++; 既简单又方便
那我要问:x = x + 8 呢?有什么简单的写法吗?单纯 x++ 或者 ++x 都不行啊。
这里给大家一种新的简便写法:x += 8; 就可以啦。同理,其他的还有很多,例如:
int x = 10;
x -= 2; //x的值为8
下面我把支持这类操作的运算符都给大家列出来:
+=, -=, *=, /=, %=, <<=, >>=, &=, ^=, |=
3、逗号表达式:
逗号表达式在以后的编程中也会经常遇到,其规则是:所有以逗号间隔的表达式都进行计算,各个表达式的计算顺序按照从左往右。整个表达式的值是最后一个逗号表达式的值。 例如:
int x = 0;
int y = 0;
x +=2, y+= 3; //此时X=2;Y=3;
int q = ((x++), (++y)); //此时只输出++y;但是X++也计算了。
我要问问大家了,q的值是多少呢?
4、小作业:
①、
int x = 2;
x = 3 5, x4;
经过计算之后,变量 x 的值是多少呢?
②、自学运算符的优先级:
5、C++的语句与输入输出
C++的语句与输入输出
1、语句:
① 一般语句以半角的分号 ; 结尾。
② 复合语句:把多条语句合成在一起,就是复合语句。那么如何来合成多条语句呢?可以用一对大括号 {} 来包装。例如:
{
int x = 2;
++x;
}
2、输入与输出:
咱们之前的课程中给大家进行了相关变量值的输出,使用的是 cout,那么在C++中进行打印输出一般是用输入与输出流库中的 cin 和 cout 来实现的。
cin 和 cout 的定义是在 iostream 中,命名空间为 std,所以如果我们的程序中要使用 cin 和 cout 就必须要加上以下两条语句:
#include
using namespace std;
在进行输入和输出的时候,我们经常使用 << 和 >> 符号。例如:cout << “ x+y = ” << 5 << endl;
<< 是流插入运算符;>> 是流提取运算符;
所以,cout << “ x+y = ” << 5 << endl; 这句话的意思是将字符串 "x+y = " 先传递给 cout,即:流插入,之后再把 常量 5 插入到 cout,之后 endl 结束。大家可以把 << 和 >> 看成是流的方向,看往哪个方向流,这样能更好理解一些。
如果你要输出,那么肯定是要把字符串或者变量什么的传递给 cout 所以箭头要指向 cout,如果你要进行输入,那么就要从 cin 提取内容赋值给变量:例如:
int x = 0;
cin >> x;
从标准设备输入一个整形的值给变量 x,这个标准输入设备一般指键盘。执行到这句 cin >> x; 的时候程序会卡住,等待用户的输入,输入完成后继续往下走。cin 和 cout 的 >> 和 << 会智能的判断参数的类型,不管是整形还是字符型或者是字符串型,他都可以智能识别并进行输入与输出操作。
另外,注意,不管是 cin 也好,cout 也好,<< 和 >> 一次性只能输出或者输入一个,例如以下的写法是错误的:
cout << a, b, c;
cin >> a, b, c;
cin 的分隔符一般用回车(Enter),下面演示给大家看!
备注:一般 C++ 中的 cin 和 cout 是适用于我们当前使用的控制台类型的工程,这样才能看到输入与输出的结果。以后大家接触到带界面的程序了,cin 和 cout 也就用不上了。
3、兼容C语言的输入与输出:
举例:setprecision是一个计算机函数,功能是控制输出流显示浮点数的有效数字个数 ,如果和fixed合用的话,可以控制小数点后面有几位。
//cout<<fixed<<setprecision(2)<<123.456<<endl;/如果在这个位置就加上fixed的话,后面的输出全部都按照fixed处理/
别忘了添加它的头文件:#include “iomanip”
6、if 语句实现逻辑运算与冒号表达式
if 语句实现逻辑运算与冒号表达式
1、逻辑运算符的运算
①、&& 逻辑与 相当于其他语言中的 AND
②、|| 逻辑或 相当于其他语言中的 OR
③、! 逻辑非 相当于其他语言中的 NOT
2、冒号表达式:
冒号表达式又叫条件表达式,是唯一一个三目运算符,就是需要三个参数的运算符。先举个 if 语句的例子,求出 变量 x 和 y 的最大值,并打印出来:
int max = 0;
int x = 0, y = 0;
cin >> x >> y;
if (x > y)
{
max = x;
}
else
{
max = y;
}
cout << “最大数是:” << max << endl;
求 x 和 y 中的较大数,我用了一个 if-else,那么有没有简单一点的方法呢?答案是当然有,就是要讲解的这个冒号表达式:
max = x > y ? x : y;
就这么一句话,就可以将 x 和 y 中较大的数的值赋值给 max 变量,怎么样,方便吧?
3、冒号表达式的规则:
表达式1 ? 表达式2 : 表达式3
若表达式1为真,则返回表达式2的值,若表达式1为假,则返回表达式3的值。如此而已!
7、用switch语句实现多分支选择结构
用switch语句实现多分支选择结构
1、为什么会有switch语句?
if-else语句只有两个分支,如果条件比较少的情况下可以使用if-else,可如果条件比较多,就会出现很多个 if-else if-else if-else-if 等。
这样会导致代码冗余,不易于阅读,同时如果条件很多的情况下效率也不高,因为系统要计算每一个if中的表达式去进行比较。所以在条件比较多的情况下建议使用switch语句来解决。
2、switch语句的格式:
switch(表达式)
{
case 常量表达式1:语句1
break;
case 常量表达式2:语句2
break;
case 常量表达式3:语句3
break;
…
case 常量表达式n:语句n
break;
default:语句n+1;
break;
}
格式说明:
①、switch后面括号内的表达式必须是数值类型(包括字符类型)的数据,不能使用字符串;
②、如果switch表达式的值与某一个case子句中的常量表达式的值匹配时,就执行此case子句中的内嵌语句,如果所有的case常量表达式都不匹配,那么就执行default子句的内嵌语句;
③、每个case标签的常量表达式的值必须不能相同,否则会出现错误的情况,而且以当前vs2013的编译器来说编译也会不通过的;
④、每个case语句,包括default语句的顺序不影响结果,可以任意放置,比如把default放在上面也是一样的;
⑤、switch语句也是本着从上往下的顺序执行,可这个从上往下是以第一个case匹配到的常量表达式开始,往下执行。如果执行完一个case语句之后不想继续执行其他case语句可以使用break关键字跳出switch结构即可;
3、实例题目:
键盘输入0-6之间的整数,分别打印出代表每周的星期日、星期一、…、星期六,请使用switch语句来实现。同时,如果是周六和周日的话要打印出“今天是周末”的字符串。
8、while 循环语句
while 循环语句
1、while 循环语句的格式:
while(表达式A)
语句B
其执行流程是:当表达式A的值为真(非0)时,执行下面的语句B(即循环体),否则不执行语句B。也就是先判断表达式A,后执行语句B。
备注:
①、语句B不限于一条语句,可以用 {} 括起来的多条语句,否则while语句循环体的范围只到while后面的第一个分号处结束;
②、在循环体的语句B中应有使循环趋于结束的语句,一定避免死循环现象的发生,这是个低级错误。
2、do-while 循环语句的格式:
do
语句B
while(表达式A)
跟 while 循环的格式也是大同小异。但是跟 while 有个很重要的区别:先执行循环体语句B,之后再判断表达式A
所以,do-while 循环至少会执行一次循环体语句B,而 while 循环则可能一次都不执行循环体语句B,因为条件不满足。大家明白了吗?
9、for循环语句及break和continue的作用
for循环语句及break和continue的作用
1、for循环语句的格式:
for(表达式A; 表达式B; 表达式C)
语句X
注意:表达式A、B、C 之间的分割是分号; 不是逗号啊,不要犯错!
2、用 break 语句提前结束循环过程并跳出:
之前给大家讲解 switch-case语句的时候,跟大家说过,break可以跳出当前的switch-case语句结构。同理break语句也可以使用在循环语句中,使用之后直接提前跳出当前的循环过程。break后面如果有语句也不执行。
3、用 continue 语句提前结束本次循环:
continue 语句一般用于循环语句中,作用是提前结束本次循环,即跳过循环体语句中X中尚未执行的语句,直接进行下一次的循环。
4、举例实现求出100~200之间的素数并打印出来:
#include <iostream>
using namespace std;
int main()
{
cout << "100~200之间的素数如下:" << endl;
int x = 0;
for (x = 100; x <= 200; ++x)
{
int y = 2;
for (y = 2; y < x / 2; ++y)
{
if (x % y == 0)
{
break;
}
}
if (y < x / 2) continue;
cout << x << endl;
}
cout << endl;
return 0;
}
10、水仙花数
求水仙花数
输出所有的“水仙花数”,什么是水仙花数呢?所谓的水仙花数就是指一个3位数,其各个位数字的立方和等于该数本身。立方大家都知道吧,比如2的立方就是 2x2x2=8。例如:153就是一个水仙花数,因为:
1的立方=1x1x1=1
5的立方=5x5x5=125
3的立方=3x3x3=27
他们的和就是:1+125+27=153
大家明白了吗?先自己动手实现一下啊!
11、函数简介
函数简介
1、定义函数的形式:
返回类型 函数名(形式参数表列)
{
声明部分;
执行语句;
}
例如:
void print_msg() //没有参数,没有返回值
{
cout<<"hello world." << endl;
}
int add(int x, int y) //有两个参数,有返回值
{
return x+y;
}
2、函数的简单调用:
#include <iostream>
using namespace std;
int add(int x, int y)
{
return x + y;
}
int main()
{
int sum = add(2,5);
cout << "sum = " << sum << endl;
return 0;
}
3、形参与实参:
在上面的例子中 add 函数的参数 int x, int y 就是形式参数,简称形参,又叫虚拟参数,因为函数调用之前他们并没有申请任何内存;
而在调用 add 函数的实际参数 2 和 5 就是实际参数;
4、函数的前置声明
5、递归调用
11、局部变量和全局变量
局部变量和全局变量
1、多文件共同访问一个全局变量:
①、在其中一个C/Cpp文件中定义并初始化全局变量,例如:
int sum = 0;
②、在C/Cpp文件都能访问到的一个h头文件中声明该全局变量为支持多文件访问的:
extern int sum; //此时只是声明,所以不需要初始化
③、在其他C/Cpp文件中 #include 这个头文件之后,就可以直接使用这个全局变量 sum 了。
备注:不能在 h 文件中直接定义这个全局变量 int sum = 0; 之前看到很多网友都是这么做的,这样的话如果有多个C/Cpp文件 include 包含这个头文件的时候就会提示 sum 重复定义了。所以一定要在 C/Cpp文件中定义该全局变量,之后在 h 头文件中声明该全局变量才行哦。
2、static 静态类型变量:
static 静态类型局部变量只初始化一次,之后的调用都不进行初始化!
12、数组与一维数组
数组与一维数组
1、如何定义数组?
上面已经粗略的给大家定义了一个学号的数组:int stu_no[60];
其中 int 就是这个数组中所有元素的类型,stu_no 就是数组的名字,以后访问数组中任何一个元素都得靠他,60就是这个数组中元素的个数。
所以数组的基本定义格式如下:
类型名 数组名[常量表达式];
注意,中括号内部的常量表达式用以表示数组中元素的个数,这个必须是个常量或常量表达式,不能是个变量或者一个不确定的值,不然编译阶段就直接报错了。因为定义好了之后,系统马上就根据这个个数来分配空间了。如果是个变量,系统就不知道要分配多少空间。
数组名字的命名规则跟变量是一样的,只能使用数字、字母、下划线,而且数字不能做开头。
2、如何访问数组中的元素:
数组必须先定义之后再使用,需要通过下标来访问,具体的访问格式如下:
数组名[元素下标];
这个元素的下标是从0开始的,范围是0~个数-1,例如:int stu_no[60]; 那么下标就是 0~59,大家明白了吗?
备注:这个下标可以是常量也可以是变量,但必须在规定的范围内访问,不然会出现访问越界,导致程序崩溃的重大低级问题。
3、一维数组的初始化:
①、在定义数组的时候就对数组的全部元素初始化:
int stu_no[5] = {101, 102, 103, 104, 105};
②、在定义数组的时候对部分元素进行初始化:
int stu_no[5] = {101, 102};
后面未初始化的默认赋初值0
③、在定义数组的时候就对数组的全部元素初始化,可以不指定数组长度:
int stu_no[] = {101, 102, 103, 104, 105};
不管是定义的时候指定长度还是不指定长度,总结起来就一个规则,定义的时候让系统知道数组的长度就可以了,也就是定义即确定大小。养成一个好习惯初始化数组:int stu_no[5] = {0};
4、案例
不太好的写法:
正常写法:
总结:如果数组里面没有0,自己又初始化了0,那么结果是最小值是0;
5、小作业
一整形数组中有10个数,分别是:5,8,9,0,2,1,4,7,6,3;
对数组进行排序,并输出排序后的数组内容。
自己写的:
小感悟:每轮对比,一定能把最小的找到,所以,从后往前一个个确定;
别人写的:
C++排序算法代码汇总
12、字符数组
字符数组
题目:字符数组定义如下:
char szbuf[100] = “hello, friends, my name is cctry.com. what is your name ?”;
复制代码
遍历字符数组 szbuf,将其中的字符 i 替换成 @ 符号,并统计其个数。最后将统计的个数及整个字符串的内容都输出出来?
大家试试吧!
13、使用字符串处理函数操作字符数组
使用字符串处理函数操作字符数组
1、目的:
之前也跟大家说过字符串在以后的编程过程中会非常频繁的用到,所以C/C++语言为了提升开发效率,本身提供了很多对字符串进行操作的函数,不用大家自己再实现。他们已经成为C/C++的标准,所以任何一个支持C/C++标准的编译器都支持这些函数的。他们被包含在 string.h 或 string 头文件中。所以要在代码中添加:
#include <string.h> //C语法
或者
#include //C++语法
2、字符串连接函数:strcat
该函数的定义原型为:
char * strcat (char destination[], const char source[]);
相关的说明在这里:http://www.cplusplus.com/reference/cstring/strcat/
其作用就是将第二个参数的字符串连接到第一个参数的字符串结尾,所以要保证第一个参数的字符数组大小够用,能装的下第1个和第2个字符串的总长度才行,不然就会发生内存溢出啦!
返回值是第一个字符串的首地址,关于地址这个话题在下几节课会给大家讲解的。
代码举例:
char des[50] = "hello "; //保证des的空间足够大
char src[] = “cctry.com”;
strcat(des, src);
cout << "des = " << des << endl;
问大家个问题,这里面des字符数组的大小最小能定义成多少?15?16?17?为什么呢?
3、字符串拷贝函数:strcpy
该函数的定义原型为:
char * strcpy( char destination[], const char source[]);
相关的说明在这里:http://www.cplusplus.com/reference/cstring/strcpy/
其作用就是将第2个参数的字符串拷贝到第一个参数的字符数组中,所以要保证第1个参数的字符数组大小够用。注意:第2个参数的结束符 ‘\0’ 也会拷贝过去哦。
返回值是第一个字符串的首地址,关于地址这个话题在下几节课会给大家讲解的。
代码举例:
char des[50] = {0};
char src[] = “cctry.com”;
strcpy(des, src);
cout << "des = " << des << endl;
问大家个问题,这里面des字符数组的大小最小能定义成多少?为什么呢?
4、字符串比较函数:strcmp
该函数的定义原型为:
int strcmp (const char str1[], const char str2[]);
相关的说明在这里:http://www.cplusplus.com/reference/cstring/strcmp/
其作用就是对比第1个和第2个参数的字符数组字符串,逐个字母比对,直到字符串结束。即比较每个字母的ASCII码值。
当第1个参数大于第2个参数,返回 > 0 的数,当第1个参数小于第2个参数,返回 < 0 的数,当第1个参数和第2个参数相等,返回0
代码举例:
char des[50] = “hello”;
char src[] = “cctry.com”;
int iret = strcmp(des, src);
cout << "iret = " << iret << endl;
5、字符串求长度函数:strlen
该函数的定义原型为:
size_t strlen (const char str[]);
相关说明在这里:http://www.cplusplus.com/reference/cstring/strlen/
其作用就是求得参数字符串的长度,通过返回值返回。
代码举例:
char des[50] = “hello”;
int len = strlen(des);
cout << "len = " << len << endl;
这里面 len 的值是5,而不是50,为什么呢?50是des中总共能容得下的字符的个数,而不是实际字符串的长度。
所以这里面一个字符串占用的字节数和字符串的长度是两个不同的概念。
6、字符串的长度和占用字节数的区别:
char des[50] = “hello”;
这个字符数组里面存的是一个字符串 hello,那么这个des字符数组所包含的字符串长度是:strlen(des)
那么,这个字符数组所占用的字节数呢?怎么求?sizeof(des)
int des[50];
sizeof(des) = ?
7、小作业:
不用系统提供的strcat函数,自己使用字符数组编写一个函数,实现两个字符串的连接功能。
14、地址与指针
地址与指针
&符号是取地址符号,&a 就是取变量a的地址。
1、变量与指针:
在C/C++语言中可以通过取地址符号&得到变量的地址,例如:
int a = 5;
int* pa = &a;
那么,通过变量的地址,能否得到变量自身呢?答案是可以的!
int a = 5;
int* pa = &a;
pa = 6;
即:在指针变量的前面加上一个就能得到指针指向的变量自身。
所以对一个变量的修改,既可以通过该变量自身(直接修改),也可以通过指针/地址来实现修改(间接修改)。
2、指针变量的定义及初始化:
①、指针变量的定义格式如下:基类型 * 指针变量名;
②、符号* 既可以靠近基类型,也可以靠近指针变量名,例如:
int* p; 和 int *p; 都是正确的。
③、指针变量可以在定义的时候就初始化,也可以先定义后初始化,也可以在以后的任意一个时间去指向某个变量的地址:
int a = 5;
int *pa;
pa = &a;
int *pb = &a;
④、基类型就是该指针变量指向的变量的类型。例如:
int* pa; 这个指针变量的定义,就是定义了一个指向int类型的指针变量pa;你就不能把一个float类型的变量地址赋给他。例如:
int* pa;
float a = 2.6;
pa = &a;
这种写法是错误的。
⑤、指针变量可以指向同一类型的变量,例如:
int a = 5, b = 6;
int *p = &a;
p = &b;
即:指针变量p既可以指向变量a的地址,也可以指向变量b的地址。
输出还是X=5;Y=6;
方法一:指针:
方法二:引用:
输出:X=6;Y=5;
小感悟:&取地址,相当于告诉别人你家位置;*指针,相当于万能钥匙,就差一个你家地址,告诉别人后,他们就能看到你家的数值是多少,甚至懒得看,把你家拆了重建;
15、数组与指针
数组与指针
这是为什么?????
小作业:
输入一个字符串,例如:
a123x456__17960?302ab5876
将其中连续的数字作为一个整数,依次存放到一个数组中a中,例如:123放在a[0]中,456放在a[1]中。统计共有多少个整数,并输出这些整数。
自己敲的代码:
#include <iostream>
#include<cstring>
using namespace std;
int main()
{
char string[28] = { "a123x456__17960 ? 302ab5876" };
int a[30] = { 0 };
char b[30] = { 0 };
int y = 0;
int z = 0;
for (int i = 0; i < 28; i++)
{
if ((string[i] >= 48) && (string[i] <= 57))//将数字挑出来
{
b[y] = string[i];
y++;
}
else
{
if (strlen(b) != 0)
{
int num = atoi(b);//将字符串变为整数
a[z] = num;
z++;
y = 0;
memset(b, '\0', strlen(b));//将数组清空
}
else
{
continue;
}
}
}
cout << "一共有" << z << "个整数。" << endl;
for (int t = 0; t < z; t++)
{
cout << "a[" << t << "] = " << a[t] << endl;
}
return 0;
}
16、结构体类型
结构体类型
1、自定义数据类型:
C/C++语言本身提供了很多基本数据类型,例如:int、float、char 等供我们使用。但是程序编写的过程中问题往往比较复杂,基本的数据类型有时候不能满足我们的需求,所以C/C++语言允许开发者根据自己的需要自定义数据类型,接下来要讲解的结构体struct、联合体union、枚举类型enum,类类型class 等就是用户自定义的数据类型。这些用户自定义的数据类型跟C/C++语言提供的基本类型一样,都可以用来定义变量。只不过在这些自定义数据类型在使用之前要先由用户声明出来才行。
2、定义结构体的必要性:
之前有给大家举过学生的例子,这里接着来说下。例如一个学生的信息包括:姓名,学号,性别,年龄 等等。按照我们之前的做法,可以使用数组来定义:
string name[100]; //姓名
int num[100]; //学号
char sex[100]; //性别
int age[100]; //年龄
这样虽然也能满足需求,但是比较麻烦,如果想获得一个学生的信息需要从4个数组中分别找到该学生的所有信息,而且没有什么关联性,容易乱,能不能把一个学生的信息都统一到一起呢?把他们当做一个组合项,在一个组合项中包含若干个类型的数据项。C/C++语言允许用户自己定义这样的数据类型,这样的类型就称作结构体。
例如:
struct Student
{
string name;
int num;
char sex;
int age;
};
这样就声明了一个结构体类型 Student,struct 是结构体类型的关键字,不能省略。
3、结构体类型的声明:
struct 结构体类型名
{
//成员表;
};
struct 是声明该类型为结构体类型的关键字,不能省略。结构体类型名就是该结构体类型的名字,以后可以直接拿这个类型名来定义变量,就跟使用int,double一样用。类型名的命名规则跟变量一样,可以是数字、字母、下划线,且数字不能开头。上面例子中的 Student 就是结构体的类型名。接下来的一对大括号内的成员表包含了该结构体中的全部成员。上例中的 name、num、sex、age 都是结构体中的成员。在声明一个结构体类型时必须对各成员进行类型声明,即:
类型名 成员名;
例如:int num;
备注:C语言中结构体的成员只能是数据,C++对此进行了扩充,结构体的成员既可以包含数据,也可以包含函数,其实在C++中 struct 跟 class 从使用角度来说差别不大,这个以后在讲解面向对象时候的class时再跟大家详细的讲解!
4、结构体类型变量的定义及初始化:
A、定义:
结构体类型声明完了之后就可以定义变量了,如下:
Student zhangsan, lisi;
这种是非常常用的一种定义结构体类型变量的方法。
当然也可以在声明结构体类型的时候就定义变量,当然这种是不常用的方法:
struct Student
{
string name;
int num;
char sex;
int age;
} zhangsan, lisi;
B、初始化:
Student zhangsan = {“张三”, 1001, ‘m’, 25};
备注:初始化参数的顺序一定要和结构体类型声明的成员表顺序一致才行,不然会报错而且会错误的赋值。
5、结构体类型变量成员的访问:
一定要初始化,再调用
结构体变量名.成员名
可以用这种方式来访问。
例如:
Student zhangsan = {“张三”, 1001, ‘m’, 25};
zhangsan.num = 29;
int num = zhangsan.num;
17、结构体数组与指针
结构体数组与指针
1、结构体类型和数组配合使用:
2、指向结构体变量的指针:
结构体变量指针的作用在上面的知识点中已经说的够详细的了,下面就给大家演示下如何来用!
之前给大家说过,结构体类型变量引用其成员的时候可以使用符号 . 来引用,例如:
Student stu;
stu.num = 102;
但如果是指针类型呢?我们可以这样做:
Student stu;
Student* pstu = &stu;
(*pstu).num = 102;
没问题吧?pstu是指向stu变量的指针,所以前面加上符号*就变成stu变量本身了。变成 stu本身了之后再用 . 来引用就可以了。
但是这么做可以说代码量少还可以,如果代码量比较多,写起来比较麻烦。所以C/C++中规定结构体类型的指针变量可以用 -> 符号来引用其成员,即如下:
Student stu;
Student* pstu = &stu;
pstu->num = 102;
这样写起来就简单了。就是把 . 换成 -> 就可以了,方便吧?
18、枚举类型及定义新的类型名字
枚举类型及定义新的类型名字
1、枚举类型:
如果一个变量只能有几种可能的值,这样的变量可以定义成枚举类型。所谓的 “枚举” 是指可以将变量的值一一列举出来,变量的值只能在列举出来的值的范围内,其他的值对该变量没有意义,例如:星期几、人种颜色、性别、月份 等等。枚举类型也是一种自定义类型。
2、枚举类型的声明:
声明枚举类型用 enum 开头,例如:
enum ESex
{
ESex_Male, //男性
ESex_FMale //女性
};
以上就是定义了一个枚举类型 ESex,大括号内部的 ESex_Male、ESex_FMale 称为枚举元素或枚举常量。表示这个枚举类型可能的值。
注意事项:
①、枚举元素按常量处理,所以称作枚举常量。他们不是变量,所以不要对他们进行赋值,即枚举元素的值是固定的;
例如:ESex_Male = 8; 这种是错误的,大家可以在vs2013中试试;
②、枚举元素是常量,所以其也是有值的,他们的值是一个整数,按照元素声明时候的顺序从0开始依次进行+1操作,默认的值就是:0,1,2,3,…
例如,上面的ESex枚举类型中,ESex_Male 的值默认是0,ESex_FMale 的默认值是1,依此类推。
③、枚举元素有默认的值,但也可以在声明的时候指定值,例如:
enum EWeekDay
{
EWeekDay_1 = 3,
EWeekDay_2 = 4,
EWeekDay_3 = 5,
EWeekDay_4,
EWeekDay_5,
EWeekDay_6,
EWeekDay_7,
};
其中从 EWeekDay_4 开始未赋值,所以按照他的上一个元素的值+1的规则进行默认赋值,也就是 EWeekDay_3 + 1 = 6。
这里面有个注意事项,即,上面赋值的最好是依次增大,不然有可能会造成两个枚举元素是一样的值,例如:
enum EWeekDay
{
EWeekDay_1 = 3,
EWeekDay_2 = 2,
EWeekDay_3 = 1,
EWeekDay_4,
EWeekDay_5,
EWeekDay_6,
EWeekDay_7,
};
④、枚举值可以用来进行跟整数一样的判断,比较,switch-case 等操作,例如:
int ab = 2;
if(EWeekDay_1 > ab )
{
//…
}
⑤、虽然枚举类型的变量可以看做是整形类型的变量,但是不能把普通的整数赋值给枚举类型变量,例如:
EWeekDay day = 2;
这种是错误的,除非进行强制类型转换,但是不建议。最好还是:
EWeekDay day = EWeekDay_2;
3、枚举类型举例:枚举类型和结构体类型结合:
struct Student
{
string name;
int num;
ESex sex;
int age;
};
Student stu;
stu.sex = ESex_Male;
4、用 typedef 类型声明新的类型名字:
除了可以用 struct 结构体,union 联合体,enum 枚举 等自定义类型以外,还可以使用 typedef 声明一个新的类型名字来代替已有的类型名。注意是新的类型名字,只是名字而已,不是一种全新的类型,只是改个名字而已。
例如,我们定义一个无符号的整型int变量可以这样来定义:unsigned int a = 5;
类型的名字比较长,unsigned int,而且以后所有定义无符号的整型int变量都得这么写,那么有没有简单的写法呢,typedef就派上用场了,咱们可以给 unsigned int 改个名字,例如:
typedef unsigned int uint;
uint a = 5;
所以,以后所有的 unsigned int 都可以改成 uint 了,方便吧?同理,其他的类型也都可以使用 typedef 改名,例如:
typedef int myint;
typedef unsigned long ulong;
typedef Student StuT;
typedef EWeekDay EWDay;
以上,都是可以的哦!
5、typedef与宏定义:
typedef详解以及与宏定义#define的区别
19、引用及new和delete的使用
引用及new和delete的使用
1、何为变量的引用?
通过之前对指针的讲解,相信大家对于指针来说都比较熟悉了,指针里面存的是某个变量的地址。那么这节课给大家讲解一下变量的引用,跟指针有点像。他是C++对于C语言的一个重要的扩充。C语言中没有引用,C++有引用,而且C++中更建议大家多用引用少用指针。
变量的引用就是一个变量的别名,变量和变量的引用代表着同一个变量。 例如:
int a = 5; //语句1
int& b = a; //语句2
int* p = &a; //语句3
这里面a是一个普通的int类型变量,b呢,就是变量a的一个引用,p呢就是指向变量a地址的一个指针变量。
其中语句2中的 & 符号是引用的声明符号,不是取地址哦,语句3中的 & 符号确实是取地址符。
如何来区分呢?大家记住:紧跟在数据类型后面的&符号就是引用的声明符号,其他情况都可以认为是取地址符号。
2、引用的注意事项:
①、引用不是一种独立的数据类型,引用只有声明,没有定义。必须先定义一个变量,之后对该变量建立一个引用。也就是说有变量才有变量的引用,不可能先声明一个引用而不去引用任何变量,这点跟指针不同,指针可以先声明,之后的任意时刻指向某个变量的地址,引用就不是;
例如:int &b; //先声明定义一个引用是错误的
②、声明一个引用时,必须同时对其初始化,即声明该引用代表哪一个变量。这个跟第①点要表达的意思一样。有一种例外的情况,当一个函数的参数是某个变量的引用时,形参不必在声明中初始化,他的初始化是在函数调用时的虚实结合实现的,即作为形参的引用是实参的别名;
void swap(int& a, int& b);
③、声明一个引用后,不能再让其作为另一个变量的引用了。例如:
int a1 = 2, a2 = 5;
int& b = a1; //正确
int& b = a2; //错误
④、不能建立引用数组,例如:
int a[5] = {0};
int& b[5] = a; //错误
int& c = a[0]; //正确(C++新标准支持)
⑤、可以建立引用的引用(C++新标准支持),也可以建立引用的指针,例如:
int a = 3;
int& b = a; //正确
int& c = b; //正确
int* p = &b; //正确,得到的是变量a的地址
*p = 5;
c = 6;
3、使用new和delete动态分配内存:
在以后的开发过程中,因为局部变量的局限性,只能在其作用域内使用。因此,我们需要动态的分配和撤销内存空间,使其可以在任何函数中使用。例如:
char* get_same_string(char* p1, char* p2)
{
//
}
get_same_string 函数的作用是从参数p1和p2中找出相同的部分,例如,p1的内容是:“aabbcc”,p2的内容是:“kkbcyy”,他们相同的子串就是 “bc” 对吧?我想把这个结果通过函数的返回值给传出去。所以函数的返回值是一个 char* 类型,如果在函数中定义一个局部变量 szret[100] 数组,用这个数组来存储相同部分的子串 “bc”,那么就不能返回,为什么呢?因为 szret 是局部变量,作用域只是在函数的内部,超过函数的作用域之后 szret 的内存就可能被释放了。所以用它来返回之后,在函数的外部再去使用是非常不安全的,也是错误的。所以这种情况就可以使用 new 动态分配内存来解决。
4、new 出来的变量/内存的生命周期:
C++ 中的 new操作符 和C语言中的 malloc 函数类似,如果你不主动 delete 掉这段申请的内存的话,它会一直存在,直到进程结束后系统会回收掉这段资源;而如果你delete掉这段申请的内存,则这段申请到的内存的生命周期为从你new(申请一段内存)到你delete(释放掉这段内存)这段时间。