仪征建设局招投标网站,学做川菜的网站,越秀区建设局网站,广州网站开发设计平台C语言指针详解 字符指针1.如何定义2.类型和指向的内容3.代码例子 指针数组1.如何定义2.类型和内容 数组指针1.如何定义2.类型和指向类型3.数组名vs数组名数组指针运用 数组参数指针参数一维数组传参二维数组传参一级指针传参二级指针传参 函数指针1.如何定义2.类型和… C语言指针详解 字符指针1.如何定义2.类型和指向的内容3.代码例子 指针数组1.如何定义2.类型和内容 数组指针1.如何定义2.类型和指向类型3.数组名vs数组名数组指针运用 数组参数指针参数一维数组传参二维数组传参一级指针传参二级指针传参 函数指针1.如何定义2.类型和指向内容3.函数名vs函数名4.两个有趣的代码 函数指针数组1.如何定义2.类型和内容3.1代码例子switch语句实现计算器3.2代码例子函数指针数组实现计算器 指向函数指针数组的指针1.如何定义2.类型和指向内容 回调函数一个小知识点如何找到指针和数组的类型和指向存储内容 博主主页zoro-1 祝大家有个好心情给大家分享一下我拍的彩虹 字符指针
1.如何定义
int main() {
char chw;
char *pcch;
*pcw;
return 0;
}2.类型和指向的内容 譬如char pcch 将左边pc去掉就是指针类型char 将*和pc去掉就是指针指向的内容char 3.代码例子
例1
int main()
{
const char* pstr hello;//这里是把一个字符串放到pstr指针变量里了吗
printf(%s\n, pstr);
return 0;}这时我就要问一个问题了pstr存储的是什么 没错pstr存储的是hello的首元素地址 例2
#include stdio.h
int main()
{
char str1[]hello bit.;
char str2[]hello bit.;
const char*str3hello bit.;
const char*str4hello bit.;
if(str1str2)
printf(str1 and str2 are same\n);
else
printf(str1 and str2 are not same\n);
if(str3str4)
printf(str3 and str4 are same\n);
else
printf(str3 and str4 are not same\n);
return0;大家猜猜这段代码会输出什么 代码解释str1str2是存放char的数组的数组名那么他们存放的就是数组首元素的地址即使他们的内容相同但地址是随机的而str3str4是被const修饰的指针变量存放的也是字符的地址只不过这里的hello bit.在这里是存放在常量池的下一个str4不需要开辟新的空间所以str3str4他们指向的是同一个hello bit.所以相同 一个小知识点 如果 const 用于修饰字符串常量那么该字符串常量将存储在常量存储区Constant Storage Area。 常量存储区是用于存储常量字符串和全局常量的特殊内存区域其中的数据在程序运行期间保持不变。
指针数组
1.如何定义
int main(){
int arr[5]{1,2,3,4,5};
int arr1[5]{2.3.4.5.6};
int arr2[5]{3,4,5,6,7};
int*p[3]{arr,arr1,arr};
}
2.类型和内容 去掉名字p就是类型 去掉p【3】就是存储的内容int* 数组指针
1.如何定义 int arr[5]{1,2,3,4,5};
int (*p)[10]arr;
//解释p先和*结合说明p是一个指针变量然后指着指向的是一个大小为10个整型的数组。所以p是一个指针指
向一个数组叫数组指针。
//这里要注意[]的优先级要高于*号的所以必须加上来保证p先和*结合。2.类型和指向类型 去掉名字p就是类型 去掉*p就是指向内容 3.数组名vs数组名 数组名是数组的首元素地址 但有两个例外 1.arr得arr表示整个数组的地址 2.sizeofarr表示整个数组的大小 #include stdio.h
int main()
{
int arr[10] {0};
printf(%p\n, arr);
printf(%p\n, arr);
return 0;
}大家猜猜这段代码执行结果是什么如果看过我之前的指针初阶就应该知道他们的输出内容一样 代码解释arr是数组首元素地址arr是整个数组的地址虽然他们输出的内容一样但是他们的权重不一样 arr1会跳过4字节arr1会跳过整个数组也就是10*4一共40个字节 #include stdio.h
int main()
{
int arr[10] { 0 };
printf(arr %p\n, arr);
printf(arr %p\n, arr);
printf(arr1 %p\n, arr1);
printf(arr1 %p\n, arr1);
return 0;
}这段代码就能很好解释arr和arr的区别了
数组指针运用
我们很少用在一维数组多数用在二维数组接下来我用代码让大家感受一下数组指针 一维数组
#include stdio.h
int main()
{
int arr[10] {1,2,3,4,5,6,7,8,9,0};
int (*p)[10] arr;//把数组arr的地址赋值给数组指针变量p
//但是我们一般很少这样写代码
return 0;
}二维数组
一个数组指针的使用
学了指针数组和数组指针我们来一起回顾并看看下面代码的意思
#include stdio.h
void print_arr1(int arr[3][5], int row, int col)
{
int i 0;
for(i0; irow; i)
{
for(j0; jcol; j)
{
printf(%d , arr[i][j]);
}
printf(\n);
}
}
void print_arr2(int (*arr)[5], int row, int col)
{
int i 0;
for(i0; irow; i)
{
for(j0; jcol; j)
{
printf(%d , arr[i][j]);//相当于*(*arrij)
}
printf(\n);
}
}
#include stdioh
int main()
{
int arr[3][5] {1,2,3,4,5,6,7,8,9,10};
print_arr1(arr, 3, 5);
//数组名arr表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr其实相当于第一行的地址是一维数组的地址
//可以数组指针来接收
我们发现这两个遍历数组的1方法只有接收数组第一个形参不一样这里写成int arr[3][5]是为了大家好理解
编译器会将int arr[3][5]转化成int (*arr)[5]
print_arr2(arr, 3, 5);
return 0;
} 数组参数指针参数
在写代码时候难免要把【数组】或者【指针】传给函数那函数的参数该如何设计呢
一维数组传参
#include stdio.h
void test(int arr[])//ok?
显然是可以的传一维数组用一维数组接收
{}
void test(int arr[10])//ok?
显然是可以的只是加上了长度
{}
void test(int *arr)//ok?
这种可以int*arr是整型指针一维数组传过来是第一个元素地址第一个元素也是整型
{}
void test2(int *arr[20])//ok?
这种显然可以的传的是指针数组也是用指针数组接收
{}
void test2(int **arr)//ok?
传的是指针数组的首元素地址而元素又是指针所以用二级指针接收二级指针解引用一次得到首元素首元素就是指针int*
{}
int main()
{
int arr[10] {0};
int *arr2[20] {0};
test(arr);
test2(arr2);
}
二维数组传参
void test(int arr[3][5])//ok
显然是可以的传二维数组用二维数组接收
{}
void test(int arr[][])//ok
虽然形参是二维数组但是不能省略列
{}
void test(int arr[][5])//ok
行可以省略列不能省略所以可以
{}
//总结二维数组传参函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组可以不知道有多少行但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr)//ok
二维数组的第一个元素是一维数组不能用整形指针接收而应该用数组指针接收
{}
void test(int* arr[5])//ok
这是指针数组所以不对
{}
void test(int (*arr)[5])//ok
这是数组指针所以可以{}
void test(int **arr)//ok
这是二级指针不行因为传过来的是一维数组地址
{}
int main()
{
int arr[3][5] {0};
test(arr);
}总结 一维整形数组传参数可以用一维整形数组接收也可以用整形指针接收 一维指针数组可以用一维指针数组接收也可以用二维指针接收 二维整形数组传参数可以用二维整形数组接收也可以用数组指针接收
一级指针传参
#include stdio.h
void print(int *p, int sz)
{
int i 0;
for(i0; isz; i)
{
printf(%d\n, *(pi));
}
}
int main()
{
int arr[10] {1,2,3,4,5,6,7,8,9};
int *p arr;
int sz sizeof(arr)/sizeof(arr[0]);
//一级指针p传给函数
print(p, sz);
//也可以直接写成printf(p,sz);
return 0;
}二级指针传参
当函数参数是二级指针可以接收哪些参数
void test(char **p)
{
}
int main()
{
char c b;
char*pc c;
char**ppc pc;
char* arr[10];
test(pc);
test(ppc);
test(arr);//Ok?
return 0;
}
函数指针 上面介绍了整形指针数组指针那么函数有没有指针呢 答案是有 1.如何定义
#include stdio.h
void test()
{
printf(hehe\n);
}
int main()
{
void (*p)()test;
return 0;
}2.类型和指向内容 去掉名字p就是类型 去掉*p就是指向内容 3.函数名vs函数名
#include stdio.h
void test()
{
printf(hehe\n);
}
int main()
{
printf(%p\n, test);
printf(%p\n, test);
return 0;
}注函数名是函数地址函数名也是函数地址没有区别
4.两个有趣的代码
注 :推荐《C陷阱和缺陷》
这本书中提及这两个代码。
//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);*代码1解释是将0看做成地址强制转化成函数指针然后解引用调用函数 代码2解释signal是一个函数有两个参数intviod*int返回值是一个函数指针 代码2简化 typedef void(pfun_t)(int); pfun_t signal(int, pfun_t);
函数指针数组 有指针数组那么有没有函数指针数组呢 答案是有 1.如何定义 #include stdio.h
#include string.hint Add(int x, int y)
{return x y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}int main()
{//int (*pf1)(int, int) Add;//int (*pf2)(int, int) Sub;//int (*pf3)(int, int) Mul;//int (*pf4)(int, int) Div;//函数指针数组//int (*pfArr[4])(int, int) {Add, Sub, Mul, Div};//return 0;
}2.类型和内容 去掉pfArr就是类型 去掉pfArr【4】就是存储的内容函数指针 3.1代码例子switch语句实现计算器
如果让你们实现自算器的简易功能你们会怎么实现
int Add(int x, int y)
{return x y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}
//目录
void menu()
{printf(***************************\n);printf(***** 1.add 2.sub ******\n);printf(***** 3.mul 4.div ******\n);printf(***** 0.exit ******\n);printf(***************************\n);
}
int main()
{int input 0;int x 0;int y 0;int ret 0;do{menu();printf(请选择:);scanf(%d, input);switch (input){case 1:printf(请输入两个操作数:);scanf(%d %d, x, y);ret Add(x, y);printf(ret %d\n, ret);break;case 2:printf(请输入两个操作数:);scanf(%d %d, x, y);ret Sub(x, y);printf(ret %d\n, ret);break;case 3:printf(请输入两个操作数:);scanf(%d %d, x, y);ret Mul(x, y);printf(ret %d\n, ret);break;case 4:printf(请输入两个操作数:);scanf(%d %d, x, y);ret Div(x, y);printf(ret %d\n, ret);break;case 0:printf(退出计算器\n);break;default:printf(选择错误重新选择\n);break;}} while (input);return 0;
}有没有感觉上面这段代码很冗杂需要这么多次case 接下来我用函数指针数组再来实现一下
3.2代码例子函数指针数组实现计算器
int Add(int x, int y)
{return x y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}//...void menu()
{printf(***************************\n);printf(***** 1.add 2.sub ******\n);printf(***** 3.mul 4.div ******\n);printf(***** 0.exit ******\n);printf(***************************\n);
}
int main()
{int input 0;int x 0;int y 0;int ret 0;//函数指针数组的使用 - 转移表int (* pfArr[5])(int, int) {NULL, Add, Sub, Mul, Div};// 0 1 2 3 4do{menu();printf(请选择:);scanf(%d, input);if (input 1 input 4){printf(请输入两个操作数:);scanf(%d %d, x, y);ret pfArr[input](x, y);printf(ret %d\n, ret);}else if(input 0){printf(退出计算器\n);}else{printf(选择错误重新选择\n);}} while (input);return 0;
}
这里我们将函数储存在数组里面直接通过下标调用是不是感觉很方便
指向函数指针数组的指针 这里给大家表演一个套娃是不是感觉很绕口这里我们不过多解释只讲定义用处不多 1.如何定义
void test(const char* str)
{printf(%s\n, str);
}int main()
{void (*pf)(const char*) test;//pf是函数指针变量void (*pfArr[10])(const char*);//pfArr是存放函数指针的数组void (* (*p) [10])(const char*) pfArr;//p指向函数指针数组的指针return 0;
}
2.类型和指向内容 去掉p就是类型 去掉*p就是指向内容 回调函数 这个意思就是有函数A,B,main方法调用函数A将函数B地址作为参数传到函数AA中利用函数地址调用函数B 这个我会在下一篇博客讲解qsort时举例讲解 一个小知识点如何找到指针和数组的类型和指向存储内容
*类型都是去掉名字 指针指向的内容是去掉指针名字 数组存储内容是去掉名字和【】中括号 更多指针相关内容请听下回讲解看到这里了不妨给博主给个三连要是想持续收听也可以关注博主 让我们一起变得更强吧大家加油