C语言22 调用约定和函数指针

函数调用约定

就是告诉编译器:怎么传递参数,怎么传递返回值,怎么平衡堆栈

int method(int x,int y)
{
    return x+y;
}
//调用
method(1,2);

常见的几种调用约定:

调用约定 参数压栈顺序 平衡堆栈
__cdecl 从右至左入栈 调用者清理栈
__stdcall 从右至左入栈 自身清理堆栈
__fastcall ECX/EDX 传送前两个,剩下:从右至左入栈 自身清理堆栈
int __stdcall method(int x,int y)
{
    return x+y;
}
//调用
method(1,2);
//观察反汇编堆栈变化

PS:一般情况下自带库默认使用 __stdcall 我们写的代码默认使用 __cdecl

……

C语言21 指针数组与数组指针

指针数组的定义

char arr[10]; //10 char
char* arr[10]; //10 指针(char*)
Point* arr[10]; //10 指针(Point*)
int******** arr[10]; 

指针数组的赋值

char* a = "Hello";
char* b = "World";
//方式1:
char* arr[2]= {a,b};
//方式2:
char* arr[2];
arr[0]=a;
arr[1]=b;

结构体指针数组

struct Point
{
    int x;
    int y;
}
Point p;            //8字节 一个结构体
Point arr[10];      //8字节 * 10 结构体数组
Point* arrPoint[10];  //4字节 * 10 结构体指针数组

分析下面的代码

int arr[] = {1,2,3,4,5,6,7,8,9,0};
int* p =&arr[0]; //数组首个元素地址
int* p = arr;    //数组首个元素地址 跟上面的表达方式是一样的 也可以说是简写形式
int* p =&arr;    //什么情况? --编译失败 (int(*)[10] 类型 不可以转换为 int* 类型)
//我们使用显式声明类型来躲避编译器检查
int* x = (int*)&arr;
//我们编译后发现 此时的x 值和上面的p值是一样的
//那么区别是什么呢
//当我们使用&arr 获得的类型就是 int(*)[10] 类型 也就是数组指针类型

数组指针的定义

//定义数组指针的写法
int(*px)[5];//一维数组指针  px 这里就是当前指针的名字
char(*px)[3];  //px 就是指针的名字
int(*px)[2][2]; //二维数组指针  px 就是指针的名字
char(*px)[3][3][3]; //三维数组指针  px 就是指针的名字
//思考:
//int *p[5] 与 int(*p)[5] 有什么区别?

数组指针的宽度与赋值

int(*px1)p[5];      //一维数组指针
char(*px2)[3];
int(*px3)[2][2];        //二维数组指针
char(*px4)[3][3][3];    //三维数组指针
printf("%d %d %d %d\n",sizeof(px1),sizeof(px2),sizeof(px3),sizeof(px4));
// 4 4 4 4
px1 = (int (*)[5])1;
px2 = (char (*)[3])2;
px3 = (int (*)[2][2])3;
px4 = (char (*)[3][3][3])4;

数组指针的运算

int(*px1)p[5];      //一维数组指针
char(*px2)[3];
int(*px3)[2][2];        //二维数组指针
char(*px4)[3][3][3];    //三维数组指针
px1 = (int (*)[5])1;
px2 = (char (*)[3])1;
px3 = (int (*)[2][2])1;
px4 = (char (*)[3][3][3])1;
px1++;  //int(4) *5  +20 =21
px2++;  //char(1) *3   +3 =4
px3++;  //int(4) *2 *2  +16 =17
px4++;  //char(1) *3 *3 *3 +9 =10
printf("%d %d %d %d \n",px1,px2,px3,px4);

数组指针的使用

//第一种:
int arr[] = {1,2,3,4,5,6,7,8,9,0}
int(*px)[10]=&arr
//*px 是啥类型? => int[10] 数组类型
//px[0] 等价于 *px 所以 *px 也等于 int[10]数组
printf("%d %d \n",(*px)[0],px[0][0]);
px++; //后 (*px)[0]就访问整个数组地址后的地址内的数据
//第二种:
int arr[3][3] = {
    {1,2,3},
    {4,5,6},
    {7,8,9}
};
//此时的 px指针 指向的 {1,2,3}这个数组的首地址
int(*px)[3] = &arr[0];
// *px -> 此时就是数组{1,2,3}本身
//越过第一个数组 此时px指针指向 {4,5,6}的首地址
px++;
printf("%d %d \n",(*px)[0],px[0][0]);
//这里打印的就是 4 4

二维数组指针可以访问一维数组吗?

int arr[] = {1,2,3,4,5,6,7,8,9,0};
int(*px)[2][2] = (int(*)[2][2])arr;
//思考:
// *px 是什么类型
//int[2][2] 二维数组
//(*px)[1][1]的值是什么? px[0][1][1]的值是什么
//4

C语言20 结构体指针

探测结构体指针的特性

struct Point
{
    int x;
    int y;
}
  1. ++、–
  2. 加法与减法
  3. 指针相减

使用结构体指针

//创建结构体
Point p;
p.x=10;
p.y=20;
//声明结构体指针
Point* ps;
//为结构体指针赋值
ps = &p;
//通过指针读取数据
printf("%d \n",ps->x);
//通过指针修改数据
ps->y=100;
printf("%d\n",ps=>y);

结构体指针一定要指向结构体吗?

struct Point
{
    int x;
    int y;
}
int arr[10]={1,2,3,4,5,6,7,8,9,10}
Point* p = (Point*)arr;
for(int i = 0;i < 5;i++,p++)
{
    printf("%d %d\n",p->x,p->y);
}

C语言19 指针取值的两种方式

一级指针和多级指针

int i =100;
int* p1 = &i; 
int** p2 = &p1; 
int*** p3 = &p2; 
int**** p4 = &p3; 
int***** p5 = &p4 
int****** p6 = &p5; 
int******* p7 = &p6;
int****** px1 = *(p7);
int***** px2 = *(px1);
int**** px3 = *(px2);
int*** px4 = *(px3);
int** px5 = *(px4);
int* px6 = *(px5);
int px7 = *(px6);
int px7 = *(*(*(*(*(*(*(p7))))))); 

*()与[]的互换表示

int* p = (int*)1;
printf("%d %d \n",p[0],*p);  //p[0] = *(p+0) = *p 
int** p = (int**)1;
printf("%d %d \n",p[0][0],**p); 
printf("%d %d \n",p[1][2],*(*(p+1)+2));
int*** p = (int***)1;
printf("%d %d \n",p[1][2][3],*(*(*(p+1)+2)+3)); 
同理:
*(*(*(*(*(*(*(p7)))))))) 
= *(*(*(*(*(*(p7+0)+0)+0)+0)+0)+0)
= p7[0][0][0][0][0][0][0] 

总结

*(p+i) = p[i] 
*(*(p+i)+k) = p[i][k] 
*(*(*(p+i)+k)+m) = p[i][k][m] 
*(*(*(*(*(p+i)+k)+m)+w)+t) = p[i][k][m][w][t] 
*() 与 []可以相互转换 

C语言18 指针与字符串

字符串的几种表示方式有什么区别?

char str[6] = {"A","B","C","D","E","F"}; //"\0" 或者0 --堆栈中 局部变量
char str[] = "ABCDE";                   //编译器末尾填0 常量区
char* str= "ABCDE";                     //常量区
//打印
printf("%s \n",str);
//字符串打印时 遇到 0 才会停止

常用的字符串函数

1、int strlen(char* s)
返回值是字符串S的长度,不包括结束符"/0"
弊端:字符串中英文混合的话。返回的长度有问题,因为中文占用两个字节
2、char* strcpy(char* dest, char* src)
复制字符串src到dest中。返回指针为dest的值
3、char* strcat(char* dest, char* src)
将字符串src添加到dest尾部。返回指针为dest的值
4、int strcmp(char* s1, char* s2)
字符串对比,一样返回0 不一样返回非0

指针函数

本质就是函数,只不过函数的返回类型是某一类型的指针
如:

……

C语言17 数组参数传递

基本类型参数传递

int x =1;
void plus(int p)
{
    p = p+1;
}
printf("%d \n",x);
//x的值是多少?

数组作为参数

编写一个函数,能够打印任意整形数组的值

……

C语言16 取值运算符

“*”的几种用途

  1. 乘法运算符

    int x = 1; int y = 2; int z = x * y;

  2. 定义新的类型

    char x; char* x;

  3. 取值运算符

      • 指针类型的变量 int* a =(int*)1; printf("%x \n",(a +1)); 10: int a =(int*)1; 00401028 mov dword ptr [ebp-4],1 11: printf("%x \n",*(a +1)); 0040102F mov eax,dword ptr [ebp-4] 00401032 mov ecx,dword ptr [eax+4] 00401035 push ecx 00401036 push offset string "%x \n" (0042201c) 0040103B call printf (00401070) 00401040 add esp,8 当然这里运行的时候肯定出错 因为 1 是一个异常的不存在的地址

探测 * 指针类型 的类型

int*** a;
int***** b;
int******* c;
int* d;
//int x = *(a+1);
//int x = *(b++);
//int x = *(c-5);
//int x = *(d+6);
//总结
//    *加指针类型 的类型是 指针类型减去一个*

取值运算符举例

int x=1;                        int x =1;
int* p = &x;                    int* p = &x;
printf("%x %x \n",p,*(p));      int** p2 = &p;
*(p)=2;                         int*** p3 = &p2;
printf("%d \n",x);              int r = *(*(*(p3)));
                                printf("%d \n",r);

C语言15 取地址符号&的使用

&符号是取地址符,任何变量都可以使用&来获取地址,但不能用在常量上

struct Point
{
    int x;
    int y;
};
char a;
short b;
int c;
Point p;
printf("%p %p %p %p \n",&a,&b,&c,&p);
printf("%x %x %x %x \n",&a,&b,&c,&p);
printf("%x \n",&10);
16:       char a;
17:       short b;
18:       int c;
19:       Point p;
20:
21:       printf("%p %p %p %p \n",&a,&b,&c,&p);
0040D408   lea         eax,[ebp-14h]
0040D40B   push        eax
0040D40C   lea         ecx,[ebp-0Ch]
0040D40F   push        ecx
0040D410   lea         edx,[ebp-8]
0040D413   push        edx
0040D414   lea         eax,[ebp-4]
0040D417   push        eax
0040D418   push        offset string "%p %p %p %p \n" (00422e90)
0040D41D   call        printf (0040d6c0)
0040D422   add         esp,14h
22:       printf("%x %x %x %x \n",&a,&b,&c,&p);
0040D425   lea         ecx,[ebp-14h]
0040D428   push        ecx
0040D429   lea         edx,[ebp-0Ch]
0040D42C   push        edx
0040D42D   lea         eax,[ebp-8]
0040D430   push        eax
0040D431   lea         ecx,[ebp-4]
0040D434   push        ecx
0040D435   push        offset string "%x %x %x %x \n" (00422e80)
0040D43A   call        printf (0040d6c0)
0040D43F   add         esp,14h

探测 & 变量的类型

char a;
short* b;
int** c;
int x = &a;
int x = &b;
int x = &c;
以上代码全都无法编译成功

说明 使用了 &符号后 将会生成 &符号后类型的指针(加一个*)

……

C语言14 指针类型

C语言14 指针类型

指针是C语言中的一种数据类型,

定义带 *类型的变量

char    x;          char*       x;
short   y;          short*      y;
int     z;          int*        z;
float   f;          float*      f;
double  d;          double*     d;
Student st;         Student*    st;

总结:

……