指针进阶学习

字符指针

char*

一般的两种用法

1.指针指向字符地址

int main()
{
    char ch='w';
    char *pc=&ch;
    *pc='w';
    return 0;
}

2.指针调用字符数组(字符串)

int main()
{
    char str[]="hello";
    char*pstr1=str;
    printf("%s\n",str);		//%s以'\0'未结束标志,否则会有乱码
    char*pstr2="hello";
    printf("%s\n",pstr2);
    //pstr2="hi";
    return 0;
}

pstr1常规使用

pstr2指向的是字符串常量"hello"的地址,指向位置未知;且字符常量,为静态(static),且为常量,无法修改!

示例

#include <stdio.h>
int main()
{
    char str1[] = "hello bit.";
    char str2[] = "hello bit.";
    char *str3 = "hello bit.";
    char *str4 = "hello bit.";
    if(str1 ==str2)		//==比较的是地址
    	printf("str1 and str2 are same\n");
    else
 		printf("str1 and str2 are not same\n");       
    if(str3 ==str4)
 		printf("str3 and str4 are same\n");
    else
 		printf("str3 and str4 are not same\n");       
    return 0;
}

指针进阶

int为例

数组概念回顾

数组名代表整个数组的两种情况

sizeof(数组名)

&数组名

int main()
{
    int arr[3]={1,2,3};
    printf("%p\n",arr[0]);
    printf("%p\n",arr);
    printf("%p\n",&arr);
    
    printf("%p\n",arr+1);
    printf("%p\n",&arr+1);
    return 0;
}

指针数组

是数组

[]优先级高于*

数组中每个元素是指针

int *a[5];

a数组有五个元素每个元素是int型指针(指向int的指针)

数组指针

是指针

[]优先级高于*

指向数组的指针

int (*a)[5];		//[]值不能省略

//使用
//1.0
int arr[5]={1,2,3,4,5};
a=&arr;
//2.0
int arr[5][5];
a=arr;

二维数组

第二个[]值不能省略

每个元素,都是一个数组。

int arr[3][3]={1,2,3,        arr[0]

                     4,5,6,        arr[1]

                     7,8,9};       arr[2]

示例

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
    int i = 0;
    for(i=0; i<row; i++)
   {
        for(j=0; j<col; j++)
       {
            printf("%d ", arr[i][j]);
       }
        printf("\n");
   }
}
void print_arr2(int (*arr)[5], int row, int col)
{
    int i = 0;
    for(i=0; i<row; i++)
   {
        for(j=0; j<col; j++)
       {
            printf("%d ", arr[i][j]);
       }
        printf("\n");
   }
}
int main()
{
    int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
    print_arr1(arr, 3, 5);
    //数组名arr,表示首元素的地址
    //但是二维数组的首元素是二维数组的第一行
    //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
    //可以数组指针来接收
    print_arr2(arr, 3, 5);
    return 0;
}

a是指向数组的指针,该数组有五个元素每个元素是int型

数组参数&指针参数

数组传参会退化为指针

一维数组传参

void test (int arr[])
{}
void test(int *p)
{}
void test2(int **p)
{}
int main()
{
    int arr[10]={0};
    int *arr2[10]={0};
    test(arr);
    test2(arr2);
} 

二维数组传参

void test1(int arr[3][5])
{}
void test2(int arr[][5])		//arr[]会退化为指针
{}
void test3(int (*p_arr)[5])           //标准写法
{}
//no,二维数组传参只能省略第一个[]的数字
//对于一个二维数组可以不知道行,必须知道一行有多少个元素
//void test(int arr[][])           
//{}
int main()
{
    int arr[3][5];
    test1(arr);
    test2(arr);
    test3(arr);
    return 0;
}

一级指针

void test(int *p)
{}
int main()
{
    int a;
    int arr[10];
    test(&a);
    test(arr);
    return 0;
}

可以接收int型数据地址(传址操作)

接受一维数组

二级指针

指向指针的指针

void test(int **p)
{}
int main()
{
    int *arr[10];
    test(arr);
}

二级指针指向数组首元素地址,而指针数组的每个元素都为指针

函数指针

是指针

()优先级高于*

指向函数的指针

int (*a)(int);

所指函数返回值,(指针),(所指函数参数)

a是指向函数的指针,该函数有一个int类型的形参,返回值类型为int

#include<stdio.h>
void test()
{
    printf("heihei\n");
}
int main()
{
    printf("%p\n",test);
    printf("%p\n",&test);
    return 0;
}

输出结果,两地址相同。

为test函数地址

函数名(同数组名含义)是函数的入口地址

示例

#include<stdio.h>
void test()
{
    printf("hell\n");
}
int test1(int a,int b)
{
    return a+b;
}
int main()
{
    void(*pfun1)()=&test;			//1.0
    (*pfun1)();
    void(*pfun11)()=test;			//2.0	常用
    pfun11();
    
    int(*pfun2)(int,int)=&test1;
    (*pfun2)(1,2);
    int(*pfun2)(int,int)=test1;
    pfun22(1,2);
}

指针函数

是函数

()优先级高于*

函数的返回值为指针

int * a(int);

a函数有一个int形参,返回int指针

int*fun(int a,int b)
{
    static int v=a+b;
    return &v;
}

多个组合练习

定义

右左法则

未知变量名开始先右,遇括号就掉头

int (*a[10])[10];

a是一个指针数组,数组有十个指针元素,每个指针指向一个数组,该数组有10个整型元素

int(*(*a)[10])(int);

a是指针,指向数组,数组有十个元素,每个元素是指针,该指针指向一个函数,函数有一个整型参数,返回值类型是int型

int*(*(*a)())[10];

a是指针,指向函数,该函数无参,返回一个指针,该指针指向数组,数组有10个元素,每个元素为int指针

大小

int (*ar)[10][10];
printf("%d",sizeof(ar));		//?

是指针,4字节

short (*ar[10])[10];

是指针数组,40字节

简化定义

typedef简化

typedef int(*pArray)[10];
typedef int(*pFun)(int*);
void main()
{
    pArray pa;
    pFun pf;
}

定义该类数组指针类型名,pArray

定义该类函数指针类型名,pFun

示例

#include<stdio.h>
typedef int(*pFun)(int*);
typedef pFun(*pArray)[5];
int main()
{
    //int (*(*func)[5])(int *)
    //pFun (*func)[5];
    pArray func;
}

解读(c陷阱与缺陷charpter2)

(*(void(*)())0)();

调用首地址为0的子程序(函数)

类型强转:()

将0强转为函数指针类型,该类型指针指向一个无参无返回值的函数;

再调用0指向的函数;

简化

typedef void(*pFun)();
(*(pFun)0)();

转移表(函数指针数组)

数组是一个存放相同类型数据的存储空间,通过函数指针数组(转移表),实现代码的简化!

本质还是数组,使用方法就是数组的使用方法。

示例

计算器的简易实现

#include <stdio.h>
int add(int a, int b)
{
    return a + b;
}
int sub(int a, int b)
{
    return a - b;
}
int mul(int a, int b)
{
    return a*b;
}
int div(int a, int b)
{
    return a / b;
}

函数结构统一。



typedef enmu{ADD,SUB,MUL,DIV,QUIT};
int main()
{
    int x, y;
    int input = 1;
    int ret = 0;
    do
    {
         printf( "*************************\n" );
         printf( " 1:add           2:sub \n" );
         printf( " 3:mul           4:div \n" );
         printf( "*************************\n" );
         printf( "请选择:" );
         scanf( "%d", &input);
         printf("输入操作数:" ); 
         scanf( "%d %d", &x, &y);
         switch (input)
         {
         case 1://ADD
              //魔鬼数字,数字含义不清晰!使用宏定义,或enum枚举
              ret = add(x, y);
              printf( "ret = %d\n", ret);
              break;
         case 2:
//SUB           
              ret = sub(x, y);
              printf( "ret = %d\n", ret);
              break;
         case 3:
//MUL     
              ret = mul(x, y);
              printf( "ret = %d\n", ret);
              break;
         case 4:DIV

              ret = div(x, y);
              printf( "ret = %d\n", ret);
              break;
         case 0://QUIT
              printf("退出程序\n");
              break;
        default:
              printf( "选择错误\n" );
              break;
       }
   } while (input);
    
    return 0;
}

switch语句可以通过转移表化简。

//简化
//typedef int (*pFun)(int,int);
//pFun p[5];
int(*p[])(int x,int y) = {NULL,add,sub,mul,div};			//转移表定义

int main()
{
    int x,y;
    int input = 1;
    int ret = 0;
    while(input)
    {
        printf("************************\n");
        printf("*   1.add       2.sub  *\n");
        printf("*   3.mul       4.div  *\n");
        printf("************************\n");
        scanf("%d",&input);
        if((input<=4 && input >= 1))
        {
            printf("输入操作数:");
            scanf("%d %d",&x,&y);
            ret = (*p[input])(x,y);
            // ret = p[input](x,y);                  
        }
        else
        	 printf("输入有误!\n");
        printf("ret = %d",ret);
    }
    return 0;
}

指向函数指针数组的指针

void test(const char *str)
{
    printf("%s\n",str);
}
int main()
{
    //函数指针pFun
    void(*pfun)(const char*) = test;
    //函数指针的数组pFunArr
    void (*pfunArr[5])(const char*str);
    pfunArr[0]=test;
    //指向函数指针数组pfunArr的指针ppfunArr
    void (*(*ppfunArr)[10])(const char*) = &pfunArr;
    return 0;
}

回调函数

回调函数就是一个通过函数指针调用的函数。

将一个函数的指针(地址)作为参数传递给另一个函数,这个指针被用来调用所指向的函数时,我们说这是回调函数。

void类型

只能定义指针

无法直接对指针进行操作

可以通过类型强转实现操作(C语言——指针)

int main()
{
    void *p_void;
    int *p_int;
    int a=1;
    p_int = &a;
    p_void = p_int;
    
}

示例

#include <stdio.h>
#include<stdlib.h>

#define _CRT_SECURE_NO_WARNINGS

//通过冒泡函数实现qsort()函数的模拟
//qsort()快速排序
//*可以排序各种能定义的类型数据

//用户定义比较函数,根据返回值,判断大小,是否交换数据
// 本例使用int比较
//*返回值为int型
//*传入两个参数,通过指针操作(方便操作各个类型数据)
//*可以通过相反数,来实现从大到小,还是由小到大
int cmp_int(const void* p1, const void* p2)
{
    return (*(int*)p1 - *(int*)p2);         //<0 =0 >0
}

//交换数值,可以交换各种类型数据
//*传入为void指针,可以交换各种类型数据
//*指针类型不明确,强转void为(char*),步长为1字节,方便操作各类型数据
//*根据size确定指针移动步长,确保交换完整数据
//--例如
//      交换为int型,4字节,即完整数据为4字节
//      每次交换1字节,共交换4次
void _swap(void* p1, void* p2, int size)
{
    for (int i = 0; i < size; i++)
    {
        *((char*)p1 + i) ^= *((char*)p2 + i);
        *((char*)p2 + i) ^= *((char*)p1 + i);
        *((char*)p1 + i) ^= *((char*)p2 + i);
    }
}

//冒泡排序,排序所有类型数组数据
//*四个参数:排序元素首地址,排序元素个数,元素类型大小,指向比较函数的函数指针
//*无返回值
void BubbleSort(void* base, int num, int size, int (*cmp)(const void* p1,const void* p2))
{
    for (int i = 0; i < num - 1; i++)
    {
        for (int j = 0; j < num - i - 1; j++)
        {
            //比较
            if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
            {
                //交换数值
                _swap((char*)base + j * size, (char*)base + (j + 1) * size,size);
            }
        }
    }
}


int main()
{
    int arr[] = { 80,20,1,3,46,96,8,9,85,79,81 };
    int n = sizeof(arr) / sizeof(arr[0]);
    BubbleSort(arr, n, sizeof(int), cmp_int);
    return 0;
}

可以自己尝试实现char、double、float、struct的cmp

主函数main的参数

命令行参数

//参数名称无所谓
//argc:命令行参数个数,初始为1
//argv:命令行参数值,以字符串指针数组定义;初始为该文件所在目录
//"远古时代"程序的调用在命令行,查找到文件位置并调用
int main(int argc, char* argv[])
{
	printf("%d\n", argc);
	for (int i = 0; i < argc; i++)
	{
		printf("%s\n", argv[i]);

	}
	return 0;
}

命令行参数传值

1.vs2019 项目->属性->调试

2.通过命令行传值

cd 文件 :进入目录

dir :显示目录

最难不过坚持! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值