易道云C/C++全栈工程师学习笔记
指针和引用(一)
指针
int* a{},b; a是指针,b是变量。
尽量分开写。
获取一个变量的内存地址
取址运算符&
int a = 5000; int* pa = &a;
指针和变量的数据类型必须一致。
读写一个指针指向的空间
间接运算符*
*pa = 500; 代表往a的内存地址里写入500
(*pa)++; 相当于a++;
std::cout<< "内存地址:" << pa << "\na=" << *pa;
指针数组
int* ptrArray[10];
声明10个int类型的指针
int studentId[5]{10001,10002,10003,10004,10005};
int* ptrStudentId[5];
for (int x = 0; x < 5; x++) {
ptrStudentId[x] = &studentId[x];
std::cout << "内存地址:" << ptrStudentId[x] << "值" << *ptrStudentId[x]<<"\n";
}
二维数组指针
int studentId1[2][2]
{
{10001,10002},
{10003,10004}
};
int* ptrStudentId1[2][2];
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2; j++)
{
ptrStudentId1[i][j] = &studentId1[i][j];
std::cout << "内存地址:" << ptrStudentId1[i][j] << "值" << *ptrStudentId1[i][j] << "\n";
}
}
数组内存空间也是连续的
内存地址:001CF6F8值10001 内存地址:001CF6FC值10002 内存地址:001CF700值10003 内存地址:001CF704值10004
指针补充
int* ptr{};
char* cptr{};
占用的空间都是8字节十六进制高位在前
因为指针是一种特殊的变量类型,在32位操作系统下,占8字节,在64位系统下,占16字节。
大多数计算机按高位优先顺序存储32位的数,但基于Intel CPU的计算机按低位优先顺序存储32位的数。
计算机存储字节顺序为:左低右高
16进制:左高右低 对于0X12,1是高位,2是地位
unsigned a{ 99901 };
int* ptr{(int*) &a};
*ptr = -1;
char* ctr{};
ctr = (char*)ptr;
*ctr = 'A'; //41 FF FF FF //0XFFFFFF41
std::cout << a;
指针实验
(*ptr)++
*ptr++ //先显示*ptr的值,然后让ptr++
指针+1的时候,数值的变化是 +1*指针类型的大小
所以int指针+1肯定要指向下一个地址的空间,所以是+4
想强制让指针后移一个字节,可使用指针类型的强制类型转换为char*
p=(int*)((char*)p+1);
那些绕晕人的指针概念
常量指针
const 变量类型*
所谓的常量指针,即这个指针指向的一个常量的内存地址,常量指针中,不能对其指向的内存地址进行改变,但是指针指向的地址可以改变;
const int a{ 1000 };
const int b{ 2500 };
int c{ 3500 };
const int* ptr{ &a };
ptr = &c;
//*ptr = 300; 不可以
std::cout << *ptr;
指针常量
变量类型* const
所谓的指针常量,即这个指针变量是一个常量,一旦初始化就不可以再指向其他内存地址,但是内存地址中的数据可以读写
int a{ 1000 };
int b{ 1000 };
int* const ptr{ &a };
//ptr = &b; 不可以
*ptr = 999999;
指向常量的常量指针
const 变量类型* const
指向常量的常量指针,即这个指针变量是一个常量,一旦初始化就不可以再指向其他内存地址,因为其本身就是一个指向常量的指针,所以其指向的内存区域也不可以修改!
int const a{ 1000 };
int const b{ 1500 };
const int* const ptrA{ &a };
//ptrA = &b;
//*ptrA = 500;
课后小练习
反汇编思考为什么*ptrA和a结果不一样
const int a{ 1000 };
const int b{ 1400 };
int* ptrA{ (int*)&a };
*ptrA = 9500;
std::cout << *ptrA << std::endl;
std::cout << a << std::endl;
指针和引用(二)
指针和数组
imul 有符号乘法,将被乘数与乘数均作为有符号数。
mul 无符号乘法,将被乘数及乘数均作为无符号数。
可以有三个操作数:imul eax,eax,0Ch
第3操作数是乘数,
第2操作数是被乘数,
运算结果放入第1操作数。
shl eax,1 左移1位
动态内存分配
C语言中的动态内存分配
malloc(size_t _Size)
字节为单位 返回void* 最多4个GB
calloc(size_t _Count,size_t _Size)
分配count*size字节个内存,并且返回内存分配的地址
realloc(void* _Block,size_t _Size)
C++中的动态内存分配
new int;
new int[4];
delete p;
delete []p;
memset
memcpy
引用
int a{ 5250 }; int& la{ a };
指针和引用(三)
智能指针
std::unique_ptr<int> intPtr{new int {5}};
std::unique_ptr<int[]> intPtr{new int[5] {5,4,3,2,1}};
std::unique_ptr<int[]> intPtr {std::make_unique<int[]>(5)};
std::unique_ptr<int> intPtr {std::make_unique<int>(5)};
u.release() | u放弃对指针的控制权,返回指针,并将u置空 |
---|---|
u.reset() | 释放u指向的对象,并将u置为空 |
u.reset(q) | 释放u原来指向的对象,令u指向这个q |
std::move()
std::shared_ptr<int> ptrA{std::make_shared<int>(1)};
std::cout << ptrA.use_count() << std::endl;
std::cout << ptrA.unique() << std::endl; C++17以后用不了了
reset()会将当前共享指针设置成nullptr,同时如果当前智能指针是最后一个拥有该指针的对象,那么将会释放内存。
字符处理
wchar_t wstr[0xFF]{ L"Hello张三" }; for (int i = 0; wstr[i]; i++) { std::cout << std::hex << (short)wstr[i] << std::endl; }
默认是GBK编码,中文占两个字节,英文占一个字节。
wchar_t为宽字节,UniCode编码,unicode为每种语言中的每个字符设定了统一并且唯一的二进制编码
所以要学会设置编码
#include<locale>
setlocale(LC_ALL, "chs");
scanf_s("%s",Name,5); //限制5个字符
wscanf_s(L"%s",wstr,可接受的最大字符值);
setlocale(LC_ALL, "chs");
wchar_t wstr[0xFF];
std::wcout << L"请输入您的名字" << std::endl;
std::wcin >> wstr;
std::wcout << wstr;
strlen()
wcslen()
联合体
基本语法
union USER
{
short sHP;
int nHP;
double fHP;
};
Union用来共享内存,内存大小取决于占内存最大的成员。
union USER
{
short sHP;
int nHP;
double fHP;
};
int main()
{
USER user;
std::cout << sizeof(user)<<std::endl;
user.nHP = 0;
user.sHP = -1;
std::cout << user.sHP << std::endl;
std::cout << user.nHP << std::endl;
执行user.sHp = -1后,user内存空间
内存分布为
11111111 11111111
为0xFF
即11111111 1111111
即为-1
但是读user.nHp时为4个字节
内存分布为
11111111 11111111 00000000 00000000
为0X00FF
即00000000 00000000 11111111 1111111
即为65535
匿名union
union
{
short sHP;
int nHP;
double fHP;
} ls;
缺点就是只能用一次
结构体也可以这样用
struct
{
int xp;
} ls;
字符串
string
std::string是C++提供的字符串。
std::string str{"哈哈哈"}
std::string 变量名称{"字符串",要截取的长度}
std::string 变量名称{"字符串",起始位置,要截取的长度}
std::string 变量名称(要复制的个数,'字符');
std::to_string();支持各种类型
还有 substring length append 等等操作
str.c_str()和str.data()一模一样 返回一个 const char*
但是在C++17标准后,str.data()返回的是char*
C字符串char*以0结尾,而string有专门记录其长度的属性,在实现的时候,没有严格要求以0结尾。
但是在C++11后,string也要求以0结尾
函数
指针可以传入nullptr空指针,而引用不可以
Role* &beAct 指针类型的引用
main函数的另一面:
argcount 参数个数
c_arg[] 程序的路径及文件名
不定量参数
#include<iostream>
#include<cstdarg>
int Average(int count, ...)
{
//va_list arg;
char* arg;
va_start(arg, count);
int sum{};
for (int i = 0; i < count; i++)
{
sum += va_arg(arg, int);
}
sum /= count;
va_end(arg);
return sum;
}
int main()
{
int x = Average(5, 53, 2, 321, 3, 321);
std::cout << "平均数:" << x;
}
函数是一个内存地址
函数功能是数据
函数指针:
int (*pAdd) (int,int) = Add
类型转换
char (*pAdd)(int,int) = (char (*)(int,int))Add;
声明一个函数指针
typedef char(*pfAdd)(int,int);
using pfAdd = char(*)(int,int);
函数模板
#include<iostream>
template <typename type1>
type1 ave(type1 a, type1 b, type1 c)
{
return (a + b + c) / 3;
}
int main() {
std::cout << ave(12.0f, 250.5f, 36.395f) << std::endl;
std::cout << ave(11,12,13) << std::endl;
std::cout << ave((char)11,(char)12,(char)13) << std::endl;
std::cout << ave<int>(12.0f,250.f,36.395f) <<std::endl;
}
编译器
OneDefinition Rule
#define
#define SHOW(X,Y) void X##Y(){std::cout<<#X;}
SHOW(test, 321321)
test321321();
namespace
全局命名空间 ::p;
assert
#define NDEBUG
可以在当前转换单元关闭assert
static_assert();
控制台API
Windows编程讲什么
API:Application Programming Interface 应用程序编程接口
Windows32API官网:
https://learn.microsoft.com/zh-cn/windows/win32/apiindex/windows-api-list
创建游戏窗口
SMALL_RECT mRect{ 0,0,40-1,20-1 };
if (SetConsoleWindowInfo(hdlOut, FALSE, &mRect)) printf("设置窗口大小成功\n");
FALSE就是窗口相对于缓冲区的上下左右
而TRUE是绝对值,全部基于左上角来计算
if (SetConsoleScreenBufferSize(hdlOut, cRd)) printf("缓冲区设置成功");
设置缓冲区大小
光标与字体
由于基础部分大多本人都熟悉,笔记就没有认真去详细记录,后面只记录和总结较为复杂的知识点,基础部分直接跳过。
Windows编程-线程
多线程开发中经常面临线程同步的问题,这时候就需要对公有操作部分进行上锁处理,windows中的加锁方案实在是太多太杂了,特此专门写一篇以便区分。
互斥量
HANDLE CreateMutex( LPSECURITY_ATTRIBUTESlpMutexAttributes, // 指向安全属性的指针 BOOLbInitialOwner, // 初始化互斥对象的所有者 LPCTSTRlpName // 指向互斥对象名的指针 );
HANDLE hMutex = CreateMutex(NULL,TRUE,NULL);
当前线程调用该互斥对象次数为1,递归计数器设置为1
WaitForSingleObject(hMutex,INFINITE);
当前线程调用该互斥对象次数为2,递归计数器+1,为2
ReleaseMutex(hMutex);//释放占用互斥对象的线程的拥有权,递归计数器-1,递归计数器值为1
ReleaseMutex(hMutex);//释放占用互斥对象的线程的拥有权,递归计数器-1,递归计数器值为0
//递归计数器值为0,互斥对象处于触发/有信号/已通知状态,可以在此为其他线程所占用
lpMutexAttributes SECURITY_ATTRIBUTES,指定一个SECURITY_ATTRIBUTES结构,或传递零值(将参数声明为ByVal As Long,并传递零值),表示使用不允许继承的默认描述符 。
bInitialOwner Long,初始化互斥对象的所有者。TRUE:占有,FALSE:不占有。 如希望立即占有该互斥量,则设为TRUE。 操作系统记录线程ID,将递归计数器设置为1,互斥量处于未触发/无信号/未通知状态。一个互斥体同时只能由一个线程拥有 。 为FALSE,则线程ID为NULL,操作系统将递归计数器设置为0,互斥量处于触发/有信号/已通知状态,不为任何线程所占用 。
lpName String,指向互斥对象名的指针,设置互斥对象的名字。创建一个未命名的互斥体对象。如已经存在这个名字,则打开已命名互斥体 。有了名字就可以跨进程得到同一把锁。
事件对象
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes,// 安全属性 BOOL bManualReset,// 复位方式 BOOL bInitialState,// 初始状态 LPCTSTR lpName // 对象名称 );
HANDLE hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);//创建对象,手动复位,初始化时无信号
WaitForSingleObject(hEvent,INFINITE);
setEvent(hEvent); //把事件设置为有信号状态
ResetEvent(hEvent); //把事件设置为无信号状态
信号量对象
- 信号量是操作系统提供的一种协调共享资源访问的方法。信号量则由操作系统进行管理,地位高于进程,操作系统保证信号量的原子性。
- 信号量(英语:semaphore)又称为信号标或者信号灯,是一个同步对象,用于保持在0至指定最大值之间的一个计数值。当线程完成一次对该semaphore对象的等待(wait)时,该计数值减一;当线程完成一次对semaphore对象的释放(release)时,计数值加一。当计数值为0,则线程等待该semaphore对象不再能成功直至该semaphore对象变成signaled状态。semaphore对象的计数值大于0,为signaled状态;计数值等于0,为nonsignaled状态。
HANDLE WINAPI CreateSemaphore(
In_opt LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,//指向SECURITY_ATTRIBUTES的指针;
In LONG lInitialCount, //信号量对象的初始值;
In LONG lMaximumCount, //信号量对象的最大值,这个值必须大于0;
In_opt LPCTSTR lpName //信号量对象的名称;
);
HANDLE g_hSemaphore = CreateSemaphore(NULL //信号量的安全特性
, 1 //设置信号量的初始计数。可设置零到最大值之间的一个值
, 1 //设置信号量的最大计数
, NULL //指定信号量对象的名称
);
WaitForSingleObject(g_hSemaphore, INFINITE); //信号量值-1
ReleaseSemaphore(g_hSemaphore, 1, NULL); //信号量值+1
我们以windows api的接口为例,讲解下信号量是如何在进程A和进程B间做到进程间同步的。
1、进程A过程 1.1、CreateSemaphore():创建一个名字为Semaphore的信号量,该信号量初始可使用的资源数为0。即S=0. 1.2、WaitForSingleObject():等待信号量>0,就是等待信号量的资源数大于0时。成功后就是S–。(启动进程A后,此处会一直等待,因为创建的信号量初始的值=0,直到进程B打开进程A的信号量,并且释放一个可以使用的资源时,S变成1,才可以继续,进行后面的程序) 1.3、在屏幕打印文字。 1.4、ReleaseSemaphore():释放上面wait成功时占用的1个资源数。执行成功后就是S++。 1.5、等待5s。 2、进程B过程 2.1、OpenSemaphore():打开进程A创建的信号量,名字为Semaphore 2.2、ReleaseSemaphore():递增信号量的当前资源计数,就是S++。S=1 2.3、WaitForSingleObject():等待信号量>0,就是等待信号量的资源数大于0时。成功后就是S–。 2.4、在屏幕打印文字。 2.5、ReleaseSemaphore():释放上面wait成功时占用的1个资源数。成功后就是S++。 2.6、等待5s。
关键代码段
很多人对CRITICAL_SECTION的理解是错误的,认为CRITICAL_SECTION是锁定了资源,其实,CRITICAL_SECTION是不能够“锁定”资源的,它能够完成的功能,是同步不同线程的代码段。简单说,当一个线程执行了EnterCritialSection之后,cs里面的信息便被修改,以指明哪一个线程占用了它。而此时,并没有任何资源被“锁定”。不管什么资源,其它线程都还是可以访问的(当然,执行的结果可能是错误的)。只不过,在这个线程尚未执行LeaveCriticalSection之前,其它线程碰到EnterCritialSection语句的话,就会处于等待状态,相当于线程被挂起了。 这种情况下,就起到了保护共享资源的作用。
CRITICAL_SECTION cs;
EnterCriticalSection(&cs);
...
// 操作dwTime
...
LeaveCriticalSection(&cs);
Windows编程-进程
剪切板通信
存储数据到剪切板
OpenClipboard();
EmptyClipboard();
char* szSendBuf;
HANDLE hClip = GlobalAlloc(GMEM_MOVEABLE,strSend.GetLength()+1);
szSendBuf = (char*)GlobalLock(hClip);
strcpy(szSendBuf,"复制的文字");
GlobalUnLock(hClip);
SetClipboardData(CF_TEXT,hClip);
CloseClipboard();
从剪切板中读取数据
OpenClipboard();
IsClipboardFormatAvailable(CF_TEXT);
char* szRecvBuf;
HANDLE hClip = GetClipboardData(CF_TEXT);
szRecvBuf = (char*)GlobalLock(hClip);
GlobalUnlock(hClip);
邮槽通信
接收端
LPCTSTR szSlotName = TEXT("\\\\.\\mailslot\\Mymailslot");
HANDLE hSlot = CreateMailslot(szSlotName,0,MAILSLOT_WAIT_FOREVER,NULL);
if(hSlot == INVALID_HANDLE_VALUE)
{
TRACE("CreateMailslot failed with %d\n",GetLastError());
return;
}
char szBuf[100] = {0};
DWORD dwRead;
if(!ReadFile(hSlot,szBuf,100,&dwRead,NULL))
{
MessageBox("Read Failed!!!");
return;
}
MessageBox(szBuf);
CloseHandle(hSlot);
发送端
LPCTSTR szSlotName = TEXT("\\\\.\\mailslot\\Mymailslot");
HANDLE hMailSlot = CreateFile(szSlotName, FILE_GENERIC_WRITE, FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if (hMailSlot == INVALID_HANDLE_VALUE)
{
TRACE("CreateFile failed with %d\n",GetLastError());
return;
}
char szBuf[100] = "MailSlot Comming";
DWORD dwWrite;
if (!WriteFile(hMailSlot,szBuf,strlen(szBuf)+1,&dwWrite,NULL))
{
MessageBox("WriteFile Failed!!");
return;
}
CloseHandle(hMailSlot);
无名管道通信
主进程创建管道
SECURITY_ATTRIBUTES sa;
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
if (!CreatePipe(&hReadPipe,&hWritePipe,&sa,0))
{
MessageBox("匿名管道创建失败");
return;
}
STARTUPINFO strStartupInfo;
memset(&strStartupInfo, 0, sizeof(strStartupInfo));
strStartupInfo.cb = sizeof(strStartupInfo);
strStartupInfo.dwFlags = STARTF_USESTDHANDLES;
strStartupInfo.hStdInput = hReadPipe;
strStartupInfo.hStdOutput = hWritePipe;
strStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
PROCESS_INFORMATION szProcessInformartion;
memset(&szProcessInformartion, 0, sizeof(szProcessInformartion));
int iRet = CreateProcess(_T("ProcessClient.exe"),
NULL,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&strStartupInfo,
&szProcessInformartion
);
if (iRet)
{
CloseHandle(szProcessInformartion.hProcess);
CloseHandle(szProcessInformartion.hThread);
szProcessInformartion.dwProcessId = 0;
szProcessInformartion.dwThreadId = 0;
szProcessInformartion.hThread = 0;
szProcessInformartion.hProcess = 0;
}
else
{
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
hReadPipe = NULL;
hWritePipe = NULL;
MessageBox("创建子进程失败");
return;
}
主进程发送
char szBuf[] = "This ";
DWORD dwWrite;
if (!WriteFile(hWritePipe,szBuf,strlen(szBuf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败");
return;
}
子进程接收
hReadPipe = GetStdHandle(STD_INPUT_HANDLE);
char szBuf[100] = { 0 };
DWORD dwRead;
if (!ReadFile(hReadPipe,szBuf,100,&dwRead,NULL))
{
MessageBox("ReadFile Failed!!!");
CloseHandle(hReadPipe);
return;
}
MessageBox(szBuf);
子进程发送,主进程接收同理。
一个是GetStdHandle(STD_OUTPUT_HANDLE);
一个是GetStdHandle(STD_INPUT_HANDLE);
命名管道通信
服务端创建命名管道
LPCTSTR szPipeName = TEXT("\\\\.\\pipe\\mypipe");
hNamedPipe = CreateNamedPipe(szPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 1, 1024, 1024, 0, NULL);
if (hNamedPipe == INVALID_HANDLE_VALUE)
{
TRACE("CreateNamedhPipe failed with %d\n", GetLastError());
MessageBox("创建命名管道失败");
return;
}
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == hEvent)
{
MessageBox("创建事件失败");
CloseHandle(hNamedPipe);
hNamedPipe = NULL;
return;
}
OVERLAPPED ovlap;
ZeroMemory(&ovlap, sizeof(OVERLAPPED));
ovlap.hEvent = hEvent;
//等待连接
if (!ConnectNamedPipe(hNamedPipe,&ovlap))
{
if (ERROR_IO_PENDING!=GetLastError())
{
MessageBox("等待客户端连接失败");
CloseHandle(hNamedPipe);
CloseHandle(hEvent);
hNamedPipe = NULL;
hEvent = NULL;
return;
}
}
if (WaitForSingleObject(hEvent,INFINITE)==WAIT_FAILED)
{
MessageBox("等待对象失败");
CloseHandle(hNamedPipe);
CloseHandle(hEvent);
hNamedPipe = NULL;
hEvent = NULL;
return;
}
客户端连接命名管道
LPCTSTR szNamedPipeName = TEXT("\\\\.\\pipe\\mypipe");
if (0 == WaitNamedPipe(szNamedPipeName,NMPWAIT_WAIT_FOREVER))
{
MessageBox("当前没有可以利用的管道");
return;
}
hNamedPipe = CreateFile(szNamedPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hNamedPipe == INVALID_HANDLE_VALUE)
{
TRACE("CreateFile failed with %d\n", GetLastError());
MessageBox("打开命名管道失败!");
hNamedPipe = NULL;
return;
}
接收端
char szBuf[100] = "Named Pipe Going Client";
DWORD dwRead;
BOOL bRet = ReadFile(hNamedPipe, szBuf, 100, &dwRead, NULL);
if (!bRet)
{
MessageBox("ReadFile Failed!!!");
CloseHandle(hNamedPipe);
return;
}
MessageBox(szBuf);
发送端
char szBuf[100] = "Named Pipe Going Client";
DWORD dwWrite;
if (!WriteFile(hNamedPipe,szBuf,strlen(szBuf)+1,&dwWrite,NULL))
{
MessageBox("WriteFile Failed!!!");
CloseHandle(hNamedPipe);
return;
}
WM_COPYDATA通信
最常用的通信方式
接收端
资源视图 --> 类向导 --> 消息 --> WM_COPYDATA
BOOL CServerReviewDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
LPCTSTR szText = (LPCTSTR)(pCopyDataStruct->lpData);
DWORD dwLength = (DWORD)pCopyDataStruct->cbData;
TCHAR szRecvText[1024] = { 0 };
memcpy(szRecvText, szText, dwLength);
MessageBox(szRecvText, "Y", MB_OK);
return CDialogEx::OnCopyData(pWnd, pCopyDataStruct);
}
发送端
CString strWindowsTitle = "服务端";
CString strMsg = "COPYDATA";
HWND hWnd = ::FindWindow(NULL, strWindowsTitle);
if (hWnd != NULL && IsWindow(hWnd))
{
COPYDATASTRUCT cpd;
cpd.dwData = 0;
cpd.cbData = strMsg.GetLength() * sizeof(TCHAR);
cpd.lpData = (PVOID)strMsg.GetBuffer(0);
::SendMessage(hWnd, WM_COPYDATA, (WPARAM)(AfxGetApp()->m_pMainWnd), (LPARAM)&cpd);
}
strWindowsTitle.ReleaseBuffer();
strMsg.ReleaseBuffer();
Windows编程-文件&注册表
创建文件和读取文件的4种方法
c语言
FILE* pFile = fopen("1.txt", "w"); //w重写文件 a追加内容
if (pFile == NULL)
{
MessageBox("打开失败");
return;
}
char szBuf[1024] = "C语言文件操作";
int iLen = strlen(szBuf) + 1;
if (fwrite(szBuf, 1, strlen(szBuf) + 1, pFile) <= 0)
{
MessageBox("写入失败");
return;
}
fclose(pFile);
FILE* pFile = fopen("1.txt", "r");
if (pFile == NULL)
{
MessageBox("打开失败");
return;
}
char szBuf[1024] = { 0 };
//fseek函数 求文件的偏移量
fseek(pFile,0,SEEK_END);
int iFileLen = ftell(pFile); //得到文件指针的当前位置
fseek(pFile, 0, SEEK_SET);
int iLen = fread(szBuf,sizeof(char), iFileLen, pFile);
fclose(pFile);
MessageBox(szBuf);
c++
#include<io.h>
#include<fstream>
std::ofstream ofs("2.txt"); //表示文件名
char szBuf[1024] = "C++操作文件";
ofs.write(szBuf,strlen(szBuf));
ofs.close();
std::ifstream ifs("2.txt"); //表示文件名
char szBuf[1024] = { 0 };
ifs.read(szBuf, strlen(szBuf));
ifs.close();
MessageBox(szBuf);
windowsApi
HANDLE hFile;
hFile = CreateFile("3.txt",GENERIC_WRITE,NULL,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
MessageBox("创建文件对象失败");
return;
}
//写文件
DWORD dwWrites;
char szBuf[1024] = "WIN API 操作文件";
WriteFile(hFile,szBuf,strlen(szBuf),&dwWrites,NULL);
TRACE("######dwWrites = %d\n", dwWrites);
CloseHandle(hFile);
HANDLE hFile;
hFile = CreateFile("3.txt", GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
MessageBox("创建文件对象失败");
return;
}
//读文件
DWORD dwWrites;
char szBuf[1024] = { 0 };
ReadFile(hFile, szBuf, 1024, &dwWrites, NULL);
TRACE("######dwWrites = %d\n", dwWrites);
CloseHandle(hFile);
MessageBox(szBuf);
mfc函数
CFile file("4.txt", CFile::modeCreate | CFile::modeWrite);
char szBuf[1024] = "MFC操作文件";
file.Write(szBuf, strlen(szBuf));
file.Close();
CFile file("4.txt", CFile::modeRead);
char szBuf[1024] = {0};
DWORD dwFilelen;
dwFilelen = file.GetLength();
file.Read(szBuf, dwFilelen);
file.Close();
MessageBox(szBuf);
对话框读取文件
CFileDialog fileDlg(TRUE);
fileDlg.m_ofn.lpstrTitle = "Test";
fileDlg.m_ofn.lpstrFilter = "Text Files(*.txt)\0*.txt\0All File(*.*)\0*.*\0\0";
if (IDOK == fileDlg.DoModal())
{
CFile file(fileDlg.GetFileName(), CFile::modeRead);
char szBuf[1024] = { 0 };
DWORD dwFilelen;
dwFilelen = file.GetLength();
file.Read(szBuf, dwFilelen);
file.Close();
MessageBox(szBuf);
}
配置项的读写方法
//写配置项
char szPath[MAX_PATH] = { 0 };
GetCurrentDirectory(MAX_PATH, szPath);
char szMyPath[MAX_PATH] = { 0 };
sprintf(szMyPath, "%s\\Test.ini", szPath);
WritePrivateProfileString("Param1", "QueryInterval", "3600", szMyPath);
WritePrivateProfileString("Param1", "CheckInterval", "4000", szMyPath);
WritePrivateProfileString("Param2", "PopupInterval", "3000", szMyPath);
char szPath[MAX_PATH] = { 0 };
GetCurrentDirectory(MAX_PATH, szPath);
char szMyPath[MAX_PATH] = { 0 };
sprintf(szMyPath, "%s\\Test.ini", szPath);
char str1[MAX_PATH] = { 0 };
char str2[MAX_PATH] = { 0 };
char str3[MAX_PATH] = { 0 };
GetPrivateProfileString("Param1","QueryInterval",NULL, str1,1024,szMyPath);
GetPrivateProfileString("Param1","CheckInterval",NULL, str2,1024,szMyPath);
GetPrivateProfileString("Param2","PopupInterval",NULL, str3,1024,szMyPath);
CString strShow;
strShow.Format("QueryInterval = %s CheckInterval = %s PopupInterval = %s",str1,str2,str3);
MessageBox(strShow);
注册表的读写方法
HKEY hKey;
DWORD dwWeight = 80;
DWORD dwRet = ::RegCreateKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\MYWEIGHT\\admin", &hKey);
if (dwRet != ERROR_SUCCESS)
{
MessageBox("创建注册表失败");
return;
}
//写注册表
dwRet = ::RegSetValueEx(hKey, "weight", NULL, REG_DWORD, (CONST BYTE*) & dwWeight, 4);
if (dwRet != ERROR_SUCCESS)
{
MessageBox("设置注册表失败");
}
::RegCloseKey(hKey);
HKEY hKey;
DWORD dwRet = ::RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\MYWEIGHT\\admin", &hKey);
if (dwRet != ERROR_SUCCESS)
{
MessageBox("打开注册表失败");
}
DWORD dwWeight;
DWORD dwType;
DWORD dwSize;
dwRet = ::RegQueryValueEx(hKey,"weight",NULL,&dwType , (LPBYTE) & dwWeight, &dwSize);
CString strShow;
strShow.Format("Weight = %d", dwWeight);
if (dwRet != ERROR_SUCCESS)
{
MessageBox("读注册表失败");
return;
}
::RegCloseKey(hKey);
MessageBox(strShow);
Windows编程-动态链接库
创建动态链接库
暴露dll函数的两个方法
方法一:使用_declspec(dllexport)声明,extern “C” 是为了保证暴露的函数名为ave而非乱码
extern "C" _declspec(dllexport) int ave(int a, int b)
{
return (a + b) / 2;
}
方法二:创建模块定义文件.def,并且声明函数名,不可以有重载 链接器->输入->模块定义文件
LIBRARY
EXPORTS
ave
WinAPI往往会加上 __stdcall 函数名会变成 _ave@8
告诉编译器
#pragma comment (linker,"/export:ave=?_ave@8")
使用动态链接库
typedef int (*ave1)(int a, int b);
typedef int (WINAPI* ave)(int a, int b);
HMODULE hMod = LoadLibraryA("2.dll");
ave func = (ave1)GetProcAddress(hMod,"ave");
ave1 func1 = (ave1)GetProcAddress(hMod,"ave1");
std::cout << func(200,300) << std::endl;
std::cout << func1(200,300) << std::endl;
Windows编程-MFC
画布
CDC* pDC = GetDC();
pDC->MoveTo(m_start);
pDC->LineTo(m_stop);
ReleaseDC(pDC);
InvalidateRect(NULL); //触发OnDraw
画笔
CPen pen(PS_DOT, 1, RGB(255, 0, 0));
CPen* pOldPen = pDC->SelectObject(&pen);
画刷
CBrush brush(RGB(255, 0, 0));
CBrush* pOldBrush = pDC->SelectObject(&brush);
pDC->FillRect(CRect(m_start, m_cur), &brush);
光标和文本
CSize sz = pDC->GetTextExtent();
CPoint pt;
SetCaretPos(pt);
pDC->TextOut();
Linux系统编程
字符串函数
头文件<ctype.h>
测试字符是否为英文字母或数字:
int isalnum(int c)
测试字符是否为英文字母:
int isalpha(int c)
测试字符是否为 ASCII 码字符:
int isascii(int c)
测试字符是否为空格字符:
int isblank(int c)
测试字符是否为 ASCII 码的控制字符:
int iscntrl(int c)
测试字符是否为阿拉伯数字:
int isdigit(int c)(int c)
测试字符是否为可打印字符:
int isgraph(int c)
测试字符是否为小写英文字母:
int islower(int c)
测试字符是否为可打印字符:
int isprint(int c)
测试字符是否为空格字符:
int isspace(int c)
测试字符是否为标点符号或特殊符号:
int ispunct(int c)
测试字符是否为大写英文字母:
int isupper(int c)
测试字符是否为 16 进制数字:
int isxdigit(int c)
数据转换函数
头文件<stdlib.h>
将字符串转换成浮点型数:
double atof(const char*)
将字符串转换成整型数:
int atoi(const char*)
将字符串转换成长整型数:
long int atol(const char*)
将字符串转换成 64 位整数(C++11)
long long int atoll ( const char * str )
将字符串转换成整数
long int strtol (const char* str, char** endptr, int base)
将字符串转换成无符号整数
unsigned long int strtoul (const char* str, char** endptr, int base)
将字符串转换成长整数(C++11)
long long int strtoll (const char* str, char** endptr, int base)
将字符串转换成无符号长整数(C++11)
unsigned long long int strtoull (const char* str, char** endptr, int base)
将字符串转换成浮点数(C++11)
float strtof (const char* str, char** endptr)
将字符串转换成双精度数
double strtod (const char* str, char** endptr)
将字符串转换成长双精度数(C++11)
long double strtold (const char* str, char** endptr)
将浮点型数转换成字符串:
char* ecvt(double value, int ndigit, int *decpt, int *sign)
ndigit 指的是全部的有效位数
将浮点型数转换为字符串:
char *fcvt(double value, int ndigit, int *decpt, int *sign)
ndigit 指的是小数点之后的有效位数
将浮点型数转换为字符串:
char *gcvt(double value, int ndigit, char *buf)
ndigit 指的是最大有效位数