美文网首页
C++基础与实战

C++基础与实战

作者: 十年之后_b94a | 来源:发表于2019-11-27 11:13 被阅读0次

Hello Word

#include <iostream>
using namespace std;
int main()
{
  cout<< "Hello Word!!"<<endl;
  return 0;
}

数据类型

整形:int占用4字节空间
短整形:short int 占用两个字节
长整型:long int占用4字节空间
超长型:long long占用8字节空间精度浮点型:float占用4字节
双精度浮点型:double 占用8字节空间
字符型:char 占用1字节
字符串:string40个字节在不同的机器上可能不一样
逻辑型:bool1个字节->注意在c/c++中没有true和false 一律使用0与非0表示

#include <iostream>
#include <Windows.h>
using namespace std;

int main()
{
    cout << "int字节:"
        <<sizeof(int) 
        <<"\nshort int字节:"
        << sizeof(short int)
        << "\nlong int字节:"
        << sizeof(long int)
        << "\nlong long字节:"
        << sizeof(long long)
        << "\nfloat字节:"
        << sizeof(float)
        << "\ndouble字节:"
        << sizeof(double)
        << "\nchar字节:"
        << sizeof(char)
        << "\nstring字节:"
        << sizeof(string)
        << endl;
    system("pause");
    return 0;
};
image.png

输出与输入

强制让cout小数显示

当一个小数足够大

double a = 10.0/3.0;
cout << a * 10000000 << endl;  输出结果会是科学计数法3.333333e+07

怎么让他显示全呢?在cout语句之前加上

cout <<fixed;
double a = 10.0/3.0;
cout << a * 10000000 << endl;

让cout显示精度(保留多少位小数)只是显示效果,不会改变值

#include <iomanip> 引入头文件并在cout语句前加上setprecision(length)
cout << setprecision(12);
double a = 10.0/3.0;
cout << a << endl;

输出空格(setw(length))

#include <iomanip>
cout<<"测"<<setw(8)<<"试"<<endl;

设置控制台程序的标题

#include <windows.h>
setConsoleTitle("标题");

输入(cin与scanf相似)

int num;
cin >> num; 注意箭头方向

把0/非0输出成true/false

cout<<boolalpha;

思考题

int n = -5,n1 = 2;
n = (n1 ++ ) - (--n1);
cout<<"n:"<<n<<"\t"<<"n1:"<<n1<<endl;

分析:n++是整个表达式运行完成才++ 而--n 是立即执行--操作
结果为什么是n:0 ,n1:2
n = 1-1 因为n1 --了 变为1 而第一个()这里就是1了

位运算符

在位运算中一切操作都是在二进制中进行的

运算符 作用 示例
& 按位与 两个操作数同时为1结果为1
| 按位或 两个操作数只要有一个为1结果为1
^ 按位异或 操作数为1,结果为0,操作数为0,结果为1
~ 按位非 两个操作数相同,结果为0,不相同为1
<< (重点 ) 左移 右侧空位补0
>> (重点) 右移 左侧空位补符号位
>>> 无符号右移 左侧空位补0

案例:

#include <iostream>
#include <Windows.h>
using namespace std;
int main()
{
  cout << (2<<3)<<endl;  结果是16
  cout<<(16>>2)<<endl; 结果是4
  cout<<(63>>2)<<endl; 结果是15
  return 0;
}

分析:2的二进制是 1 0 然后左移3 左移是往右侧补0 那么就是1 0 0 0 0 转为十进制就是16
16>>2分析16的二进制是 1 0 0 0 0 就是去掉2个0 结果就是100 转为十进制就是4
63>>2分析63的二进制是1 1 1 1 1 1 去掉2个1 结果就是1111 转为十进制就是15

循环小案例

int i = 0;
while(i ++ <=2);
cout<<i<<endl;结果是4

分析,当i为三的时候,再去比较一次是否小于等于2,不小于跳出循环但是还有一次++哦,所以结果是4;

容器vector(数组)

vector数组和普通数组不同
1、他可以在运行阶段改变数组长度,而普通数组只能在定义时给定长度,或者就给初始值
2、可以插入和删除数组元素
vector属于vector头文件

vector<double> test; //test集合中只能存放double类型的数据
vector<string> str(5); //存放字符型且空间大小事5
vector<int> num(20,998);给20个空间(元素)每个元素都是998
方法 常用方法
clear() 移除容器中所有的数据
empty() 判断容器是否为空
size() 返回容器中的元素个数
[index]、at[index] 返回索引为index的元素 (查找下标为index的元素)
erase(pos) 删除pos位置处的数据
erase(beg,end) 删除[beg,end) 区间的数据
front() 返回第一个元素
insert(pos,elem) 在pos位置插入一个元素
pop_back() 删除最后一个元素
push_back(elem) 末尾追加元素
resize(num) 重新设置容器大小
begin()、end() 返回容器收尾元素的迭代器
find(vector.begin(),vector.end(),element) 查找目标是否在vector中

实践

#include <iostream>
#include <Windows>
#include <vector> //导入头文件
using namespace std;
int main()
{
  vector<int> num = {1,2,3,4,5,6,7};
  num.push_back(8) ; //追加一个元素为8
  for (int i = 0; i < num.size(); i++)
  {
    cout << num[i] << endl;
  }
  num.clear();  //清空元素 此时的num.size()为0


    -------------------------
  //高级vector循环的例子
  vector<double>::iterator it; 这个it就是指针
  循环含义 收it等于num容器的首个指针 ,然后指针一直往后移动 当指针不等于最后一个指针之后 就不再循环
  for(it = num.begin() ;it != num.end(); ++it ) 这里进行 ++it 让指针往后移动
  {
    cout << *it <<endl; 指针
  }
  return 0;
}
sort()用法
1、
#include <algorithm>
vector<int> num = {50,45,7962,1454,2,12,45,284,2142,541};
sort(num.begin(),num.end());传入第一个和最后一个 小到大排序
reverse(num.begin(),num.end());从大到小排序
vector<int>::iterator it;
for(it = num.begin() ;it != num.end(); ++it )
{
   cout << *it <<endl; 指针
}

实践 - 查找某值是否在vector中

bool is_element_in_vector(vector<int> v, int element)
{
    vector<int>::iterator it;
    it = find(v.begin(), v.end(), element);
    if (it != v.end()) {
        return true;
    }
    else {
        return false;
    }
}

array类型(普通数组)

#include <array>
array<int,5> myArr;   => 相当于 int myArr[5] 一模一样

指针

void 类型指针一般用来比较,他不能改变值

int num = 2014;
int * ptr_num = &num;
void *ptr_num1 = &num;
*ptr_num = 2048;
*ptr_num1 = 4849;  报错

引用

注意引用不能直接引用常量!如
int &test = 2;这个是错误的必须引用变量
当形参是引用类型是,进行数据改变,会改变原数据

int num = 1024;
int& test_num = num;
cout << test_num <<endl;  结果1024

例子引用改变数据

void test(int& a,int& b)
{`如果形参前加上const 就不会改变原数据`   const int& a
  a ++;
  b ++
}
int main()
{
  int a = 3,b = 4;
  test(a,b);
  cout << a << setw(8) << b <<endl; 结果a等于4  b等于5
  return 0;
}

动态分配内存(new方法)一般与指针相配合使用

告别c语言中的malloc以及calloc
为什么动态分配内存要与指针配合使用呢?分配了内存,我们怎么找到该内存?所以我们用指针指向这块内存区域就行

温习下两者分配内存的用法

//malloc
int * p = (int *)malloc(10);int类型的10个空间
int * t = calloc(5,sizeof(int)) 分配20个空间

使用new方法申请内存

int *p = new int; 申请4个字节的内存空间
delete p; 释放内存

注意动态分配内存之后我们需要释放掉内存,即使使用malloc或者calloc也要用free释放掉
使用delete释放内存

int *p = new int[5];分配了5个空间的整形
类似 
int num[5];
int *p = num;
释放内存
delete[] p

分配二维数组的动态内存

int (*p)[3]  = new int[5][3];

函数指针

int sum(int,int);//定义函数原型
int sum(int a ,int b) //函数的实现
{
  return a +b;
}
int main()
{//主函数
  int (*ptr_fn))(int,int);
  ptr_fn = sum;
cout << ptr_fn (3,4)<<endl;  调用
  或者
  cout << (*ptr_fn )(3,4)<<endl;一样也可以
}

内联函数

相对于普通函数来说,运行速度及效率内联函数都是高于普通函数;
普通函数运行机制:当代码运行到执行函数的时候,现在栈区找到改函数对应的内存地址,再去执行;
内联函数:当代码运行到执行函数的时候,直接执行函数里面的内容,相当于直接把函数的代码块搬过来;

通俗一点解释:普通函数看书需要查目录,而内联函数直接看内容
inline关键字

//定义函数
inline int sum(int,int); // 比普通函数多一个inline

什么时候使用内联:当程序逻辑及代码较少的情况可以使用内联,使用内联,会消耗相对大的内存消耗

重载

重载讲的是一个函数多次定义,但是参数不同
案例,对数组进行排序,但是按传统排序方法,传入的数组可能是个整数型数组、浮点型数组、、、这样我们得写很多份函数,

#include <iostream>
#include <Windows.h>
#include <iomanip>
using namespace std;
/*
    模板
*/
template<typename T> void Sorts(T [], int);
template<typename A> void showArr(A[], int);
int main()
{
    int intArr[6] { 45,8,12,48,21 };
    Sorts(intArr, 6);
    showArr(intArr, 6);
    system("pause");
    return 0;
}

template<typename T>
void Sorts(T arr[], int len)
{
    T temp;
    for (int i = 0; i < len; i++)
    {
        for (int j = 0; j < len - i - 1; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
            }
        }
    }
}
template<typename A>
void showArr(A arr[],int len)
{
    for (int i = 0; i < len; i++)
    {
        cout << arr[i] << endl;
    }
}

面向对象

类的关键字:class

//新建类文件
#ifndef ***
#define ***
#include <iostream>
#include <string>
using namespace std;
class newClass
{
  private: //私有的属性 ,不写private 默认是私有的属性
    string name;
  public: //静态的方法/属性
   newClass(); //构造函数
  ~newClass(); //析构函数
  show();//自购函数
}
newClass::newClass()
{
  
 }
newClass::~newClass()
{
}
newClass:show()
 {
}
#endif

main.cpp
#include "导入刚刚创建的头文件"
newClass.name = "测试" 
会报错的!!!因为这个name属性是私有的无法在外面访问该属性
我们应该把这个属性 放在public里面,但是有一个很不好,因为会被随便变更值,所以我们写个方法
setName 放在public中 设置name的值
在写一个getName 返回name就行

public在这里面定义的方法/属性在任何地方都可以使用
private只能在类里面或者有团函数可以使用
protected受保护的只能是同家族的可以使用
类里面的构造函数一般是用来初始化成员变量(private静态变量)也可以
类里面的够着函数可以带参数的

类形指针

创建一个类例如studen
class Student{
  private:
    string name;
    int classInt; //班级
    string teacher; //老师
   public:
    Student(string,string,int); 
    ~Student();
    void showInfo();
}
Student::Student(string a,string b,int c){
  name = a;
  classInt= c;
  teacher = b;
};
Student::~Student(){};
void  Student::showInfo(){};
实例化指针
Student* st1 = new Student("小明","王老师",3); 和js一样new对象
st1->showInfo();
delete st1;

当类型指针用完之后!一定要释放内存delete st1

析构函数

作用用来释放资源(内存)一般用于当实例化类用完之后,调用该析构函数 释放掉内存

用上述的student的类
Student::~Student(){
  cout<<"释放内存"<<endl;
}

this指针

this的字面意思指这个,一般在类里面使用

class Student{
  string name;
  string teacher;
  float *scroe; //分数数组  我们用指针的方式来生成数组可以看本笔记的new分配内存
  public:
  Student();
  ~Student();
};
Student::Student()
{
  this->name = "小明";
  this->teacher = "王老师"
  this->scroe = new float[1]; //分配一个空间
}
Student::~Student()
{
  delete this->scroe ; 在析构函数中得释放内存
}

运算符重载

运算符重载关键字operator
什么叫做运算符重载?比如有一个MyTime
MyTime t1,t2,t3;
t3 = t1 +t2; 或者其他运算,我们知道,+-*/怎么能运算类与类呢?

C/C++与汇编

在C中汇编关键字_asm

#include <iostream>
using namespace std;
int main()
{
  int a,b,c;
  _asm{
    mov a,1h  把1放入a变量 =>  a = 1
    mov b,5h 把5放入b变量 =>  b = 5
    mov eax,a  把a变量的值放入寄存器eax中  =>  eax = a
    mov ebx,b   把b变量的值放入寄存器ebx中  =>  ebx = b
    add eax,ebx  然后两个寄存器的值相加 =>  eax = eax(a) +  ebx(b)
    mov c,eax  最后把 eax的值放到c变量中  c  = eax(a+b)
  };
  cout<< c <<endl; 结果会是6 
  return 0;
}

稍微解释下汇编知识:
在汇编中数值一律都是十六进制的向上述案例1h代表的是十六进制的1
寄存器:这里指32寄存器一般把值放在寄存器中进行运算
EAX EBX ECX EDX 这四个寄存器
mov 把右边的值赋值给左边
add 把两个寄存器里面的值相加之后把值放在左边这个寄存器
shl 左移运算

int a = 6;
 _asm{
  mov eax,a   把a的值给寄存器eax =>eax = a
  shl eax,2     =>eax = eax << 2
  mov a,eax  => a = eax
};
 cout << a<<endl; 结果是64

拓展C++数据类型

短整形 长度:-32768 ~ 32767
short__int16
无符号整形长度:0-65534
USHORTunsigned __int16WORD
长整型 长度:-2147483648~2147483647
int__int32long
无符号长整型长度:0~4294967295
unsigned intunsigned longunsigned __32DWORDDWORD32size_tULONG
超长整形长度:-9223372036854775808~9223372036854775807
long long__int64LONG64
无符号超长整形
DWORD64ULONG64unsigned long longunsigned __int64
单字节类型长度:-128~127
char__int8
无符号单字节类型长度0~255
BYTEUCHAR
双字节
WCHARwchar_t
自适应字节
TCHARDWORD_PTR
小数
floatdouble

线程与创建

c++11 thread类
createThread方法
_beginthreadex方法

thread类
#include <thread>
void hello_thread()
{
  cout <<"这是第一个线程"<<endl;
}
int main()
{//主函数
  thread t1(hello_thread);
  t1.join();
  return 0;
}

CreateThread函数原型

HANDLE WINAPI CreateThread(
      _In_opt_  LPSECURITY_ATTRIBUTES  lpThreadAttributes,   
      _In_      SIZE_T                 dwStackSize,
      _In_      LPTHREAD_START_ROUTINE lpStartAddress,
      _In_opt_  LPVOID                 lpParameter,
      _In_      DWORD                  dwCreationFlags,
      _Out_opt_ LPDWORD                lpThreadId
    );

CreateThread参数说明

  • 第一个参数 lpThreadAttributes 表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。
  • 第二个参数dwStackSize 表示线程栈空间大小。传入0表示使用默认大小(1MB)。
  • 第三个参数lpStartAddress表示新线程所执行的线程函数地址,多个线程可以使用同一个函数地址。
  • 第四个参数 lpParameter 是传给线程函数的参数。
  • 第五个参数 dwCreationFlags 指定额外的标志来控制线程的创建,为0表示线程创建之后立即就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用ResumeThread()
  • 第六个参数 lpThreadId 将返回线程的ID号,传入NULL表示不需要返回该线程ID号。

createThread使用

DWORD WINAPI test_thread(void * p)
{
    cout << "我是子线程, pid = "
        << GetCurrentThreadId()
        << endl;
    //输出子线程pid
    return 0;
}
int main()
{
  HANDLE hThread;
  DWORD threadId;
  hThread = CreateThread(NULL, 0, test_thread, 0, 0, &threadId); // 创建线程

  cout << "我是主线程, pid = "
        << GetCurrentThreadId()
        << endl;
  return 0;
}
image.png

_beginthread创建线程

unsigned __stdcall test_call(PVOID p)
{
    cout << "这是子线程 PID = "
        << GetCurrentProcessId()
        << endl;
    return 0;
};
_beginthreadex(NULL,0, test_call,NULL,NULL,NULL);

sendMessage()

SendMessage((HWND)0x000901AC, WM_PASTE, 0, 0);
将剪切板的内容发送到窗口句柄为0x000901AC的窗口
窗口句柄的获取方式可以使用Spy++进行获取,或者使用代码:FindWindow();

实战利用sendMessage以及多线程往记事本/QQ对话框发送消息

  • 首先我们打开三个记事本/QQ消息对话框分别用Spy++获取它们的窗口句柄
HWND jubing[3] = {(HWND)0x003906C2 ,(HWND)0x0050069C ,(HWND)0x004E087E }; 
把他们的窗口句柄数据用数组保存下来
  • 然后我们创建线程函数
unsigned __stdcall test_thread(void * p)
{
    SendMessage((HWND)p, WM_PASTE, 0, 0); 剪切板的内容我们可以事先黏贴好,或者我们使用代码设置剪切板的内容
    Sleep(20);
    return 0;
}
  • 然后使用线程调用它
for (size_t i = 0; i < 30; i++)
    {
        for (size_t k = 0; k < 3; k++)
        {
            _beginthreadex(NULL, 0, test_thread, jubing[k], NULL, NULL);
        }
    }

相关文章

网友评论

      本文标题:C++基础与实战

      本文链接:https://www.haomeiwen.com/subject/qfwwictx.html