1、类型不同 C语言是面向过程的,而C++是面向对象的。 2、函数库不同 C语言的标准的函数库很松散,而C++对于大多数的函数都是集成的很紧密。 3、结构不同 C语言中结构只有成员变量,而在C++中结构中,可以有成员变量和成员函数。 4、两者编译组成不同 汇编语言是将由0、1组成的机器语言用具有简单语义的英文代码表示,而C语言不但将许多相关的机器指令合成为单条指令,并且去掉了与具体操作有关但与完成工作无关的细节,例如使用堆栈、寄存器等。 5、两者被计算机识别的路径不同 汇编语言通常用于对硬件的直接操控。而且C语言所编制的程序不能直接被计算机识别,必须经过转换才能被执行。 7、两者学习难易程度不同 汇编语言所需要的编绘知识很多很复杂,经常被开发者使用。而C语言是一门很简单方便的语言。计算机语言(Computer Language)指用于人与计算机之间通讯的语言。计算机语言是人与计算机之间传递信息的媒介。
项目需要在触摸屏上增加一个虚拟键盘。记录下过程中遇到的问题及解决方法。 1.模拟按键 网上找到如下3种方法 1)SendKeys.Send 测试单独的shift不好用,所以最终未采纳此方法 SendKeys.Send("^{E}");//shift+e SendKeys.Send("{Enter}"); 2)keybd_event 最终选择了这个方法,简单有效。 虚拟按键对照表:https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes [DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true)] public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo); [DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true)] public static extern void keybd_event(Keys bVk, byte bScan, uint dwFlags, uint dwExtraInfo); public static uint KEYEVENTF_KEYDOWN = 0;// 键按下 KEYEVENTF_KEYDOWN = 0 public static uint KEYEVENTF_KEYUP = 2;//键弹起 KEYEVENTF_KEYUP = 2 keybd_event(0x14, 0, KEYEVENTF_KEYDOWN, 0); //键按下 KEYEVENTF_KEYDOWN = 0 keybd_event(0x14, 0, KEYEVENTF_KEYUP, 0); //键弹起 KEYEVENTF_KEYUP = 2 3)PostMessage 这个方法也是简单有效,但是据说有些其他问题,没有仔细研究,有兴趣的可以自己研究。 [DllImport("user32.dll", EntryPoint = "PostMessageA", SetLastError = true)] public static extern int PostMessage(IntPtr hWnd, int Msg, Keys wParam, int lParam); PostMessage(textBox2.Handle, 256, Keys.D, 2);//模拟按下2次 Keys.D 2.各种类型键的处理 微软对键盘输入有进行了分类,可以参考这个。 https://learn.microsoft.com/zh-cn/windows/win32/learnwin32/keyboard-input 1)CapsLock键/NumLock键 想要实现键盘按下抬起与界面效果相同,就需要捕获键盘操作。我使用了Hook 以下这篇博客写的很清晰明了,就不赘述了。 https://www.cnblogs.com/chorm590/p/14199978.html 在hook回调函数中做了如下处理 private int keyboardHookCallback(int...
.Net的私有属性、成员变量、方法,都可以通过反射获取调用,当然正常我们不会这么操作 直接上例子,先定义一个类 public class TenantModel { public int Id { get; init; }//属性,未定义成员变量会自动生成 public string Name { get; set; }//属性 private string password;//成员变量 public string Password//属性 { private get //方法(属性里的get;set;均为方法,或者自己定义一个方法测试) { return password; } set { if (value.Length < 6) throw new Exception("密码需要大于6位"); password = value; } } } 然后利用反射,获取到私有的password信息 1)通过对象进行反射 var te = new TenantModel() { Id = 1, Name = "kxy", Password = "1234567" }; Type type = te.GetType(); //te.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);//获取所有私有方法 MethodInfo func1 = type.GetMethod("get_Password", BindingFlags.NonPublic | BindingFlags.Instance);//获取私有方法 var str = func1.Invoke(te, null).ToString();//执行方法 FieldInfo field = type.GetField("password", BindingFlags.NonPublic | BindingFlags.Instance);//获取私有成员变量 string pwd = field.GetValue(te)?.ToString();//取值 field.SetValue(te, "123");//赋值,直接操作成员变量,可以跳过验证 这样就简单实现了一个反射读取私有信息的案例,当然还可以通过程序集反射 2)通过程序集进行反射(因为实例化) 反射也是通过构造函数实例化的,默认为无参,也可以带参,为了展示,我们多定义一个带参构造函数 public TenantModel(int id,string name,string password) { Id = id; Name = name; Password = password; } 然后,反射代码如下: Assembly...
初始化列表构造函数VS普通构造函数 初始化列表构造函数最优先匹配问题 对于一个类而言,只要其中包含有初始化列表的构造函数,编译器在编译使用{}语法的构造时会最倾向于调用初始化列表构造函数,哪怕做类型转换也在所不惜,哪怕有类型最佳匹配的普通构造函数或移动构造函数也会被劫持 class Widget { public: Widget(int i, bool b); Widget(int i, double d); Widget(std::initializer_list<long double> il); operator float() const; }; Widget w1(10, true); // 使⽤小括号初始化 //调⽤第⼀个构造函数 Widget w2{10, true}; // 使⽤花括号初始化 // 调⽤第三个构造函数 // (10 和 true 转化为long double) Widget w3(10, 5.0); // 使⽤小括号初始化 // 调⽤第二个构造函数 Widget w4{10, 5.0}; // 使⽤花括号初始化 // 调⽤第三个构造函数 // (10 和 5.0 转化为long double) Widget w5(w4); // 使⽤小括号,调⽤拷⻉构造函数 Widget w6{w4}; // 使⽤花括号,调⽤std::initializer_list构造函数 Widget w7(std::move(w4)); // 使⽤小括号,调⽤移动构造函数 Widget w8{std::move(w4)}; // 使⽤花括号,调⽤std::initializer_list构造函数 编译器这种热衷于把括号初始化与初始化列表构造函数匹配的行为,会导致一些莫名其妙的错误 class Widget { public: Widget(int i, bool b); Widget(int i, double d); Widget(std::initializer_list<bool> il); // element type is now bool … // no implicit conversion funcs }; Widget w{10, 5.0}; //错误!要求变窄转换,int(10)double(5.0)无法转换为bool类型 只有在没有办法把{}中实参的类型转化为初始化列表时,编译器才会回到正常的函数决议流程中 ⽐如我们在构造函数中⽤std::initializer_list<std::string>代替std::initializer_list<bool> ,这时⾮std::initializer_list构造函数将再次成为函数决议的候选者,因为没有办法把int和bool转换为std::string: class Widget { public: Widget(int i, bool b); Widget(int...
在判定机器采用大端还是小端存储时,可以按字节输出某数据对象的机器表示的位模式。机器表示的位模式即某数据对象在内存中的二进制串。下面是访问数据对象位模式的一个方法: //传入一个数据对象,从低地址到高地址按字节输出这个对象的每字节的十六进制表示 void printByte(unsigned char* a,int n) { for( int i=0; i<n; i++ ) { printf("%x ",a[i]); } printf("\n"); } 上面的printByte函数的功能是:以参数*a传入一个数据对象的地址,以及你想打印的以这个地址为起始地址的连续的字节数量n,printByte打印出这连续的n个字节内存的位模式的十六进制表示。我们使用了一个unsigned char* 类型的指针变量,它以一字节大小解读所指向的数据,并以”%x”格式输出这个一字节大小的数据。 我们定义一个int变量并打印它的位模式: int x = 128; printByte((unsigned char*)&x,sizeof(int)) 可以看到输出为: 上面的输出也表示,这里实验的机器使用小端存储,即数据对象的低位部分存储在低地址部分。 在printByte函数中,需要注意:char* 型的指针指向的同样是一字节大小的数据,但此处一定要用unsigned char* 类型,而不能用char* 类型。因为”%x”是以十六进制形式输出int型的变量,所以,如果printByte的参数设置为char* 类型,在使用%x输出时,会将char类型隐式转换为int类型。此时,在上面的例子中,x值为128,其低8位的位模式为:1000 0000,即0x80,对于char型变量,其真值为-128。转换成int型变量后,位模式为:1111 1111 1111 1111 1111 1111 1000 0000 (补码编码),即0xffffff80: void printByte(char* a,int n) { for( int i=0; i<n; i++ ) { printf("%x ",a[i]); } printf("\n"); } int main() { int x = 128; printByte((char*)&x,sizeof(int)); return 0; } 所以,在这个方法打印内存的位模式时,参数指针一定是unsigned char* 类型,而不能是char*类型。
一直以来是一个关注效率的代码,这样关于函数的参数传递和返回值的接收,是重中之重。下文提供了一些个人的见解。 函数存储位置 函数参数在编译期展开,目前各平台的编译期均有不同。 名称 存储位置 函数名称和逻辑 代码段存储 函数参数和返回值 栈中或者寄存器(64位会有6个寄存器使用) new malloc 的变量 堆 函数参数入栈顺序 微软有几种编译期属性,用来定义函数参数的顺序和堆栈。 关键字 堆栈清理 参数传递 __cdecl 调用方 在堆栈上按相反顺序推送参数(从右到左) __clrcall 不适用 按顺序将参数加载到 CLR 表达式堆栈上(从左到右)。 __stdcall 被调用方 在堆栈上按相反顺序推送参数(从右到左) __fastcall 被调用方 存储在寄存器中,然后在堆栈上推送 __thiscall 被调用方 在堆栈上推送;存储在 ECX 中的 this 指针 __vectorcall 被调用方 存储在寄存器中,然后按相反顺序在堆栈上推送(从右到左) 所以直接在函数参数上,调用表达式和函数来回去值的话,非常危险 初始化列表 class Init1 { public: void Print() { std::cout << a << std::endl; std::cout << b << std::endl; std::cout << c << std::endl; } int c, a, b; }; A这个类,可以通过 A a{1,2,3}; 来初始化对象。 看着很美好,但是有几个问题需要注意。 参数是的入栈顺序是跟着类的属性的顺序一致, 当前是 c, a, b; int i = 0; Init1 a = {i++, i++, i++}; a.Print(); 当我如此调用的时候,得到的返回值是 1 2 0 i++的执行顺序是从左到右,跟函数调用顺序无关。 另外不能有 构造函数 class Init1 { public: Init1(int ia, int ib, int ic) { std::cout << "construct" << std::endl; a = ia; b =...