C++基础
1:头文件
1.0:头文件:其中有带h和没有带h后缀的,带h后缀的是老版本的编译器,没带是新版本的编译器。
#include <limits.h> #最大最小要导入这个
#include <ctype.h> #字符处理函数功能
#define _CRT_SECURE_NO_WARNINGS
#include <iostream> #用cout打印
1.1:头文件iostream
:如果程序使用输入或输出工具,一定要提供下面这两行代码:
#include <iostream>
using namespace std;
iostream是C++标准库中的一个头文件
提供了输入输出流的支持,包括了cin、cout、cerr、clog等对象以及相应的操作符<<和>>等。通过引入iostream头文件,可以使用C++中的输入输出流机制,使得程序的输入输出更加方便、灵活,也更符合面向对象的编程思想。可以通过以下代码引入iostream头文件:
#include <iostream>
在程序中使用cout输出信息的示例:
#include <iostream>
using namespace std;
int main() {
cout << "Hello World!" << endl;
return 0;
}
其中,cout代表标准输出流,<<代表插入操作,将数据流插入到输出流中,endl代表换行操作。以上程序会输出一行字符串"Hello World!"。
#include<iostream>
:该编程指令导致预处理器将iostream
文件内容添加到程序中,这个是一种典型的预处理操作,在原代码编译之前,替换或添加文本
1.2:问题解答
1.21:为什么将iostream
文件的内容添加到程序中?
答:涉及到程序与外部世界之间的通信,
iostream中的
io指的就是输入(进入程序的信息)和输出(从程序中发出的信息
)
解释:c++中输入和输出方案,涉及
iostream文件中的多个定义,为了使用
cout来显示消息,第一个程序需要这些定义,
include编译指令导致
iostream文件的内容一起被发送给编译器,
iostream中的内容没有被修改,而是将源代码文件和
iostream组合成一个符合文件,编译的下一阶段
1.22:using namespace std
有什么作用:
在 C++ 中,using namespace std; 是一个编译指令,用来告诉编译器在代码中使用 std 命名空间中的标识符时不需要加上前缀 std::,直接使用即可。
例如,如果不使用 using namespace std;,则需要使用 std::cout 来输出内容:
#include <iostream>
int main() {
std::cout << "Hello, world!" << std::endl;
return 0;
}
而使用 using namespace std; 后,则可以直接使用 cout:
#include <iostream>
using namespace std;
int main() {
cout << "Hello, world!" << endl;
return 0;
}
需要注意的是,对于大型项目或者需要避免命名冲突的情况,建议不要在头文件中使用 using namespace std;,而是在源文件中使用。同时,其他命名空间中的标识符也应该使用对应的命名空间前缀来调用。
1.3:函数解析:
using
:叫做编译指令,名称空间,在编译器执行编译的时候,不知道是那个版本的,名称空间的名称用来指哪个版本的产品,按照这种方式,类,函数,变量便是c++编译器的标准组件,现在都被放置在名称空间的std中。仅当头文件没有扩展名h时,情况才如此。意味着,在iostream
中定义用于输出的cout变量上是:std::cout,endl是std::endl,可以省略编译指令using,以下述方式进行编码。
cin
:输入符
cout
:输出符:默认是以10进制格式显示整数的
cout.put()
:输出符:通过类对象cout
来使用函数put()
,是另一种显示字符的方法,可以代替<<运算符.
endl
:控制符,在输出流中插入endl
将屏幕光标移到下一行,
\n
:换行符:通常在字符串中运用,显示字符串时,字符串包含了换行符,而不是在末尾加endl
,可以减少输出量
int carrots
:变量:首先用int,确定数据类型,carrots:为变量,在c++中变量都必须声明。
2:函数:函数分为两种,有返回值的和没有返回值的,
1:平方根函数:sqrt()
要定义头文件,平方根的头文件为:include <cmath>
,使用平方根函数,要定义一个数据类型平方根的返回值double
#include<iostream>
using namespace std;
#include <cmath>;
int main() {
//变量的定义
//语法:数据类型 变量名 = 初始值
int carrots;
cout << "hew many" << endl;
cin >> carrots;
carrots = carrots + 1;
cout << "now you" << carrots << endl;
//平方根的返回值数据类型定义
double area;
double side;
//输入
cin >> area;
//使用sqrt函数
side = sqrt(area);
//输出
cout << side << endl;
//结束返回值
return 0;
}
LNK开头的是连接错误
3:数值类型
int类型最大最小值
INT_MIN :最小值
INT_MAX :最大值
unsigned int 类型最大是:
UINT_MAX
long类型的最大最小
LONG_MIN
LONG_MAX
无符号的long类型大小
ULONG_MAX
long long 类型大小
LLONG_MIN
LLONG_MAX
无符号类型
ULLONG_MAX
整型:short
,int
,long
,long long
,这些整型是从小到大进行排序的。
存储值:
short
:至少16位
int
:int至少与short
一样长
long
:至少32为,且至少与int
一样长
long long
:至少64位,且至少与long
一样长
无符号整型:就是值不能为负数,整型前面加上unsigned
如:unsigned short
等等.
输出符,进制的修改
cout
:输出符:默认是以10进制格式显示整数的
使用控制符可以修改输出cout
函数的进制,控制符为:dec:修改十进制
,hex:修改十六进制
,oct:修改八进制
cout
默认是以10进制格式显示整数的,改成十六进制的,代码如下
cout << hex;
cout << 十六进制的变量 << endl;
char
类型:字符和小整数,足够长,能够表示目标计算机系统中的所有基本符号,所有的字母,数字,标点符号等,c++将常量存储为char
类型
常量函数:const
,让数值固定死。
浮点数类型:float
,double
,long double
,在默认情况下是双进度的:double
类型
float
:至少32位
double
:至少48位
long double
:为80,96,或128
定点模式:写入到函数体中,cout.setf(ios_base::fixed,ios_base::floatfield);
精确显示如是float,精度到后面的6位
数组
创建一个数组 数组的类型 数组的变量[ 多少个数组]
创建数组及赋值 int 变量[3] = {3个自定义元素}
不可以这样赋值,如下列:
int hend[4] = {5,6,7,8};
:定义完成
hand = heand
:不能定义后,在进行赋值。
hend[4] = {5,6,8,9}
:也不能定义以后在fu
显示类型的内存大小函数
sizeof("%对应的占位符号",&对应的变量)
计算数组里面多少个元素
列如:short things[] = {1,5,3,8};
int num_elememts = sizeof things / sizeof (short);
数组的初始化
如:
unsigned int counts[10] = {};
全部初始化为0。
float balances[100] {};
全部初始化为0。
数组初始化禁止缩窄转换,要类型一样的。
如:long plifs[] = {25,92,3.0};
这里的3.0是浮点数,转换成整数是要缩窄转换的,即使浮点数后面小数点为0,也不能通过编译。
字符串
字符 : ’ '(单引号是一个字符,如果要赋值成数组后面要加上 /0 其中 /0 为空字符),
字符串: " "(双引号是一个字符串是默认带 /0 的):其实双引号代表着的是一个地址。
strlen
:字符串的有效长度,这里是指,,如果设置为15个长度数组,但是只用到了4个有效数组这个函数就只返回4个字节
strlen(字符串变量)
字符串复制:strcpy(变量1,变量2):对变量复制
字符串的拼接:strcat():字符串的拼接
struct inflatable:结构体
struct inflatable
{
char name[20];
float volume;
double price;
}
创建枚举量,枚举赋值必须是整型,int或long或long long
enum spectrum{red,orange,yellow,green,blue,violet,indigo,ultraviolet};
括号内的是枚举量
spectrum :枚举类
spectrum band; :通过枚举类创建枚举变量
枚举的取值范围如下,首先,
要找到上限,需要知道枚举的最大值,找到小于这个最大值的,最小的2的幂,将它减去1,得到的便是取值范围的上限。
要找到下限:需要知道枚举量的最小值,如果它不小于0,则取值范围的下限为0,否则采用与寻找上限方式相同的方式,但加上负号,列如,如果最小的枚举量为-6,而比他小的,最大的2的幂是-8,因此下限为-7
上限:
下限:
4:指针
C++中的指针是一种特殊的数据类型,它存储了一个变量的内存地址。通过指针,程序可以对变量的值和地址进行操作,使得程序具有更高的灵活性和效率。
在C++中,定义一个指针需要使用 * 符号。例如:
int* ptr;
上述代码定义了一个整型指针 ptr。
指针可以通过取地址符 & 来获取变量的地址,例如:
int num = 10;
int* ptr = #
上述代码定义了一个整型变量 num,并且通过指针变量 ptr 获取了 num 的地址。此时,ptr 存储的值就是 num 的地址。
指针可以通过解引用符 * 来获取指针所指向的变量的值,例如:
int num = 10;
int* ptr = #
cout << *ptr; // 输出:10
上述代码通过解引用符 * 获取了 ptr 所指向的 num 变量的值,即输出了 10。
指针还可以用于动态内存分配,例如:
int* arr = new int[10]; // 动态分配长度为 10 的整型数组
上述代码使用了 new 操作符动态分配了一个长度为 10 的整型数组,并将数组的首地址赋给了指针变量 arr。此时,arr 就可以像普通数组一样使用。
需要注意的是,指针也可以为空,表示指向的地址为 NULL。在使用指针时需要注意空指针的情况。
指针:int* p_updates
:创建一个int*类型的p_updates指针,带 int*
符号的就是创建int类型的指针
&updates:前面带个&符号在写上变量名:打印出来的是内存地址
p_updates = &updates:将内存地址赋值到指针变量中
*p_updates :打印出来的是&updates地址里面的值。
5:new用法
在C++中,new
是一个操作符,用于在堆上动态分配内存空间。它的基本语法为:
指针变量 = new 数据类型;
其中,指针变量
是一个指向已分配内存空间的指针,数据类型
是要分配的数据类型。
例如,如果想动态分配
一个整型变量,可以使用以下语句:
int *p = new int;
这样就在堆上动态分配了一个int型变量,并将其地址赋给了指针变量p。
另外,new
操作符还可以用于动态分配数组。例如,如果想动态分配一个大小为10的整型数组,可以使用以下语句:
int *p = new int[10];
这样就在堆上动态分配了一个大小为10的int型数组,并将其首元素的地址赋给了指针变量p。
使用完new
分配的内存后,应该使用delete
操作符来释放这些内存,避免内存泄漏。delete
的使用方法为:
delete 指针变量;
例如,如果要释放前面所分配的整型变量内存,可以使用以下语句:
delete p;
如果要释放前面所分配的整型数组内存,可以使用以下语句:
delete [] p;
使用new来动态的分配内存的操作符:是分配的内存块,
new可以在运行时动态地申请内存来构造一个对象或一组对象,这些对象的生命周期由程序员控制。
当使用new时一定要使用下面的释放内存的delete,否则会内存泄漏。
new 数据类型;
new 数据类型[数组大小];
typeName * pointer_name = new typeName ;
释放掉new开辟的内存空间delete
delete 内存块的指针;
释放的是指针指向的内存,不是删除指针本身。
在C++中,使用new动态地分配内存空间,程序员需要手动释放这些内存空间以避免内存泄漏。可以使用delete表达式来释放由new运算符开辟的内存空间。
delete
有两种形式,一种是释放单个对象的内存,另一种是释放一个对象数组的内存。具体用法如下:
例如:
1:释放单个对象的内存
:
int* ptr = new int; //分配一个int类型的内存空间
*ptr = 10; //给这个内存空间赋值
delete ptr; //释放这个内存空间
2:释放一个对象数组的内存
:
// 分配内存
int* arr = new int[10];
// 使用内存
// ...
// 释放内存
delete[] arr;
当delete表达式执行时,它会先调用该空间中的析构函数,然后将空间返回给操作系统。如果指针不是通过new分配的,则不能使用delete来释放它,否则会导致未定义的行为。
6:
窗口一直执行
system("pause");
代码块,方法
定义方法
void test()
输入阻塞函数
scanf("%hd",&变量)
&变量:取变量的地址
从键盘上输入给变量赋值:cin
,getline()
输入头文件 :#include <iostream>
cin >> 变量 :这个面向字符读取,如果遇到空格或者换行符的话就会终止
cin.getline()
:输入的字符要比指定的字符多就会getline和get将把剩余的字符留到输入的列表中,而getline()会设置失效位
cin.getline() 这个面向行读取,遇到空格也不会终止
这里getline():有两个函数,第一个是字符的变量,第二个是字符变量的上限
cin.get()
:get():在读取空行会后将设置失效位
cin.get()
也有两个参数 ,第一个是字符的变量,第二个是字符变量的上限 ,面向行的输入,但是还有一个换行符,在缓存区里面
再使用使用cin.get
:就可以在换行符进行了捕获了
可以连续使用cin.getline().getline()
:getline()括号内写上,变量和字符的上限值,但是get()的是不行的,因为第一个get读取了缓存区的字符,但是回车换行符还再缓存区中当,连续再点get时读的是回车换行符,缓存区就失效
如果失效了,在进行开启捕获位的话使用cin.clear();
创建结构体:struct 变量 {}
输出函数
printf()
显示类型的内存大小函数
sizeof("%对应的占位符号",&对应的变量)
sizeof 变量名
类型
数据类型不一样,首先转换成一样的类型再进行计算
unsigned :无符号类型也就是不为负数,最小值为0
无符号是u%打印
代表着10进制无符号
字符语法
char
占位符为%c
内存大小为1个字节
字符存储的时候存储的是一个数字这里有一个ascii码表,每个字符都对应着数字对字符代表着
字符串
char[] = "";
增加头文件
#include <string>
string 变量名 = ""; :`string` :能自动调节所需的内存大小
布尔数据类型
bool
bool 变量名= true; 真
bool 变量名 = false; 假
所存空间为1
short :有符号的类型,最大到了32768多左右 ,短整型
占位符号: %hd\n
unsigned short :无符号
占位符号: %hu\n
内存占2个字节
int :数值类型 ,整型,
unsigned int :无符号的整型
占位符号:用%d
内存大小为4个字符
long :长整型
unsigned long :无符号的长整型
占位符号:用%ld
内存大小为 4或者8,在gcc编译器中确定就是占用8个字符,32位的4个字节,64位的8个字节
long long
unsigned long long :无符号long类型
占位符号为%lld
内存大小为8个字符
float :浮点型
精确到6位
占用4个字节
double 小数双精度
精确到15位
内存占用8个字节
字符常量: '' ,单引号
字符串常量: "" ,双引号
宏常量: #define 变量名 定义上方
修饰变量(也称为常量): const
\t :中间形成个空格,形成对其方式
字符转换
toupper() :将字符转换成大写
tolower() :将字符转换成小写
toascii() :将字符转换成ascii 码
进制转换
十进制:逢十进一,0-9 ,正常以数字1-9开头 ,%d :源码占位
二进制:逢二进一,0-1 ,c语言不能直接行书写2进制进
八进制:逢八近一,0-7 ,以数值0开头 ,%o :补码占位
十六进制:逢十六进一,0-F ,以0x开头,如0x123 ,%n :补码占位
十进制转二进制
十进制转八进制
十进制转十六进制
小数转二进制
在有符号开头中的第一个位经常表示正负
1表示为负
0表示为正
反码
源码:正数的源码 等于反码等于补码
在计算过程中正码计算是有错误的
反码:在正数的反码是不变的,负数的反码(符号位不变,其他位取反),如果计算机用反码进行存储,负数运算结果是正确的,但是0的状态还是有两种
补码:正数的补码不变,负数的补码等于反码加1,如果计算机用补码去存储,负数运算结果是正确的,0的状态只有一种
补码求源码
补码求反码:符号位不变,其他位取反
反码求源码:反码加1
变量赋值时
在变量赋值时,赋值是十进制,给的是源码,如果赋值时给的八进制或者十六进制给的就是补码
在打印的时候,用十进制打印,就是用的源码,如果是八进制和十六进制,用的就是补码。
在无符号的情况下对其打印如果是%d的需要进行转码
因为打印%d的时候变成了有符号的数
进制转换
char bin[128]={0}; :char bin[128]申请指定空间内存。={0}将128字节的全部清空
_ltoa(要转换的变量,转换地址,转换几进制) :进制转换
小数转换成二进制
运算符
赋值运算
逻辑运算符
判断语句
if后面不能加这个分号符号 : (;)
if (变量名> 条件)
{
过了条件运行;
//嵌套if
if(变量名> 条件)
{
}
}
else if
{
过了条件运行;
}
else
{
过了条件运行;
}
三木运算符
C++中的三目运算符(也称条件运算符)是一种简单的条件语句,用于根据一个条件的真假值来选择不同的计算方式。
它的语法为:
条件表达式 ? 表达式1 : 表达式2;
其中,条件表达式为一个返回布尔值的表达式,如果它的结果为true,则执行表达式1,否则执行表达式2。
示例代码:
int x = 10, y = 20;
int max_num = (x > y) ? x : y; // 如果x>y,则max_num=x,否则max_num=y
在上面的例子中,如果x>y,则三目运算符返回x,否则返回y,最终将返回的值赋给max_num变量。
a > b ? a : b ; //如果a大于b则返回a,反之返回b
a < b ? a : b ; //如果a小于b则返回a,反之返回b
switch语句,执行多条件分支语句
判断只能是整型或者字符型,不可以是一个区间
C++中的switch语句用于多分支判断,语法如下:
switch(expression){
case constant1:
//代码块1
break;
case constant2:
//代码块2
break;
default:
//默认代码块
break;
}
其中,expression是一个可求值的表达式,case后面的常量可以是整型、字符型、枚举型或者变量值,用于匹配expression的值,如果匹配成功,则执行相应的代码块,执行完毕后用break终止switch语句。如果所有的case都不匹配,则执行default代码块,如果没有default代码块,则switch语句不会做任何操作。
需要注意的是,case后面的常量必须是编译时常量,即不能使用变量或者函数调用的返回值。
switch(表达式)
{
case 结果1:执行语句;break;
case 结果2:执行语句;break;
.....
default:执行语句;break;
}
循环语句:while()
while(循环条件)
{
执行循环内容;
}
do…while循环语句
do-while循环是一种后测试循环语句,它与while循环的区别在于它至少执行一次循环体。语法如下:
不管条件是否满足先执行一次循环语句,在进行判断看接下来的循环
do {
// 循环体
} while (条件表达式);
for循环
在 C++ 中,for 循环用于重复执行一定次数的代码。for 循环的语法如下:
for (initialization; condition; increment) {
// code to be executed
}
initialization
- 在循环开始前执行一次的代码,通常用于初始化循环计数器。condition
- 每次迭代前都会被检查的条件。如果为 true,则执行循环体中的代码;否则跳出循环。increment
- 在循环体执行完之后执行的代码,通常用于递增或递减循环计数器的值。
下面是一个简单的 for 循环示例:
for (int i = 0; i < 10; i++) {
cout << i << endl;
}
这个循环将执行 10 次,每次输出一个数字。
if判断
||:这个符号为或
if(判断条件 || 判断条件2 || 判断条件3)
{
}
跳出循环:break:break是一种控制语句,可用于中断循环语句(如for循环或while循环)或switch语句中的执行
#include <iostream>
using namespace std;
int main() {
for (int i = 1; i <= 10; i++) {
if (i == 6) {
cout << "中断循环" << endl;
break;
}
cout << i << endl;
}
return 0;
}
结束本次循环:continue
用于在循环结构中跳过当前迭代并进行下一次迭代。当continue语句出现在循环结构中的某个代码块内,它会立即终。
无条件跳转语句:goto
goto后面跟着一个标签(label),该标签必须定义在当前函数中的某个位置,并且标签后面必须紧跟一个语句。当程序执行到goto语句时,会立即跳转到标签所在的位置,并从该位置开始执行下一条语句
随机数
rand()
数组
int 数组名[数组的长度] = {10,20,30,40};
统计数组占用的空间:sizeof
通过总体的数据空间除于单独空间等于数组的个数
数组元素的个数 = sizeof(数组)/sizeof(数组[0])
数组的逆置
可以通过循环交换数组元素的方法实现数组的逆置,具体步骤如下:
-
定义一个数组,例如 int arr[] = {1, 2, 3, 4, 5};
-
计算数组的长度,例如 int len = sizeof(arr) / sizeof(arr[0]);
-
定义两个指针,一个指向数组的开头,一个指向数组的结尾,例如 int *start = arr; int *end = arr + len - 1;
-
循环交换数组元素,直到指针 start 大于等于指针 end。
while (start < end) {
int temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
- 最终数组 arr 的元素顺序将会被逆置。
完整代码:
#include <iostream>
using namespace std;
int main() {
int arr[] = {1, 2, 3, 4, 5};
int len = sizeof(arr) / sizeof(arr[0]);
int *start = arr;
int *end = arr + len - 1;
while (start < end) {
int temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
for(int i = 0; i < len; i++) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
数组的冒泡排序
#include <stdio.h> //std标准 io输入输出
#include <stdlib.h>
#include "my_tools.h"
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
int main() {
int arr[9] = {8,2,6,4,3,1,5,7,9};
cout << "排序前" << endl;
for (int i = 0; i < 9; i++)
{
cout << arr[i] << " ";
}
cout << endl;
//开始冒泡程序
for (int i = 0; i < 9; i++)
{
for (int 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;
}
}
}
cout << "排序前" << endl;
for (int i = 0; i < 9; i++)
{
cout << arr[i] << " ";
}
cout << endl;
system("pause");
}
第2种
#include <iostream>
using namespace std;
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr[j], arr[j + 1]);
}
}
}
}
int main() {
int arr[] = {5, 2, 9, 4, 7, 3};
int n = sizeof(arr) / sizeof(arr[0]);
cout << "Original array: ";
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
bubbleSort(arr, n);
cout << "\nSorted array: ";
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
return 0;
}
二维数组
C++中二维数组是由多个一维数组组成的数组。一般来说,使用二维数组可以方便地处理具有多个维度的数据,例如矩阵。
定义一个二维数组的语法如下:
数据类型 数组名[行数][列数];
例如,下面的代码定义了一个名为matrix
的二维数组,它有3行和4列:
int matrix[3][4];
我们可以通过指定行数和列数来初始化二维数组,例如:
int matrix[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
使用二维数组时,可以使用两个循环来遍历所有元素。例如,下面的代码遍历了一个3行4列的矩阵,并打印出每个元素的值:
for(int i=0; i<3; i++) {
for(int j=0; j<4; j++) {
std::cout << matrix[i][j] << " ";
}
std::cout << std::endl;
}
int arr[2][3]=
{
{1,2,3},
{4,5,6}
};
int arr[2][3] = {1,2,3,4,5,6,}
查看二维数组行数和列数多少
数组的行数 : sizeof(数组)/sizeof(数组[0])
数组的列数 : sizeof(数组[0])/sizeof(数组[0][0])
其中,sizeof(arr) 表示数组总共占用的字节数,sizeof(arr[0]) 表示数组中第一行占用的字节数。通过这两个值的比较即可得到行数和列数。注意,此方法仅适用于静态数组,对于动态分配的二维数组需要使用其他方式获取行数和列数。
二维数组的案例,用于存储学生的成绩信息并打印输出:
#include <iostream>
using namespace std;
int main() {
//onst是一个关键字,用于限制变量、指针或者函数的值或参数在程序的执行过程中不能被修改
const int NUM_STUDENTS = 3; // 学生数量
const int NUM_SUBJECTS = 2; // 科目数量
// 声明二维数组用于存储学生的成绩信息
int grades[NUM_STUDENTS][NUM_SUBJECTS];
// 循环输入每个学生的成绩
for (int i = 0; i < NUM_STUDENTS; i++) {
cout << "请输入第 " << i+1 << " 个学生的成绩:" << endl;
for (int j = 0; j < NUM_SUBJECTS; j++) {
cout << "科目 " << j+1 << ":";
cin >> grades[i][j];
}
}
// 循环输出每个学生的成绩
for (int i = 0; i < NUM_STUDENTS; i++) {
cout << "第 " << i+1 << " 个学生的成绩:" << endl;
for (int j = 0; j < NUM_SUBJECTS; j++) {
cout << "科目 " << j+1 << ":" << grades[i][j] << endl;
}
}
return 0;
}
头文件定义: 文件.h
#include <iostream>
using namespace std;
写入创建的函数,函数声明
函数声明.cpp中要调用头 文件.h文件
#include 文件.h
主函数文件也要调用 头 文件.h
#include 文件.h
写一个加法的函数
int main() {
int a = 10, b = 20;
int sum = add(a, b); // 调用 add 函数计算 a 和 b 的和
cout << "The sum of " << a << " and " << b << " is " << sum << endl;
return 0;
}
写一个函数的调用的示例代码
#include <iostream>
// 定义一个函数
int add(int a, int b) {
return a + b;
}
int main() {
// 调用函数
int result = add(2, 3);
// 打印函数的返回值
std::cout << "The result is: " << result << std::endl;
return 0;
}
指针
int * p = NULL; //空指针用于给指针变量进行初始化,这个为空指针
int * p = 0xxx; //野指针就是非法指针,对指定地址没有权限
int a=10
int * p
p = &a //指针指向数据a的地址
c++中sizeof
c++中的野指针
C++ 中的野指针是指一个指针变量,它保存了一个无效或者未初始化的内存地址,即一个不受控制的地址。这种指针可能指向一个没有分配给你的内存位置,或者指向一个已经被释放的内存区域。野指针是一种常见的编程错误,可能会导致程序崩溃、未定义行为或者不可预测的结果。
以下是一个例子,展示了野指针的情况:
#include <iostream>
int main() {
int *wildPtr; // 未初始化的指针,成为野指针
// 假设在这里发生了一些代码,没有为 wildPtr 分配内存
// 尝试解引用野指针会导致未定义行为
// int value = *wildPtr; // 这行代码会导致问题
return 0;
}
在这个示例中,指针 wildPtr
被声明为一个整数指针,但是没有被初始化或者分配内存。尝试解引用这个指针将会导致未定义行为,因为它指向了一个未知的内存地址。
为了避免野指针问题,你可以遵循以下几点:
-
初始化指针:在声明指针的同时,将其初始化为
nullptr
,以确保它不包含任何无效地址。在 C++11 及之后的标准中,可以使用nullptr
来初始化指针。int *ptr = nullptr; // 初始化指针为 nullptr
-
指针使用前检查:在使用指针之前,先检查它是否为
nullptr
,以避免解引用野指针。if (ptr != nullptr) { int value = *ptr; // 只有在指针不为 nullptr 时解引用 }
-
谨慎使用动态内存分配:使用
new
关键字分配内存时,要确保在不需要使用这块内存时使用delete
释放它。避免忘记释放内存,以防止出现野指针。int *dynamicPtr = new int; // 使用 dynamicPtr delete dynamicPtr; // 释放内存,避免野指针
总之,野指针是一个需要注意的问题,为了避免未定义行为和程序崩溃,始终确保你的指针在使用前都是有效和正确初始化的。
const修饰指针
常量指针:指针指向可以改,指针指向的值不能修改
const int * p = &a;
指针常量:指针指向不能修改,指针指向的值可以修改
int * const p = &a;
指针指向和指针指向的值都不能修改
const int * const p = &a
指针指向数组
创建一个数组
int arr[4] = {1,2,3,4};
创建一个指针,指向arr数组
int * p = arr
第一个元素为*p
p++
第二个元素
指针和函数
三种值的传递方式:值传递、引用传递和指针传递。
C++中有三种值的传递方式:值传递、引用传递和指针传递。
- 值传递:
值传递是将函数参数的值复制一份给函数的形参,形参和实参之间没有任何关系,函数运行结束后,形参的值也会被销毁,不会对实参产生影响。
示例代码:
#include <iostream>
using namespace std;
//形参,形参进行改变也改变不了实参的值
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
//实参
int main()
{
int x = 10, y = 20;
swap(x, y);
cout << "x = " << x << ", y = " << y << endl;
return 0;
}
输出结果为:
x = 10, y = 20
可以看到,虽然在swap函数中交换了a和b的值,但是在main函数中x和y的值并没有被改变,因为是值传递,函数中的a和b只是x和y的拷贝。
- 引用传递:
引用传递是将函数参数的引用传递给函数的形参,形参和实参之间共用同一个内存地址,函数运行结束后,形参和实参都指向同一个内存地址,实际上是对实参进行的修改。
示例代码:
#include <iostream>
using namespace std;
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int x = 10, y = 20;
swap(x, y);
cout << "x = " << x << ", y = " << y << endl;
return 0;
}
输出结果为:
x = 20, y = 10
可以看到,使用引用传递的方式,函数中的a和b是x和y的引用,实际上是对x和y进行的操作,所以在交换之后,x和y的值得到了真正的修改。
- 指针传递:
指针传递是将函数参数的指针作为实参传递给形参,形参是指针类型,可以通过指针来操作实参的值,实际上和引用传递的方式十分相似。
示例代码:
#include <iostream>
using namespace std;
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int x = 10, y = 20;
swap(&x, &y);
cout << "x = " << x << ", y = " << y << endl;
return 0;
}
输出结果和引用传递的方式相同:
x = 20, y = 10
总的来说,值传递只是传递了实参的一个副本,使用时要注意,如果需要在函数中修改实参的值,应该使用引用传递或指针传递的方式。
函数的声明
返回类型 函数名称(参数列表);
int add(int a, int b);
以下是关于函数声明的一些注释:
函数声明只需要描述函数的接口,不需要具体实现。
函数声明通常放在头文件中,以供其他文件使用。
如果函数在引用前没有声明,则编译器会报错。
函数可以在一个文件中声明,而在另一个文件中实现。
同一个文件中的函数可以在 main 函数之后定义,但是它们需要在 main 函数之前被声明。
函数的分文件编写
**作用:**让代码结构更加清晰
函数分文件编写一般有4个步骤
- 创建后缀名为.h的头文件
- 创建后缀名为.cpp的源文件
- 在头文件中写函数的声明
- 在源文件中写函数的定义
示例:
1:swap.h文件
//
#include<iostream>
using namespace std;
//实现两个数字交换的函数声明
void swap(int a, int b);
2:swap.cpp文件
//swap.cpp文件
#include "swap.h"
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
3:main函数文件
//main函数文件
#include "swap.h"
int main() {
int a = 100;
int b = 200;
swap(a, b);
system("pause");
return 0;
}
结构体:自定义数据类型
创建自定义数据类型
struct Struct{
string name;
int age;
int score;
};
通过main函数调用自定义数据类型
int main(){
//方法1赋值
struct Struct s1;
s1.name = "张三"; //调用自定义的数据name
s1.age = 18; //调用自定义的数据age
s1.score = 100; //调用自定义的数据score
//方法2赋值
struct Struct s2 = {"张三",18,100};
}
//方法3:直接在定义函数后写上s3,对s3进行调用
struct Struct{
string name;
int age;
int score;
}s3;
s3.name = "张三"; //调用自定义的数据name
s3.age = 18; //调用自定义的数据age
s3.score = 100; //调用自定义的数据score
结构体数组
struct 结构体名 数组名[元素个数] = {{},{},{},{}}
创建自定义数据类型
struct Struct{
string name;
int age;
int score;
};
使用main()函数进行数组的添加
main(){
struct Struc stuarray[3] =
{
{"张三",18,100}
{"李四",19,82}
........
};
}
通过遍历结构体数组
for(int i =0 ;i<3,i++)
{
cout<<"姓名"<<stuarray[i].name
<<"姓名"<<stuarray[i].age
<<"姓名"<<stuarray[i].score<<endl;
}
使用结构体指针
struct Struct{
string name;
int age;
int score;
};
int main(){
Struct s = {"张三",18,100};
Struct * p = &s
cout>> p->name>>endl;
system("pause")
return 0 ;
}
结构体嵌套结构体
struct student
{
string name;
int age;
int score;
};
struct teacher
{
string name;
int age;
int score;
struct student stu; //嵌套上面结构体
}
main(){
teacher t;
t.name = "老师"
....
t.stu.name = "学生"
.....
}
结构体做参数
注意如果要修改主函数中的数据,用值传递,反之用地址传递,
struct student
{
string name;
int age;
int score;
};
//值传递
void printStudent1(struct student s)
{
cout<<s.name<<....<<endl;
}
//地址传递
void printStudent2(struct student * p)
{
p->age = 150; //这里的修改也可以对主函数上进行修改,如果是值函数传递,主函数不会修改而且还要进行备份,占用空间大
cout<<p -> name <<....<<endl; //使用箭头传递 ->
}
main(){
struct student s;
s.name = "老师"
printStudent1(s);
printStudent2(&s);
}
结构体中的const使用场景
作用:用const来防止误操作
struct student
{
string name;
int age;
int score;
};
//将函数中的形参改为指针,可以减少内存空间,而且不会复制新的副本出来
void printStudent1(const student *s) //为了防止误操作在形参中加入const的
{
//s->age = 150; //这里的修改也可以对主函数上进行修改,如果是值函数传递,主函数不会修改而且还要进行备份,占用空间大 ,加入const之后就不能在这上面进行修改
cout<<s->name<<....<<endl; //使用的是指针地址用->指向
}
main(){
struct student s;
s.name = "老师"
printStudent1(&s);
}
利用new关键字,可以将数据开辟到堆区
int * func()
{
//利用nue关键字,开辟到堆区
//指针本质也是局部变量,放在栈上,指针保存的数据存放在堆区
int * p = new int(10) //
return p;
}
int main()
{
//在堆区开辟数据
int * p = func();
system("pause")
}
利用delete来手动释放
delete p;
静态变量
static int 变量名 = 10; //静态变量