【技术研究】指针函数 函数指针 数组指针

TEC

Posted by Corax on November 15, 2023

指针函数

返回值为指针的函数

int *func(int x, int y)

来点搞的

#include "stdafx.h"
#include <iostream>
#include <stdio.h>
using namespace std;

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(int argc)
{
    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;
}

说实话虽然看着头疼,但要理解下来还是可以学到内存相关的东西的。

复习一下strlen和sizeof

strlen:

根据提供的引用内容,可以得知strlen函数是用来返回字符串长度的,它的参数必须是字符型指针(char*),而当数组名作为参数传入时,实际上数组就退化成指针了。该函数实际完成的功能是从代表该字符串的第一个地址开始遍历,直到遇到结束符NULL。返回的长度大小不包括NULL。因此,strlen函数并没有读取指针内容的功能。

所以明白了,strlen的参数会被退化为指针,然后从指针的第一个位置开始遍历字符串。

sizeof:

这个好像要看不同的情况

https://blog.csdn.net/zhouml_msn/article/details/103361539

image-20231116153834234

出了这些结果,我们好好看看。

  1. 12 strlen不算终止符\0,
  2. 12 说的指针字符串读入
  3. 13 读\0
  4. 4 因为参数是一个指针,所以指针类型大小 是4
  5. 13 字符串 同理

进入函数

这里比较搞了,现在我们先看看内存里面干了什么。

image-20231116154243853

进入函数之后,传的是数组的首地址,即19ff20,然后是返回的地址40119b

image-20231116154415536

然后是返回的栈基地址

image-20231116154437316

再往下走

printf("%d\r\n",strlen(szBuf));
printf("%d\r\n",sizeof(szBuf));
  1. 12 刚刚说的他会降级成为指针 然后读长度 不带\0
  2. 4 这里就有点搞了,进来的是个19ff20,实际上可以说传进来的一个地址,字符串首地址,现在szBuf就像是一个指针,char指针就是4
 char szBuf2[20];
    strcpy(szBuf2,szBuf);
    printf("%d\r\n",strlen(szBuf2));
    printf("%d\r\n",sizeof(szBuf2));

箭头内容实际上就是szbuf2的地址

image-20231116154808960

  1. 12 一样的读字符串
  2. 20 这里把szBuf2当作一个字符串,详见文章,规定了20,就是20。

再出来

char *psz = foo(szBuf1);
    puts(psz);
    printf("%d\r\n",strlen(psz));
    printf("%d\r\n",sizeof(psz));

  1. 要记住刚刚返回的是指向szbuf2的指针,也就是psz为19feac
  2. 所以现在我们要puts其实是一个奇奇怪怪的字符串Hellrld!
  3. 因为puts实际上还要继续往上覆盖,很容易就覆盖到了19feac的位置,所以就会输出这些东西。
  4. strlen也不太好识别,现在是1,release也不晓得是多少
  5. sizeof还是好研究的,这边是一个char指针 4

但是如果在foo申请一个很长的变量

image-20231116155554630

是因为a把地址太高了,puts就踩不到szbuf2了

image-20231116155623011

image-20231116161408929

就如上代码,max(&a,&b)传入的是main函数的栈内地址给max函数,然后不管谁大,p都会指向main空间的一个地址,以这个值赋给p,仍然安全。

函数指针

image-20231116162612496

image-20231116162627315

函数类型

  1. 参数序列(个数,类型,顺序)
  2. 调用约定
  3. 返回值类型

如果如上三项一样,那么在调用方生成调用代码的时候基本上只需要改一个目标就可以了。

这就是一个函数指针,指向函数的第一条指令,在调试中也指向一个索引表。

image-20231116162612496

image-20231116163050909

说的就是下面这个表

image-20231116163116379

函数名称是函数的首地址 第一个指令的位置

#include "stdafx.h"
#include <iostream>
#include <stdio.h>
using namespace std;

void SortA(int ary[], int nCount)
{
    puts("ÅÅÐò·¨");
}

void SortB(int ary[], int nCount)
{
    puts("Ñ¡Ôñ·¨");
}


int main(int argc)
{
    int ary[54];

    void (*pfnSort)(int [], int) = NULL;
    pfnSort = SortB;
    pfnSort(ary, 54);



    system("pause");
    return 0;
}

如上代码就形成一个间接引用了。

跑起来

image-20231116164117370

打开表看看

image-20231116164138033

两个写法都可以

image-20231116164809118

另外一种写法,利用typedef,写成int i这种样貌

image-20231116165115757

函数指针 便于维护

免杀常用手段,用于恶意程序服务器交互,只有在执行那一时刻,恶意代码恶意行为才出现。

算法和业务逻辑分离。自定义流程。

数组指针

image-20231116171043610

image-20231116171050088

函数指针一个个排列在内存,供流程调用。

image-20231116171211660

运行时说换就换说改就改

image-20231116172217128