char*
int main()
{
char ch='w';
char *pc=&ch;
*pc='w';
return 0;
}
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
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
void print_arr1(int arr[3][5], int row, int col)
{
int i = 0;
for(i=0; i|
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
void test()
{
printf("heihei\n");
}
int main()
{
printf("%p\n",test);
printf("%p\n",&test);
return 0;
}
输出结果,两地址相同。
为test函数地址
函数名(同数组名含义)是函数的入口地址
#include
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
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
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
#include
#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
命令行参数
//参数名称无所谓
//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 :显示目录
最难不过坚持!