第二十课 c语言13 多级指针 数组指针 函数指针
1.多级指针反汇编
一二级指针
可以看到p1== *(p1+0) == p1[0]
本来一直没想懂为什么是movsx ecx,byte ptr [eax],是byte,才发现p1是char类型,所以才得用movsx拓展
(p1+2) == p1[2],指针可以用和[]取值,他们是一样的
(((p3+1)+2)+3) == p3[1][2][3]
在((p2+1)+1)中,(*(p2+1)+1)是char类型,所以他的+1等于+4,(p2+1)是char类型,他的+1就是+1*
三级指针
p3是char类型,p3是char类型,(p3)是char类型,((p3))是char类型
总结:
2.数组指针
基本特性
数组指针的*p和p
虽然*px和px的值一样,但是类型不一样,所以运算出来的值也不一样
疑问?
为什么px的值会和px一样?
看一下反汇编
px这个值的地址存放的就是1,那我px就是取0113FD38这个地址的值,为什么不是取出1呢
写个普通的例子,他*px是会取出px的值后再取一次
答
px的类型是int (*)[2],所以是指向arr数组首地址,*px的类型是int [2],
还是一个数组类型,数组类型的含义还是数组的首地址,所以*px的值还是一个地址,
参考直接arr的时候,arr取值还是一个数组首地址。
总结:
数组指针运算
(px+1)就先把px的类型(int ()[2])砍个星,那么现在砍完是int [2],那么(px+1)等于px+8
((px+3)+3) px+3算完是int [2]类型,要继续+3运算就继续砍星,int [2]砍完是int类型,所以(*(px+3)+3)第二个+3就是+12
总结:
3.多维数组指针
一维数组
通过上面数组指针的运算已经练的很熟练了,+48,指向第49个数据也就是0xA0,但是我们的数据类型是int (*)[5],宽度为4,所以读出来的数据是000000A0
二维数组
二维数组运算
一样砍星运算,*(px+3)要运算砍星后的类型是char [2][3],所以+3等于+18,((px+3)+2)运算砍星后的类型是char[3],所以+2等于+6
4.函数指针
基础特征
函数指针宽度还是4,但是由于砍星后宽度不确定,无法进行加减运算,但是可以比较大小
赋值
定义的函数指针的返回值和参数,要和在赋值的函数的返回值和参数一样,否则一运行就挂了,可以强制转型编译成功但是一运行还是会挂
函数也是一段数据
论证函数其实也就是一段数据,这段数据的地址被指向函数指针时,可以被当做函数执行,强转欺骗编译器
作业
1、不完全正确,不要这么理解,指针就是指针,爱指哪就指哪,指针只是操作数据的工具
第二十一课 c语言14 位运算
1.什么是位运算
2.汇编中的算数位移指令
- SAL(Shift Arithmetic Left):算术左移(和SHL效果一样)
- SAR(Shift Arithmetic Right):算术右移
- 格式:即算数移位指令后面的第一个操作数是寄存器或者内存;第二个操作数是寄存器或者立即数
反汇编例子
0x81二进制为10000001,左移一位后为00000010,左边最高位给cf,右边最低位补0
如果是用eax寄存器的话最高位的1就直接左移,不用移到cf标志位了
0x81二进制为10000001,右移一位后为11000000,左边补最高位,右边最低为的移到cf标志位
3.逻辑移位指令
- SHL(Shift Left):逻辑左移
- SHR(Shift Right):逻辑右移
4.循环移位指令
- ROL(Rotate Left):循环左移
- ROR(Rotate Right):循环右移
5.带进位的循环移位指令
- RCL(Rotate through Carry Left):带进位循环左移
- RCR(Rotate through Carry Right):带进位循环右移
作业
第二十二课 c语言15 内存分配
1.c程序的执行步骤
2.宏定义
无参数
#define 标识符 字符序列
#define DEBUG 1
void Function(){
//....
if(DEBUG)
printf("测试信息");
}
有参数
#define 标识符(参数表) 字符序列
类似定义函数,但是define是把标识符直接替换成后面字符序列(函数执行代码),不会在堆栈创建空间
#define MAX(A,B) ((A) > (B)?(A):(B))
void Func(){
int x = MAX(1,2);
}
注意事项:
- 宏名标识符与左圆括号之间不允许有空白符,应紧接在一起
- 宏与函数的区别:函数分配额外的堆栈空间,而宏只是替换
- 为了避免出错,宏定义中给形参加上括号
- 末尾不需要分号
- define可以替代多行的代码,记得后面加
\
(一行写不下,就加\
接着下一行写)
#define MALLOC(n,type)\
((type*)malloc((n)*sizeof(type)))
3.动态分配内存
前面学习的int x; char arr[100];都是静态申请内存,今天学学动态的申请内存
malloc函数
作用:分配所指定大小的内存空间,并返回一个指向它的指针,如果内存空间不够,则返回NULL
声明:
#include "stdlib .h"
void* malloc(size_t size)
void*
void*表示任何类型的指针,在需要的时候再去强转成我们需要的对应类型指针,比如malloc动态申请内存,要返回一个指向内存的指针,但是我们声明时不知道要用一个什么类型的指针,那么返回值可以使用void*,因此宽度就不确定,无法做加减运算
使用:
//在堆中申请内存,分配128个int
int* ptr = (int *)malloc(sizeof(int)*128); //假设这块内存要给一个int型数组使用,将void*强转int*
//无论申请的空间大小,一定要进行校验,判断是否申请成功
if(ptr == NULL){
return 0;
}
//初始化分配的内存空间,将分配的这片内存中全设为0(可以不用加,这里是害怕这块内存中有别人留下的数据)
memset(ptr,0,sizeof(int)*128);
//使用内存
*(ptr) = 1; //使用指针来操作指向的内存中的数据
//使用完毕,释放申请的堆空间
free(ptr);
//将指针设置为NULL。因为这次我使用了ptr指针,我用完之后ptr应该还是指向了最后的内存中的地址,如果有坏蛋尝试使用了ptr指针,即用完后又使用了ptr指针,那很可能把原先指向的内存中的其他数据给读出来了,不安全。如果设置了NULL,后面不小心使用ptr,会报错
ptr = NULL;
内存泄露问题:
我们平时如果在函数外定义一个变量,分配的内存在全局区;在函数内定义一个变量,分配的内存在堆栈;使用完这个变量,也不用我们手动的去释放分配的内存空间,因为堆栈平衡等原因,使用完后这些内存中的数据就变成了垃圾,下一次再使用赋初始值覆盖这块内存中的数据即可。
但是现在如果我们使用malloc函数动态申请内存,分配的内存空间在堆中,堆有一个特点,如果此时一个数据占用了堆中的某块内存,那么操作系统就会记住这块内存已经分配出去了,其他数据就不能占用了,要么等待释放、要么此exe程序退出后,其他的数据才能再使用这块内存。
但是像服务器上运行的程序,会长时间运行,使用malloc函数申请内存,如果使用完没有释放,就会造成这块内存一直被占用,当数据庞大时,会将堆全部占住,最后内存占用率会很高,程序就会奔溃,这就是内存泄露问题(堆)。所以一定要释放内存
作业
#pragma warning(disable:4996)//忽略函数不安全警告
int F_Size(FILE* fp)
{
fseek(fp, 0, 2);
int len = ftell(fp);
fseek(fp, 0, 0);
return len;
}
void F_exe()
{
FILE* fp;
fp = fopen("C:\\Windows\\notepad.exe","rb");
char* addr = (char*)malloc(F_Size(fp));
if (addr)
{
fread(addr, F_Size(fp),1,fp);
}
printf("%x",addr);
free(addr);
fclose(fp);
}
int main()
{
F_exe();
}
ftell函数和fseek函数
返回当前文件位置指示符,可以和fseek函数联合使用,先使用fseek把文件指针定位到文件尾部,ftell就可以返回当前文件位置距离文件头还有多少字节,从而计算出文件大小
流stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数。
int fseek( FILE *stream, long offset, int origin );
第一个参数stream为文件 指针
第二个参数offset为 偏移量 ,正数表示正向偏移,负数表示负向偏移
第三个参数origin设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET
SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件结尾
其中SEEK_SET,SEEK_CUR和SEEK_END依次为0,1和2.
简言之:
fseek(fp,100L,0);把文件内部 指针 移动到离文件开头100字节处;
fseek(fp,100L,1);把文件内部 指针 移动到离文件当前位置100字节处;
fseek(fp,-100L,2);把文件内部 指针 退回到离文件结尾100字节处。
标签:fp,运算,21,22,int,px,内存,指针
From: https://www.cnblogs.com/xiaoxin07/p/18072924