vlambda博客
学习文章列表

c++ c语言 - 控件及概述补充

C语言 基础概述2 - 控件及概述补充

dll 依赖库

一、dll概念

DLL:Dynamic Link Library,即动态链接库,这种库包含了可由多个程序同时使用的代码和数据。

它是microsoft在windows操作系统中实现共享函数库概念的一种实现方式。其中windows中 一些作为DLL实现的文件有:

  • ActiveX控件(.ocx)文件:如windows上的日历控件。
  • 控制面板(.cpl)文件:控制面板中的每一项都是一个专用的DLL。
  • 设备驱动程序(.drv)文件:如控制打印到打印机的打印机驱动程序。

DLL最初用于节约应用程序所需要的磁盘和内存空间。早前,在传统的非共享库中,一部分代码简单地附加到调用的程序中。如果两个程序同时调用同一个子程序,就会出现两份那段代码。相反,许多应用共享的代码能够切分到一个DLL中,在硬盘上存为一个文档,在内存中只需使用一个实例。

二、dll 优缺点

优点:

(1)节省内存和代码重用:当多个程序使用同一个函数库时,DLL可以减少在磁盘和物理内存中加载代码的重复量,且有助于代码的重用。

(2)模块化:DLL有助于促进模块式程序开发。模块化允许仅仅更改几个应用程序共享使用的一个DLL中的代码和数据而不需要更改应用程序自身。这种模块话的基本形式允许如Microsoft Office、Microsoft Visual Studio、甚至windows自身这样大的应用程序 使用较为紧凑的补丁和服务包。

缺点:

DLL Hell:即DLL地狱,指几个应用程序在使用同一个共享的DLL库时发生版本冲突。究其原因是因为共用。因为DLL Hell正是由于动态链接库可与其他程序共用函数、资源所导致。

三、dll与lib的关系

lib是静态链接库;DLL是动态链接库,一个编译时提供;一个运行时提供。

静态lib:它将导出声明(后面会讲)和实现均放到lib中,编译后所有代码都嵌入到宿主程序中去。

动态lib:相当于一个h文件,它是对实现部分(.DLL)的导出部分的声明。编译后只是将导出声明部分编译到宿主程序中,运行时需要相应的DLL文件的支持,否则无法工作。当生成一个新的DLL时,也会有配套的lib产生(即二者需一起分发),此时的lib即为动态lib(后面会有还有实验)。

四、如何生成一个DLL

在VS2012开发环境下,打开File\New\Project选项,可以选择Win32 Dynamic-Link Library或MFC AppWizard【dll】来以不同的方式创建Non-MFC DLL、Regular DLL、Extension DLL等不同种类的动态链接库。下面以选择Win32 Dynamic-Link Library方式来创建一个DLL(实现加法运算)。

// mydll.h file  
extern "C" _declspec(dllexport) int add(int a, int b);  
 
//mydll.cpp file  
#include "mydll.h"  
int add(int a, int b) //该DLL需要导出的函数功能:加法  
{  
   return a + b;  
}

(1)前面的 extern “C” 告诉编译器函数可以在本模块或其他模块中使用,其中“C”表明需按照C语言方式编译和连接它,因为C++编译时,会对函数名进行修饰,用于实现函数重载,而C里面没有这个功能,所以需要用extern "C"在头文件进行声明的时候加以区分,以便链接时能进行正确地函数名查找。

(2)_declspec(dllexport)为导出函数关键字,意为需从DLL中导出该函数,以便使用。

(3) 之后会有一个dll文件生成以供使用。

五、如何调用一个DLL

下面实现两种调用方式:单独.dll 和.h + .lib + .dll结合

注:需把对应的 .dll 文件以及.lib 文件和.h文件(结合方式时)拷贝至调用的程序目录下

1)单纯使用.dll
#include<wtypes.h>   
#include <winbase.h>   
#include <iostream>  
_declspec(dllimport) int Add(int a, int b)//导入声明,亦可以不加,如果加上可加快程序运行  
typedef int(*pAdd)(int a,int b);  
int main()  
{  
    HINSTANCE hDLL;  
    pAdd Add;  
    hDLL=LoadLibrary(L"mydll.dll");  //加载 DLL文件  
    if(hDLL == NULL)std::cout<<"Error!!!\n";  
    Add=(pAdd)GetProcAddress(hDLL,"add");  //取DLL中的函数地址,以备调用  
    int a =Add(5,8);  
    std::cout<<"a: "<<a<<std::endl;  
    FreeLibrary(hDLL);  
    return 0;  
}
2).h + .lib + .dll 结合方式
#include<wtypes.h>   
#include <winbase.h>   
#include <iostream>#include "../MyDll/mydll.h"  
#pragma comment(lib,"mydll.lib")  //将mydll.lib库文件连接到目标文件中(即本工程)  
extern "C"_declspec(dllimport) int add(int a,int b);  
int main()  
{  
    int a =add(5,8);  
    std::cout<<"a: "<<a<<std::endl;  
    return 0;  
}

CPU内部结构与寄存器

64位和32位系统的区别

  • 寄存器是CPU内部最基本的 存储单元。
  • CPU对外是通过总线(地址、控制、数据)来和外部设备交互的,总线的宽度,是8位,同时CPU的寄存器也是8位,那么这个CPU就叫8位CPU.
  • 如果总线是32位,寄存器也是32位的,那么这个CPU就是32位CPU。
  • 有一种CPU内部的寄存器是32位的,但总线是16位,准32为CPU.
  • 所有的64位CPU兼容32位的指令,32 位要兼容16位的指令,所以在64位的CPU.上是可以识别32位的指令。
  • 在64位的CPU构架上运行了64位的软件操作系统,那么这个系统是64位。
  • 在64位的CPU构架上,运行了32 位的软件操作系统,那么这个系统就是32位。
  • 64位的软件不能运行在32位的CPU之上。.

寄存器、缓存、内存关系

  • 按与CPU远近来分,离得最近的是寄存器,然后缓存(CPU缓存),最后内存。
  • CPU计算时,先预先把要用的数据从硬盘读到内存,然后再把即将要用的数据读到寄存器。
  • 于是CPU<--->寄存器<--->内存,这就是它们之间的信息交换。
  • 那为什么有缓存呢?因为如果经常操作内存中的同一址地的数据,就会影响速度。于是就在寄存器与内存之间设置一个缓存。
  • 因为从缓存提取的速度远高于内存。当然缓存的价格肯定远远高于内存,不然的话,机器里就没有内存的存在。
  • 由此可以看出,从远近来看: CPU <---> 寄存器. <---> 缓存<--->内存。

C语言嵌套汇编代码

#include <stdio.h>
int main(){
//    定义变量abc
 int a;int b;int c;
 __asm{
    mov a,3       //3的值放在a的内存位置
    mov b,4       //4的值放在b的内存位置
    mov eax,a     //a的内存值放在eax寄存器上
    add eax,b     // eax和b相加,依然把结果放在eax上
    mov c,eax     // eax寄存器放c上面
 }
 printf("%d\n",c);
 return 0
}

printf格式化输出

格式字符用以指定输出项的数据类型和输出格式。

  • d格式:用来输出十进制整数。有以下几种用法:

    • %d:按整型数据的实际长度输出。
    • %md:m为指定的输出字段的宽度。如果数据的位数小于m,则左端补以空格,若大于m,则按实际位数输出
  • o格式:以无符号八进制形式输出整数。对长整型可以用"%lo"格式输出。同样也可以指定字段宽度用“%mo”格式输出。

    • 例:

    • printf("%d, %o"-1-1);
    • 运行结果:-1,177777

    • 程序解析:-1在内存单元中(以补码形式存放)为(1111111111111111)2,转换为八进制数为(177777)8。

  • x格式:以无符号十六进制形式输出整数。对长整型可以用"%lx"格式输出。同样也可以指定字段宽度用"%mx"格式输出。

  • u格式:以无符号十进制形式输出整数。对长整型可以用"%lu"格式输出。同样也可以指定字段宽度用“%mu”格式输出。

  • c格式:输出一个字符。

  • s格式:用来输出一个串。有几中用法

    • %s:例如:printf("%s", "CHINA")输出"CHINA"字符串(不包括双引号)
    • %ms:输出的字符串占m列,如果字符串本身长度大于m,则突破获m的限制,将字符串全部输出。若串长小于m,则左补空格。
    • %-ms:如果串长小于m,则在m列范围内,字符串向左靠,右补空格。
    • %m.ns:输出占m列,但只取字符串中左端n个字符。这n个字符输出在m列的右侧,左补空格。
    • %-m.ns:其中m、n含义同上,n个字符输出在m列范围的左侧,右补空格。如果n>m,则自动取n值,即保证n个字符正常输出。
  • f格式:用来输出实数(包括单、双精度),以小数形式输出。有以下几种用法:

    • %f:不指定宽度,整数部分全部输出并输出6位小数。
    • %m.nf:输出共占m列,其中有n位小数,若数值宽度小于m左端补空格。
    • %-m.nf:输出共占m列,其中有n位小数,若数值宽度小于m右端补空格。
  • e格式:以指数形式输出实数。可用以下形式:

    • %e:数字部分(又称尾数)输出6位小数,指数部分占5位或4位。
    • %m.ne和%-m.ne:m、n和”-”字符含义与前相同。此处n指数据的数字部分的小数位数,m表示整个输出数据所占的宽度。
  • g格式:自动选f格式或e格式中较短的一种输出,且不输出无意义的零。

%a       浮点数、十六进制数字和p-记数法(C99)
%A    浮点数、十六进制数字和p-记法(C99)
%c    一个字符(char)
%C       一个ISO宽字符
%d    有符号十进制整数(int)(%e浮点数、e-记数法
%E    浮点数、E-记数法
%f    单精度浮点数(默认float)、十进制记数法(%.nf  这里n表示精确到小数位后n位.十进制计数)
%g    根据数值不同自动选择%f或%e.
%G    根据数值不同自动选择%f或%e.
%i       有符号十进制数(与%d相同)
%o    无符号八进制整数
%p     指针
%s    对应字符串char*(%S 对应宽字符串WCAHR*(%u无符号十进制整数(unsigned int)
%x    使用十六进制数字0f的无符号十六进制整数 
%X     使用十六进制数字0f的无符号十六进制整数
%%     打印一个百分号



往期回顾



04

| 4、 数组

01


02


03

3、 数据类型







图 | 郭嘉

文 | 郭嘉





扫码关注桔梗与少年

新浪微博:桔梗与少年