C語言各種數據類型的內存映像(32位平臺):
#include <stdio.h>
int main()
{
char min=1<<7;
char max=(1<<7)-1;
for(int i=min;i<=max;i++)
if(i<0)
printf("%.2X ",(unsigned char)i);
else
{
printf("%c ",i);
if(i%32==0)
printf("\n%d ",i);
}
getchar();
}
output:
#include <stdio.h>
int main()
{
signed int smin=1<<31;
signed int smax=(1<<31)-1;
printf("%d\n",smin); // -2147483648
printf("%d\n",smax); // 2147483647
unsigned int umax=-1;
printf("%u\n",umax); // 4294967295
umax=(1<<32)-1;
printf("%u\n",umax); // 4294967295
}
如果一個表達式同時包含signed和unsigned整型,signed會提升為unsgined,可能會隱藏一些意想不到的錯誤,特別是用在比較運算時:
unsigned int a=4294967290;
int b=-6;
printf("%d\n",a==b); // 1 , b promotes to unsigned
#include <stdio.h>
void printByte(double d)
{
int bs=sizeof d;
unsigned char *ch=(unsigned char*)&d;
for(int i=0;i<bs;i++)
printf("%.2X ",*(ch+i));
}
int main()
{
int n=0x01020304;
if(*(char*)&n==4)
printf("小端:");//小端:
double d=15.75; // 1111.11, 指數位值:1023+3
//0 100 0000 0010 1111100000000000000000000000000000000000000000000000
printByte(d);//00 00 00 00 00 80 2F 40
// 40 2F 80
// 0100 0000 0010 1111 1000 0000
getchar();
}
將double分成4部分顯示:
#include <stdio.h>
typedef struct packed_double {
unsigned int low32; // 小數位 低32位
unsigned int low20:20; // 小數位 低33-52位
unsigned int exp11:11; // 指數位 低53-63位,移碼1023+二進制整數位-1
unsigned int sign:1; // 符號位
} packed_double;
typedef union {
double d;
packed_double b;
} packed;
int main()
{
packed pd;
pd.d=-15.75;
pd.d=12.3;
printf("%u %u %u %u\n",pd.b.sign,pd.b.exp11,pd.b.low20,pd.b.low32);
getchar();
return 0;
}
/*
0 1026 1015808 0
*/
數組名是一個存儲數據首元素地址具有常量性質的特殊指針,成員是相對于基址的偏移:
#include <stdio.h>
void printArr(short arr[],int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",*(arr+i));
}
printf("\n");
}
int main()
{
short arr[]={1,3,2};
int len=sizeof arr / sizeof *arr;
printArr(arr,len);
}
#include <stdio.h>
int main()
{
enum Nm{LOSS,TIE,WIN}nm; // 實質是一個整型,成員只是可能的右值(符號常量)的枚舉
nm=LOSS;
printf("%d ",nm); // 0
nm=TIE;
printf("%d ",nm); // 1
nm=WIN;
printf("%d ",nm); // 2
nm=(enum Nm)3;
printf("%d ",nm); // 3
printf("\n%d",sizeof(enum Nm)); // 4
getchar();
}
枚舉讓相關符號常量內聚為一組,相對于#define,枚舉對數據的描述性更清晰。
#include <stdio.h>
int main()
{
union Nn{int a; double b;}nn;// 成員的起始地址相同,值相互覆蓋
nn.a=123; //
printf("起始地址:%X,內存空間占用:%d\n",&nn.a,sizeof nn.a);
nn.b=12.3;
printf("起始地址:%X,內存空間占用:%d\n",&nn.a,sizeof nn.b);
nn.a=12;
printf("起始地址:%X,內存空間占用:%d\n",&nn.a,sizeof nn.a);
getchar();
}
/*
起始地址:12FF40,內存空間占用:4
起始地址:12FF40,內存空間占用:8
起始地址:12FF40,內存空間占用:4
*/
當一些事物具有更多共性,但有少量差異時,可以只用一個內嵌一個共用體的結構體來描述:
#include <stdio.h>
#include <string.h>
#define MAXPARTS 12
struct Parts{ // 零件
int cost;
char supplier[12];
char unit[12] ;
};
struct Assembly{ // 裝配件
int n_parts;
struct {
char partno[12];
short quan;
}parts[MAXPARTS];
};
struct Inventory{ // 存貨類型,或是零件,或是裝配件
char partno[10];
int quan;
enum{PART,ASSEMBLY}type; // 存貨類型
union {
struct Parts parts;
struct Assembly assembly;
}info;
};
int main()
{
struct Inventory screen;
strcpy(screen.partno,"p001");
screen.quan=12;
screen.type=Inventory::PART;
screen.info.parts.cost=122;
strcpy(screen.info.parts.supplier,"hw");
strcpy(screen.info.parts.unit,"pcs");
struct Inventory shell;
strcpy(shell.partno,"a001");
shell.quan=4;
shell.type=Inventory::ASSEMBLY;
shell.info.assembly.n_parts=22;
strcpy(shell.info.assembly.parts[0].partno,"d001");
shell.info.assembly.parts[1].quan=5;
int costs;
if(shell.type==Inventory::ASSEMBLY)
costs=shell.info.assembly.n_parts;
printf("%d\n",costs); //22
getchar();
return 0;
}
結構體各數據成員的引用可以通過其內存大小和字節對齊來相對于基址偏移來計算。結構體通常用于描述某一事物,用其成員來描述該事物的某些關鍵屬性。讓該事物既可以用結構體變量整體表示,也可以對其成員分別引用來處理該事物的各個屬性。
#include <stdio.h>
int main()
{
struct demo{char a; short b;int c;} abc; // 成員相對于基址偏移,字節對齊
abc.b=12;
short *p=(short*)((int)&abc+sizeof(short)); // 模擬編譯器計算第2個成員的偏移地址
printf("%d %d\n",abc.b,*p); // 12 12
printf("%d\n",sizeof(struct demo));// 8
getchar();
}
(一次可以處理n個位,1<=n<=整形長度)
位域(全局)二進制位顯示:
#include <stdio.h>
void printBinM(unsigned int n)
{
for(int i=31;i>=0;i--)
printf("%d",(n & 1<<i)>>i);
printf("\n");
}
struct Bf{
unsigned a:3;
unsigned b:4;
unsigned c:5;
}bf;
int main()
{
bf.a=1;
bf.b=15;
bf.c=3;
int *p=(int*)&bf; // 505
printf("%d\n",*p);
printBinM(*p);//00000000000000000000000111111001
getchar();
}
位域(局部)二進制位顯示:
#include <stdio.h>
void printBinM(unsigned int n)
{
for(int i=31;i>=0;i--)
printf("%d",(n & 1<<i)>>i);
printf("\n");
}
int main()
{
struct Bf{
unsigned a:3;
unsigned b:4;
unsigned c:5;
}bf;
bf.a=1;
bf.b=15;
bf.c=3;
int *p=(int*)&bf; // -858996231
printf("%d\n",*p);
printBinM(*p);//11001100110011001100000111111001
getchar();
}
-End-
原先做Winform的時候,經常調用C/C++生成的動態庫,這篇便是當時學習的筆記(2017.03.20).C/C++動態庫在其他編程語言是可以調用的.比如.Net提供的P/Inovket,Java提供JNI.
在mylib.h文件中聲明2個函數
#pragma once
int add(int a, int b);
int sub(int a, int b);
mylib.c函數實現
#include "mylib.h"
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
方式一,添加依賴項
鏈接器輸入添加靜態庫
方式二,代碼引用靜態庫
#include <stdio.h>
#include <stdlib.h>
//靜態庫的頭文件直接放入項目的根目錄
#include "mylib.h"
//將mylib.lib文件放入項目的根目錄下
//方式1. 鏈接器 輸入 附加依賴項 添加 mylib.lib
//方式2. 指定引用靜態庫mylib,
//#pragma comment(lib,"mylib.lib")
int main(int argc, char *argv[])
{
int a=10;
int b=20;
int sum=add(a, b);
printf("sum=%d\n", sum);
sum=sub(20, 10);
printf("sum=%d\n", sum);
system("pause");
return 0;
}
先簡單學習函數指針,因為動態庫的使用,要用到指針.
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
int add(int a, int b)
{
return a + b;
}
int main(int argc, char *argv[])
{
typedef int func(int, int); //使用typedef 定義一個函數類型
func *pfunc; //定義一個函數類型的指針(函數指針)
pfunc=add; //函數指針指向add函數
int sum=pfunc(10, 20);
printf("%d\n", sum);
system("pause");
return 0;
}
動態庫代碼
//_declspec (dllexport) 將函數進行導出
_declspec (dllexport) int add(int a, int b)
{
return a + b;
}
_declspec (dllexport) int sub(int a, int b)
{
return a - b;
}
設置為動態庫dll
調用動態庫代碼
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
int a=20;
int b=10;
//定義函數指針
typedef int(*Add)(int a, int b);
typedef int(*Sub)(int a, int b);
//通過LoadLibraryEx,動態加載dll文件
HMODULE hmodule=LoadLibraryEx("mydll.dll", NULL, DONT_RESOLVE_DLL_REFERENCES);
//獲取add函數地址,用我們定義的函數指針接收add函數地址
Add add=(Add)GetProcAddress(hmodule, "add");
int sum=add(a, b);
printf("sum=%d\n", sum);
//獲取sub函數地址, 用我們定義的函數指針接收sub函數地址
Sub sub=(Sub)GetProcAddress(hmodule, "sub");
int result=sub(a, b);
printf("result=%d\n", result);
//使用完之后,要進行釋放
FreeLibrary(hmodule);
system("pause");
return 0;
}
生成之前,指定應用動態庫的所在的文件目錄
設置動態庫引用的目錄
在.Net中也可以使用加載動態庫,指定函數在內存中地址,進行調用的.
個人能力有限,如果您發現有什么不對,請私信我
如果您覺得對您有用的話,可以點個贊或者加個關注,歡迎大家一起進行技術交流