C语言内存地址 1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> int main () { int n = 999 ; printf ("Hello world!%p:%d\r\n" ,&n,n); getchar(); return 0 ; }
WinHex 打开内存文件
查找16进制:Alt+Ctrl+F 搜索下一个:F3
到特定的偏移地址,Seek Logical Address/Go to Virtual Address
VC++6.0固定变量内存地址,最新版的VC是随机地址
VC98/CRT/SRC/CRT0.C->mainCRTStartup
内存存储机制 补码(整数和小数的表示) 整数 内存中存储的都是补码形式,先判断正负数,再对两者之间进行转换 比如:winHex里面的十六进制跟变量十进制之间的转换
二进制的 + - * / 原理
1 2 3 4 5 6 A - B = A + (0x100-B) - 0x100 B + ~B = ff B + ~B + 1 = 0x100 ~B + 1 = 0x100 - B //求补运算 = A + neg(B) - 0x100 //进位丢失 010进位10
求补运算是一种运算 补码是一种编码 补码规定了数据的读写双方必须做到: 1、最高有效位是符号位,0表示正,1表示负 2、当数据是正数的时候,其余各种直接存储其数值 3、当数据为负数的时候,其余各种存储其求补后的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <stdlib.h> #include <stdio.h> #include <time.h> int main () { int n = 52 ; int m = -52 ; printf ("%x\r\n" ,&n); getchar(); return 0 ; }
公式:补码=反码+1
十进制整形在内存中的存储:
1、转化为二进制 2、正数则直接按照二进制转换为十六进制 3、负数则除符号位,取反+1,再转换为十六进制 4、按照内存4个字节,一个字节8位?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 计算 34 ,-34 => 22 00 00 00 => de ff ff ff 0x86 单字节有符号数的十进制真值是什么? 定义变量为-122 ,用winHex去寻找地址看是不是86 0x80 呢?
PS:这是在winHex里面的表现形式,十六进制表示,其余程序不一定是这样 十进制整数在winHex的表示,直接把十进制转化为十六进制,小端排序即可
1 2 3 4 5 十六进制中,大于等于8的都是负数 1000 因为存储的都是补码形式 看首位是否<8,<8说明是正数,≥8说明是负数。 原理:临界值为7fffH,我们知道首位7的二进制码为0111,而0111的首位是0,说明是正数。而像8000H首位8二进制码为1000,首位为1,就变成负数了。
小数 二进制存储小数原理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 1、固定分解 二进制0.1表示为十进制的0.5,0.01表示为0.25,0.001表示为0.125 因此,十进制的小数转化为二进制如下: 0.375 < 0.5 => 0 0.375 > 0.25 => 1 0.125 = 0.125 => 1 因此 0.375(十进制) = 0.011(二进制) 2、乘2取余 乘2如果小于1说明原值小于0.5,即取0 0.375 * 2 = 0.75 < 1 => 0 0.75 * 2 = 1.5 > 1 => 1 0.5 * 2 = 1 == 1 => 1 因此 0.375(十进制) = 0.011(二进制) 0.6就没法精确 实际存储 总共4个字节32位,1个字节8位即一个十六进制 一位放正负符号、一个字节(8位)放小数点位置,其余23位放值。注:64位有11位表小数点位置 32位 = 1 + 8 + 23 32位4个字节,小数形式十六进制存储形式如下: S EEEEEEEE DDDDDDDDDDDDDDDDDDDDDDD 案例计算: 23.625(十进制) => 10111.101(二进制) 有小数转十六进制的形式 => 1.0111101 * 10^4 S(正负符号位) EEEEEEEE(小数点位) DDDDDDDDDDDDDDDDDDDDDDD(值,补0) 小数点位存储8位,大小为,0-127-255,转化为 -128-0-127 即原来0-4-|127|-255变成-128-|0|-4-127 原先0-255表示4是 00000100 而后-128-127表示4是 4+127 = 128+3 = 10000011 =>0 10000011 01111010000000000000000 =>01000001101111010000000000000000 =>0100 0001 1011 1101 0000 0000 0000 0000 =>4 1 B D 0 0 0 0 =>00 00 BD 41
1 2 3 4 5 6 7 8 9 题目1: 将895.75转换为float类型的16进制形式,895 = 1101111111(B) 1101111111.11 1.10111111111 * 10^9 9+127 => 128+8 => 10001000 0 10001000 10111111111000000000000 0100 0100 0101 1111 1111 0000 0000 0000 4 4 5 F F 0 0 0 00 F0 5F 44
1 2 3 4 5 6 7 8 9 10 11 题目2: 已知地址0013FF74内容为:00 68 45 44,系统字节顺序为小尾方式,如果这个地址是存放flaot变量,那么其10进制真值为多少? 44 45 68 00 0100 0100 0100 0101 0110 1000 0000 0000 0 10001000 10001010110100000000000 1 10^9 +1.100010101101 * 10^9 +1100010101.101 +2^9 + 2^8 + 2^4 + 2^2 + 1 + 0.5 + 0.125 +512+256+16+4+1+0.5+0.125 +789.625
1 2 3 4 5 6 7 8 9 10 #include <stdlib.h> #include <stdio.h> #include <time.h> int main () { float f = 23.625f ; printf ("%08x\r\n" ,&f); getchar(); return 0 ; }
关于字节问题 4个字节,一个字节8位,每8位相当于一个十六进制 0000 0000
winHex是双字节存储,每个地址对应的是两个十六进制数
随机数 VC98/CRT/SRC/RAND.C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdlib.h> #include <stdio.h> #include <time.h> int main () { int n = 0 ; srand((unsigned )time(NULL )); while (n<10 ) { printf ("%x\r\n" ,rand()); n++; } return 0 ; }
1 2 3 4 5 6 7 del *.obj del *.exe cl /c /W4 /WX 2.c link 2.obj2.exe pause
C语言基础语法分析 1、数值和字符串 ‘a’表示为ASCLL,输入到变量地址要&n,输入指定地址直接n
“a”表示为字符串,默认取首地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <stdlib.h> #include <stdio.h> #include <time.h> int main () { char szBuf[20 ] = {0 }; scanf ("%s" ,szBuf); system("pause" ); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <stdlib.h> #include <stdio.h> #include <time.h> int main () { int n; scanf ("%d" ,n); system("pause" ); return 0 ; }
C0000005内存访问异常
scanf怎么判断指定输入的是地址还是值,默认都是存储在19ff20这一行
结论:
scanf后续指定的就是地址
有初始化值,默认存储在19ff2c,后续的输入是覆盖变量值(整数、字符等)
无初始化值,取残留值,即默认地址存储的是残留地址,残留地址存储的才是值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdlib.h> #include <stdio.h> #include <time.h> int main () { int n = 0x19ff2c ; int m = 0 ; scanf ("%d" ,n); printf ("%08x\r\n" ,n); printf ("%08x\r\n" ,m); system("pause" ); return 0 ; }
不同于printf,默认变量定义或从scanf获取值时,就已经开辟内存
m = 0x19ff2c时,是存储在19ff28
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdlib.h> #include <stdio.h> #include <time.h> int main () { int n = 0x19ff2c ; int m = 0 ; int i = 0 ; printf ("n address:%p\r\n" ,&n); printf ("m address:%p\r\n" ,&m); printf ("i address:%p\r\n" ,&i); getchar(); return 0 ; }
结论:
vc++中变量存储地址从19ff2c开始
但是有多个变量,地址是往前排,比如有两个变量,第一个变量地址是 19ff28 ,第二个是19ff2c
2、新建C项目(新建&调试) 新建MFC项目控制台项目(基于 Microsoft 基础类 (MFC) 库的 Windows 可执行应用程序)
create -> Win32 Console Application->Helloworld
快捷键(F5、Ctrl+F7、F9、F10、F11),反汇编
F5:编译、链接、调试、运行
Ctrl+F7:编译不链接
F7:编译、链接
F9:打断点
F10:执行下一步,跳过循环或函数
F11:执行下一步,进入循环或函数
CTRL+F10:运行到光标所在行
Disassembly:反编译
Alt+8跳转汇编,对应代码和汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <stdlib.h> #include <stdio.h> int main () { int x = 0 ; int y = 0 ; for (x = 0 ; x < 7 ; x++) { for (y = 0 ; y < 7 ; y++) { if (x + 3 == y || -x + 9 == y || x - 3 == y || -x + 4 == y) { printf ("* " ); } else { printf (" " ); } } printf ("\r\n" ); } }
配调试界面(十六进制窗口、变量窗口、栈窗口、寄存器)
View -> Debug Windows ->Memory
View -> Debug Windows ->Watch
View -> Debug Windows ->Call Stack
View -> Debug Windows ->Register
3、循环&goto&基本代码 double 8字节、int 4字节,double类型数据按int输出会丢失字节,取值(ceil、floor)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <stdlib.h> #include <stdio.h> #include <math.h> int main () { int n = 15000 ; double dblBitCount = 0.0 ; for (int i = 1 ;i <= n; i++) { dblBitCount = dblBitCount + log10 (i); } goto NEXT; system ("pause" ); NEXT: printf ("%d\r\n" ,(int )ceil (dblBitCount)); return 0 ; }
4、函数调用 (调用机制) 1,按调用约定传递参数。
1.1调用约定调用方(caller)需要和被调方(callee)作出以下约定:
参数的传递方向
参数的传输媒介
函数返回值的位置
释放参数空间的负责方,有且仅有一方去释放参数空间。
__cdecl:参数使用栈空间传递,从右往左函数返回值在寄存器,由调用方负责释放参数空间
__stdcall:参数使用栈空间传递,从右往左函数返回值在寄存器,由被调方负责释放参数空间
__fastcall:左数前两个参数使用寄存器传递,其它参数使用栈空间传递,从右往左函数返回值在寄存器,由被调方负责释放参数空间
2.保存返回地址,函数调用结束后应该执行的地址值
3.保存调用方的信息(栈底)
4.更新当前栈到栈顶(把当前栈顶作为被调方的栈底)
5.为局部变量申请空间。(抬高栈顶)
6.保存寄存器环境。(把即将使用的寄存器原值保存在栈里)
7.如果编译选项有/zI/Zi,则将局部变量初始化为oxcccccccc
8.执行函数体
9.恢复寄存器环境
10.释放局部变量的空间
11.恢复调用方的栈信息(栈底)
从右向左填充
对于栈来说,数据出入的口是栈顶。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int GetFib (int n,int m,int y) { int nF1 = 1 ; int nF2 = 1 ; int nFib = 0 ; if (n == 2 || n == 1 ) { return 1 ; } return nFib; } int main () { unsigned int nFib1 = GetFib(1 , 0 , 3 ); unsigned int nFib2 = GetFib(1 , 0 , 3 ); for (int i = 2 ; i < 47 ; i++) { nFib1 = GetFib(1 , 0 , 3 ); printf ("%02d:%15u\t%f\r\n" , i-2 , nFib1, (double )nFib2 / nFib1); nFib2 = nFib1; } system("pause" ); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 1.调用者在自己的栈帧里开辟好被调函数形参需要的空间 2.入栈 函数调用结束后应该执行的地址值,即返回地址,其实就是回收第一步为形参开辟的空间的指令的地址 3.进入被调函数了,入栈调用函数栈帧的栈底地址 4.在新函数的当前栈帧内为局部变量分配空间后,入栈局部变量 5.被调函数遇到return语句了,说明即将结束本函数了,就开始做回收本栈帧的空间的事了: 1)如果有返回值,那么把返回值赋值给EAX,如果没有则忽略这一步。 2)回收局部变量空间,即esp指向调用函数栈帧的栈顶了 3)提前存好的main函数栈帧的栈底地址赋值进入ebp寄存器,从而使得ebp指向main函数栈帧的栈底 4)把返回地址填入EIP寄存器,接着就会指向,回收main函数当初为被调函数开辟的两个形参的空间的指令地址 5)回收形参空间 ———————————————— 原文链接:https://blog.csdn.net/kangkanglhb88008/article/details/89739105
5、数组 1 2 3 4 5 6 7 int n = ...;type ary[M] = ...; ary[n] address: (int )ary + sizeof (type)*n = 0x00400000 int *p = NULL ;printf ("%p\r\n" ,&p[20 ]);
没有限制数组访问,默认可以任意访问
1 2 3 4 5 6 7 8 9 10 int main () { int arr = {1 ,2 ,3 }; printf ("%d-%p" ,arr[-1 ],&arr[-1 ]); printf ("%d-%p" ,arr[0 ],&arr[0 ]); printf ("%d-%p" ,arr[1 ],&arr[1 ]); printf ("%d-%p" ,arr[2 ],&arr[2 ]); printf ("%d-%p" ,arr[3 ],&arr[3 ]); return 0 ; }
利用越界,访问指定地址
1 2 3 4 5 6 7 8 9 10 0x0019ff2c 求出0x00400000 int main () { int n = 2 ; printf ("%p\r\n" ,ary[(0x00400000 - (int )ary) / sizeof (int )]); system("pause" ); return 0 ; }
间接访问,[] * ->
1 2 3 4 5 6 7 8 9 10 11 12 13 void change (int n,int m) { int temp = n; n = m; m = temp; } int main () { int n = 1 ,m = 2 ; change(n,m); system("pause" ); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 void change (int arr[]) { int temp = arr[0 ]; arr[0 ] = arr[1 ]; arr[1 ] = temp; } int main () { int arr[] = {1 ,2 }; change(arr); system("pause" ); return 0 ; }
内存结构示例图
两个BUG(Build-clean)
1 2 3 4 5 6 7 8 int main () { float f; scanf ("%f" ,&f); system("pause" ); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 int foo () { goto NEXT; return 0 ; NEXT: ; } int main () { foo(); system("pause" ); return 0 ; }
6、二维数组 1 2 3 4 5 6 7 8 9 ary[n] address: (int )ary + sizeof (type)*n type ary[N][M] ary[x][y] address; (int )ary + sizeof (type[M])*x + sizeof (type)*y
1 2 3 4 5 6 7 8 9 10 int ary[3 ][4 ] = { {1 ,2 ,3 ,4 }, {2 ,2 ,3 ,4 }, {3 ,2 ,3 ,4 } }; int x = 2 ;int y = 3 ;printf ("%p\r\n" ,&ary[x][y]);printf ("%p\r\n" ,&ary[x][4 *x+y]);printf ("%p\r\n" ,(int )ary + sizeof (int [4 ])*x + sizeof (int )*y);
内存是一维存储,二维数组只有一个首地址,可以用一维数组访问到二维数组
1 2 3 4 5 6 7 8 type ary[N][M] ary[x][y] address; (int)ary + sizeof(type[M])*x + sizeof(type)*y (int)ary + sizeof(type)*M*x + sizeof(type)*y (int)ary + sizeof(type)*(M*x + y) ary[3][4],从ary[0]开始访问 其实就是 4y+x
自定义strcpy实现对比
1 2 3 4 5 6 7 8 9 10 11 12 void mystrcpy (char szDst[],char szSrc[]) { int i = 0 ; while (szSrc[i]!='\0' ) { szDst[i] = szSrc[i]; i++; } szDst[i]='\0' ; }
7、 作用域 全局变量的初始化与否会影响编译文件的大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int g_nTest = 0x87654093 ;char g_szBuf[] = "Hello" ;const char g_szTest[] = "jjyy" ;int g_nTest2[0x10000 ]; void Fun (int n) { static int stc_nTest = 123 ; stc_nTest++; printf ("%d\r\n" ,stc_nTest); } int main () { Fun(10 ); Fun(20 ); Fun(30 ); return 0 ; }
分析每种不同的变量,内存中定义
名称粉碎
汇编代码、内存、寄存器
变量放到寄存器,汇编里看不到??
0042开头,全局变量
19ff2c,栈地址
8、 地址和指针 *取值 &取地址
1 2 3 4 int a = 8 ;int *pa = NULL ;pa = &a; *pa = 999 ;
指针和数组的区别 数组在定义时已经明确类型,指针存放的是地址,还需要附带解释方式,即类型sizeof(type)
指针效率小于等于数组
1 2 3 float flt = 3.14f ;printf ("%08x" ,*(int *)&flt);printf ("%08x" ,*(int )flt);
是否交换判定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void myswap (int *pn1,int *pn2) { int *nTmp = pn1; pn1 = pn2; pn2 = nTmp; } void myswap2 (int *pn1,int *pn2) { int nTmp = *pn1; *pn1 = pn2[0 ]; pn2[0 ] = nTmp; } int main () { int n1 = 8 ; int n2 = 10 ; myswap(&n1,&n2); return 0 ; }
字符串指针 1 2 3 4 5 6 7 8 char szTest[] = "Hello" ;char *pszTest = "Hello" ;szTest[0 ] = 'h' ; pszTest[0 ] = 'h' ; puts (szTest);puts (pszTest);getchar(); return 0 ;
修改常量区可读写;
打开二进制文件,查找rdata字符串,下两行的40,修改为C0
附带 局部变量区会变化,基本上默认C那些都是,之后就是三个寄存器。再之后才是函数传参
9、函数指针、数组指针 函数指针 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <string.h> #include <stdlib.h> char *foo (char szBuf[]) { printf ("%d\r\n" ,strlen (szBuf)); printf ("%d\r\n" ,sizeof (szBuf)); char szBuf2[20 ]; strcpy (szBuf2,szBuf); printf ("%d\r\n" ,strlen (szBuf2)); printf ("%d\r\n" ,sizeof (szBuf2)); return szBuf2; } int main () { char szBuf1[] = "Hello world!" ; char * szBuf2 = "Hello world!" ; printf ("%d\r\n" ,strlen (szBuf1)); printf ("%d\r\n" ,strlen (szBuf2)); printf ("%d\r\n" ,sizeof (szBuf1)); printf ("%d\r\n" ,sizeof (szBuf2)); printf ("%d\r\n" ,sizeof ("Hello world!" )); char *psz = foo(szBuf1); puts (psz); printf ("%d\r\n" ,strlen (psz)); printf ("%d\r\n" ,sizeof (psz)); system("pause" ); return 0 ; }
数组指针 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 void SortA (int ary[], int nCount) { puts ("冒泡法" ); } void SortB (int ary[], int nCount) { puts ("选择法" ); } typedef void (_cdecl*PFNSORT) (int [], int ) ;int main () { int ary[54 ]; PFNSORT pfn = NULL ; pfn = SortB; pfn(ary,54 ); pfn = SortA; pfn(ary,54 ); return 0 ; }
10、指针和多维数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 int main (int argc) { int ary[3 ][4 ] = { {1 , 2 , 3 , 4 }, {10 , 20 , 30 , 40 }, {100 , 200 , 300 , 400 } }; printf ("%p\r\n" , ary); printf ("%p\r\n" , *ary); printf ("%p\r\n" , &ary); printf ("%p\r\n" , ary[0 ]); printf ("%p\r\n" , ary[0 ][0 ]); printf ("%p\r\n" , ary + 1 ); printf ("%p\r\n" , *ary + 1 ); printf ("%p\r\n" , &ary + 1 ); printf ("%p\r\n" , ary[0 ] + 1 ); printf ("%p\r\n" , (*(ary + 1 ))[1 ]); system("pause" ); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 int main (int argc) { int ary[3 ][4 ] = { {1 , 2 , 3 , 4 }, {10 , 20 , 30 , 40 }, {100 , 200 , 300 , 400 } }; int (*p)[4 ] = ary; int (*pAry)[3 ][4 ] = &ary; printf ("%p\r\n" , p); printf ("%p\r\n" , *p); printf ("%p\r\n" , pAry); printf ("%p\r\n" , *pAry); printf ("%p\r\n" , **pAry); printf ("%p\r\n" , p + 1 ); printf ("%p\r\n" , *p + 1 ); printf ("%p\r\n" , pAry + 1 ); printf ("%p\r\n" , *pAry + 1 ); printf ("%p\r\n" , **pAry + 1 ); system("pause" ); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int main (int argc, char **argv, char *envp[]) { while (*envp != NULL ) { puts (*envp); envp++; } for (int i = 0 ; i < argc; i++) { puts (argv[i]); } char *aryPoint[3 ] = { "Hello" , "world" , "C++" }; return 0 ; }
11、结构体 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 struct tagStudent { char szName[5 ]; int nAge; float fHeight; double dblWeight; unsigned short int wID; char nGender; }; int main (int argc, char **argv, char *envp[]) { struct tagStudent stu = { "Jack" , 25 , 185.0f , 80.0 , 9527 , 'm' }; printf ("%d\r\n" ,sizeof (stu)); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 struct tagStudent { char szName[5 ]; char nGender; int nAge; double dblWeight; float fHeight; unsigned short int wID; }; int main (int argc, char **argv, char *envp[]) { struct tagStudent stu = { "Jack" , 'm' , 25 , 80.0 , 185.0f , 9527 }; printf ("%d\r\n" ,sizeof (stu)); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 struct tagStudent { char szName[5 ]; char nGender; double dblWeight; int nAge; float fHeight; unsigned short int wID; }; int main (int argc, char **argv, char *envp[]) { struct tagStudent stu = { "Jack" , 'm' , 80.0 , 25 , 185.0f , 9527 }; printf ("%d\r\n" ,sizeof (stu)); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 struct tagStudent { char szName[5 ]; char nGender; int nAge; double dblWeight; unsigned short int wID; float fHeight; }; int main (int argc, char **argv, char *envp[]) { struct tagStudent stu = { "Jack" , 'm' , 25 , 80.0 , 9527 , 185.0f , }; printf ("%d\r\n" ,sizeof (stu)); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 struct tagStudent { char szName[5 ]; int nAge; double dblWeight; char nGender; float fHeight; unsigned short int wID; }; int main (int argc, char **argv, char *envp[]) { struct tagStudent stu = { "Jack" , 25 , 80.0 , 'm' , 185.0f , 9527 }; printf ("%d\r\n" ,sizeof (stu)); return 0 ; }
共用体(共用同一个内存结构,以最大成员字节开辟空间)
1 2 3 4 5 6 7 union unScore { char chLevel; float fPoint; char szTest[8 ]; }
12、堆 栈分配和堆分配的区别?能否识别出内存中栈地址和堆地址
申请堆也是存储在栈,不过存储的是堆的地址,类似于常量地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include "stdafx.h" #include <malloc.h> #include <string.h> int main (int argc, char **argv, char *envp[]) { int *p = (int *)malloc (4 ); *p = 999 ; char *psz = (char *)malloc (20 ); strcpy (psz, "Hello" ); char *psz2 = (char *)realloc (p, 1 ); psz2[0 ] = 'a' ; p = (int *)realloc (psz2, 8 ); p[0 ] = 0x6666 ; p[1 ] = 0x8888 ; free (p); free (psz); return 0 ; }
堆结构分析:
堆的起始地址要减20字节,堆存储有附加数据(貌似是调试状态才有),
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include "stdafx.h" #include <malloc.h> #include <string.h> #include <crtdbg.h> #ifdef _DEBUG #define malloc(n) _malloc_dbg(n, _NORMAL_BLOCK, __FILE__, __LINE__) #endif int main (int argc, char **argv, char *envp[]) { int *p = (int *)malloc (4 ); *p = 999 ; char *psz = (char *)malloc (20 ); strcpy (psz, "Hello" ); char *psz2 = (char *)realloc (p, 1 ); psz2[0 ] = 'a' ; p = (int *)realloc (psz2, 80 ); p[0 ] = 0x6666 ; p[1 ] = 0x8888 ; free (p); free (psz); return 0 ; }
只有调试版在释放堆空间时才填充数据,发布版是残留,发布版没有堆内没有调试信息
调试小技巧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include "stdafx.h" #include <malloc.h> #include <string.h> #include <crtdbg.h> #ifdef _DEBUG #define malloc(n) _malloc_dbg(n, _NORMAL_BLOCK, __FILE__, __LINE__) #endif int main (int argc, char **argv, char *envp[]) { char *psz = (char *)malloc (strlen ("Hello world!" )) puts ("test" ); puts ("test" ); puts ("test" ); puts ("test" ); puts ("test" ); strpcy (psz, "Hello world!" ); puts ("test" ); puts ("test" ); puts ("test" ); puts ("test" ); puts ("test" ); free (psz); system ("pause" ); return 0 ; }
位运算(针对二进制的运算,运算前要转换成十进制):
是一种底层运算,加减本质上都是位运算
1 2 3 4 5 6 7 8 9 10 11 12 13 位是一种二进制运算 A and 1 = A A or 0 = A A and 0 = 0 A or 1 = 1 A xor A = 0 A xor 0 = A A xor 1 = not A A and not A = 0 A or not A = 1 此处的1默认为 1111 假设A为1010
有符号数移位补符号位,无符号位移位补0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int myabs (int n) { unsigned int i = 7 ; i = i >> 1 ; i = -1 ; i = i >> 1 ; int j = 7 ; j = j >> 1 ; i = -1 ; i = i >> 1 ; return 0 ; } int main (int argc, char **argv, char *envp[]) { int n = myabs(-5 ); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 int myabs (int n) { int i = n >> 31 ; n = n ^ i; return n - i; } int main (int argc, char **argv, char *envp[]) { int n = myabs(-5 ); return 0 ; }
位运算2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #define ADD 1 #define DEL 2 #define EDT 4 #define QUE 8 int main (int argc, char **argv, char *envp[]) { int nPrivilege = 0 ; nPrivilege = nPrivilege | DEL; nPrivilege = nPrivilege | EDT; if (nPrivilege & ADD) { puts ("add" ); } if (nPrivilege & DEL) { puts ("DEL" ); } if (nPrivilege & EDT) { puts ("EDT" ); } if (nPrivilege & QUE) { puts ("QUE" ); } return 0 ; }
位运算3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 struct tagPrivilege { int add:1 ; int del:1 ; int edt:1 ; int que:1 ; }; int main (int argc, char **argv, char *envp[]) { struct tagPrivilege pri = {0 }; pri.del = 1 ; pri.edt = 1 ; int n = pri.del; if (pri.add) { puts ("add" ); } if (pri.del) { puts ("DEL" ); } if (pri.edt) { puts ("EDT" ); } if (pri.que) { puts ("QUE" ); } return 0 ; }
位运算4:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 struct tagPrivilege { int add:1 ; int del:3 ; int : 0 ; unsigned int edt:4 ; int que:2 ; }; int main (int argc, char **argv, char *envp[]) { struct tagPrivilege pri = {0 }; pri.del = 9 ; pri.edt = 20 ; int n = pri.del; if (pri.add) { puts ("add" ); } if (pri.del) { puts ("DEL" ); } if (pri.edt) { puts ("EDT" ); } if (pri.que) { puts ("QUE" ); } return 0 ; }
https://www.yuque.com/books/share/08f3006b-131e-4f17-85cb-ab78f322c090