宏
//例如
#define ABS(a) (a>0 ? a : a*-1) //如果写成#define ABS(a) (a>0 ? a : -a)会报错,--缺少左值,因为--3.14无法无法被编译
int main()
{
float a = ABS(-3.14);
cout << a;
}
//显示
3.14
宏的应用
//按照WIN32的方式编译
//如果在WIN32下,打开FILE "D:\\"
//否则打开FILE "/user/.../doc/"
#define WIN32
#ifdef WIn32
#define FILE "D:\\"
#elif
#define FILE "/user/.../doc/"
#endif
常用标准宏
__LINE__:输出当前所处的行数
__FILE__:文件名
__DATE__:编译时候的日期
__TIME__:编译时候的时间
__FUNCTION__:当前所处的函数
//例如
void c()
{
cout << "当前函数" << __FUNCTION__ << "\n";
}
int main()
{
cout << "当前行" << __LINE__ << "\n";
cout << "当前文件" << __FILE__ << "\n";
cout << "当前日期" << __DATE__ << "\n";
cout << "当前时间" << __TIME__ << "\n";
c();
}
//显示
当前行18
当前文件D:\MySoftware\Visual Studio\class\ConsoleApplication2\ConsoleApplication2.cpp
当前日期Dec 8 2022
当前时间11:11:04
当前函数c
函数重命名
typedef int myint; //给Int重命名为myint,后续可以直接使用myint,当做int来用
函数和结构体都可以用
方便跨平台通用
函数指针
//例如
#define ABS(a) (a>0 ? a : a*-1)
typedef int (*func) (int a, int b); //函数指针,int是返回类型,*func是函数名称,不要去括号
int add(int a, int b) //有一个函数add
{
int c = a + b;
return c;
}
int main()
{
func(fadd) = add; //这样可以直接使用fadd,像add一样
int d = fadd(1, 3);
cout << d;
}
用于回调函数
全局变量
放在最前面,然后后面使用的时候在前面加::,就可以,函数里面也可以用
//例如
int globalnum = 1; //作为全局变量
int main()
{
::globalnum++; //使用
for (int i = 0;i < 5;i++)
{
::globalnum++; //循环内使用
}
cout << globalnum;
}
//显示
7
memcpy / memset
memset
void *memset(void *s,int c,unsigned long n);
为指针变量s所指的 前n个字节,填充指定的int类型数值c,它可以为任何数值进行初始化;
就是,将
数值c以单个字节逐个拷贝的方式放到指针变量s所指的内存中去,
对结构体
//例如
struct student
{
char name[12];
int age;
float num;
};
int main()
{
student *s1 = new student();
memset(s1, 2, sizeof(s1));
cout << s1->name << " " << s1->age<<" " << s1->num;
}
//显示
0 0 //可以做到初始化,只不过name是字符,在转换过程中会乱码
对数组
//例如
int main()
{
int a[20];
memset(a, 0, sizeof(a)); //如果将0换成其他数字,也可以填充,但是由于内存的排布,会显示成其他数字,这里不做赘述
for (int i = 0;i < 20;i++)
{
cout << a[i]<<"\n";
}
0换成1,显示不会填充1
如果将0换成1,会显示16843009
因为memset是内存填充
一个数字四个字节
数字以16进制的形式存储,如果填充1,会将1逐个填充到b的80个字节内存中去,一个数字占4个字节,所以这个16进制就成了0x01010101,这个再转换成10进制就是16843009
//显示
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
memcpy
void *memcpy(void *dest, const void *src, size_t n);
源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。
const void *src:原地址
void *dest:要拷贝到的地址
size_t n:字节长度
内存整块拷贝
//例如
int a = 1;
int b;
memcpy(&b, &a, sizeof(a));
cout << b << "\n";
int c[10];
int d[15];
memset(c, 0, sizeof(c));
memcpy(d, c, sizeof(c));
for (int i = 0;i < 15;i++)
{
cout << d[i] << "\n";
}
//显示
1
0
0
0
0
0
0
0
0
0
0
-858993460
-858993460
-858993460
-858993460
-858993460
strcpy和memcpy主要有以下3方面的区别:
1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy。
enum
枚举
定义枚举空间之后,相关变量只能从枚举空间里面取值
枚举空间中的变量没有赋值的话,则默认从0开始,依次递增
枚举空间的值只能赋整型
if语句中变量不受枚举空间限制
//例如
enum studytype
{
A, //用逗号结尾
B,
C = 2,
D = 3,
};
int main()
{
cout << A<<" " << B << " " << C << " " << D << "\n";
studytype a = A;
studytype b = A;
a = D;
//a = 2; //枚举的限制值只能是整型,但是整型不会自动转换为枚举类型,因此会报错
cout << a;
cout << int(a) + int(b); //转换成整型做加法
if (a == 10000);
{
cout << "执行";
}
//显示
0 1 2 3
33执行
***应用:***做编译器检查,防止赋奇怪的值
枚举变量不能直接做加法,但是可以转换成整型之后做加法
位运算
C++支持按位运算
并且运算规则和python的运算规则一致
&按位与
|按位或
~按位取反
数学库
导入
#include <math.h>
常用数学公式
//例如
int main()
{
int a = 2;
float b;
b = sqrt(a); //开平方
cout << a << "\n";
cout << pow(2,3) << "\n"; // 2的3次方
cout << time(0) << "\n"; //返回当前格林尼治标准时间与格林尼治标准时间1970年0分0秒的时间间隔
}
//显示
2
8
1670507285
随机数
srand():随机数生成器
由随机种子根据一定的计算方法计算出来的数值。所以,只要计算方法一定,随机种子一定,那么产生的随机数就不会变。
在相同的平台环境下,编译生成exe后,每次运行它,显示的随机数都是一样的。这是因为在相同的编译平台环境下,由随机种子生成随机数的计算方法都是一样的,再加上随机种子一样,所以产生的随机数就是一样的。
因此通常,将time(0)扔进去,因为time(0)每时每刻都在发生变化
如果里面的整数固定,则相同变量得到的随机数是固定的
//例如
int main()
{
srand(3);
int c = rand();
int d = rand();
cout << c << "\n";
cout << d << "\n";
}
//显示
48 //无论运行多少次,c都是48,如果换成e结果就不一样了
7196
因此通常和time来配个使用获得随机整数
rand获得的最大整数是0x7fff
因此,如果想要获得小数
需要做进一步的转换
//例如
int main()
{
srand(3);
int c = rand();
int d = rand();
cout << c << "\n";
cout << d << "\n";
float e = (c % 0x7fff) / (float)0x7fff;
cout << e << "\n";
}
//显示
48
7196
0.00146489 //产生0到1的随机小数
命名空间
//例如
namespace KK
{
int add(int a, int b)
{
return a + b;
}
};
int main()
{
int x = KK::add(1,3) //使用KK命名空间下的add函数
//如果不想每次写KK::,也可以在开头写using namespace KK
}
跟前面导入using namespace std一个道理
命名空间可以导入多个,也可以使用多个
如果两个命名空间里面的函数一样,在调用的时候会报错,因为不知道调用哪个
这时候就需要写明哪个空间的东西
头文件
1、在头文件里写入结构体,并且在项目文件中用#include"地址+名称"导入
则可以直接在int main()中使用结构体;
2、因此数学库,string,等等都是被写在头文件中的,导入之后可以直接用
3、将函数定义放在头文件中,导入头文件后,也可以直接使用函数;
4、将函数声明放在头文件中,将函数内容放在源文件中另一个cpp文件夹这种,也可以在int main()所在的cpp文件直接使用函数,是因为整个项目下的.cpp文件都会被编译,所以不会报错
5、所有的cpp文件只能有一个函数,结构体实体,如果重复,则会报错,但是可以写在不同的命名空间下
6、为了防止重复编译报错,就可以用到
#ifndef 函数名 //如果没有编译过,则进行编译,如果编译过了,则不编译
#define 函数体
#endif
7、导入的时候,<>表示先在系统库中找.h文件,如果是" ",则会先在项目目录下找该文件,如果找不到,再去系统库找