【程序猿必备:指针与数组的高级技能秘籍】(中):https://developer.aliyun.com/article/1424716
数组参数和指针参数
一维数组传参
#include <stdio.h> void show(int a[]) { printf("show: %d\n", sizeof(a));//4 } int main() { int a[10] = { 0 }; printf("main: %d\n", sizeof(a));//40 show(a); return 0; }
- 数组传参是要发生降维的
- 为何要降维?不降维传数组会整体被拷贝一份,使调用函数成本变高
- 降维成什么? 降维成指向其内部元素类型的指针
- 有没有形成临时变量的拷贝?只要传参,就一定会发生临时变量的拷贝
//一维数组的元素个数是被忽略的。 //如何理解忽略? #include <stdio.h> void show(int a[], int num) { printf("show: %d\n", sizeof(a));//4 for (int i = 0; i < num; i++) { printf("%d\n", a[i]); } } int main() { int a[10] = { 0 }; int num = sizeof(a) / sizeof(a[0]); show(a, num); return 0; }
一维数组的元素个数是被忽略的。
- 如何理解忽略? 此时已结被降维成指针
一级指针传参
#include <stdio.h> void test(char* p) { //test: &p = 0093F890 printf("test: &p = %p\n", &p); } int main() { char* p = "hello world"; //main: &p = 0093F964 printf("main: &p = %p\n", &p); test(p); return 0; }
函数调用,指针作为参数,要不要发生拷贝?需要!因为指针变量,也是变量,在传参上,它也必须符合变量的要求,进行临时拷贝!
结论:在C语言中,只要函数调用,必定发生拷贝。只不过要根据具体情况去决定,拷贝了什么,拷贝了多少!
有没有可能直接把一个指针变量本身传递给指定函数?指针变量本身不能传递给函数,只能传递指针变量所在内存地址的副本。
但是间接有可能。 通过二级指针或者返回值两种方法。
#include <stdio.h> #define N 10 void GetStr(char* pp) { pp = malloc(sizeof(char) * N); if (NULL != pp) { strcpy(pp, "hello"); } else { //do nothing! } } int main() { char* p = NULL; GetStr(p);//指针变量 - 传值操作 //这里能不能打印出hello printf("%s\n", p);//hello return 0; }
在这个例子中,我们定义了一个指向char类型的指针变量p,并将其初始化为NULL。您将p作为参数传递给GetStr函数。在函数中,首先通过malloc函数为pp分配了一段内存,然后将字符串"hello"复制到pp所指向的内存中。
然而,在函数结束时,pp指针变量超出了其作用域,它所指向的内存已经被释放。这意味着在GetStr函数结束后,p仍然是NULL指针,它指向的位置并没有被赋值为"hello"字符串。在main函数中,您尝试打印指针p指向的字符串,但由于p仍然是NULL指针,所以您不会看到任何输出。
//二级指针的方法 #include <stdio.h> #define N 10 void GetStr(char** pp) { *pp = malloc(sizeof(char) * N); if (NULL != *pp) { strcpy(*pp, "hello"); } else { //do nothing! } } int main() { char* p = NULL; GetStr(&p);//指针变量的地址 - 传址操作 //这里能不能打印出hello printf("%s\n", p);//hello return 0; }
二维数组参数和二级指针参数
//二维数组传参 //要不要发生降维? 所有的数组传参,都要发生降维 //降维成什么? 降维成指针 //什么类型的指针呢?数组指针,仍然使指向其内部元素类型的指针 #include <stdio.h> void show(char a[3][4]) //或者void show(char a[][4])//在二维数组传参的时候,只能一维度被省略 //所以也可以写成void show(char(*a)[4]) { printf("show: %d\n", sizeof(a)); } int main() { char a[3][4] = { 0 }; printf("main: %d\n", sizeof(a)); show(a); return 0; }
#include <stdio.h> void show(char a[3][4]) { printf("show: %d\n", sizeof(a)); } int main() { char a[3][4] = { 0 }; printf("main: %d\n", sizeof(a)); show(10); //故意写错 return 0; }
结论:
- 二维数组传参
- 要不要发生降维? 所有的数组传参,都要发生降维
- 降维成什么? 降维成指针
- 什么类型的指针呢?数组指针,仍然是指向其内部元素类型的指针
- 在二维数组传参的时候,只能一维度被省略?因为数组的下标也是数组类型的一部分,所以能二维度决定了参数的类型
任何维度的数组,传参的时候,都要发生降维,降维成指向其内部元素类型的指针。
那么,二维数组,内部“元素”是一维数组!那么降维成指向一维数组的指针。
函数指针
函数指针的定义
函数指针是指向函数的指针变量,它可以存储函数的地址,使得程序能够通过指针直接调用该函数。
#include<stdio.h> int Add(int x, int y) { return x + y; } int main() { //函数是否有地址呢? //函数名和&函数名有区别吗? printf("0x%p\n", Add); printf("0x%p\n", &Add); return 0; }
- 函数是代码的一部分,程序运行的时候,也要加载到内存当中,以供CPU后续寻址访问,从上面的运行结果也知道,函数有地址, 函数名和&函数名没有区别,函数名和&函数名完全等价。
函数指针的使用
#include <stdio.h> #include <string.h> char* fun(char* s1, char* s2) { int i = strcmp(s1, s2); if (0 == i) { return s1; } else { return s2; } } int main() { //函数的数据类型是什么? //指针类型 (参数类型...) //char* (char*, char*); //也可以写成char* (*funp)(char*, char*) = &fun; char* (*funp)(char*, char*) = fun; //函数调用的多中方法 char* s1 = funp("hello", "world"); char* s2= (*funp)("hello", "world"); char* s3 = fun("hello", "world"); char* s4 = (*fun)("hello", "world"); char* s5 = (&fun)("hello", "world"); char* s6 = (*(&fun))("hello", "world"); printf(s1); printf(s2); printf(s3); printf(s4); printf(s5); printf(s6); return 0; }
小程序
#include<stdio.h> #include<string.h> static int flag; void Welcome() { printf("################################\n"); printf("########欢迎来到王者荣耀########\n"); printf("######## 1.play 0.exit ########\n"); printf("################################\n"); scanf("%d", &flag); } void GetGift() { if(flag == 1) printf("恭喜你获取地狱火的皮肤!"); } //王者荣耀欢迎界面和礼物赠送界面 void login(void (*Welcome)(),void(*GetGift)()) { #define NAME "175区摘星楼" #define PASSWD "123456" char name[32]; char passwd[32]; printf("Please Enter Your Name:>"); scanf("%s", name); printf("Please Enter Your Passwd:>"); scanf("%s", passwd); if ((strcmp(name, NAME) == 0) && (strcmp(passwd, PASSWD) == 0)) { Welcome(); GetGift(); } } int main() { login(Welcome, GetGift); return 0; }
(*(void (*)())0)() - 这是什么
#include <stdio.h> int main() { int* p = 0;//NULL == (void*)0 *p = 100;//取的是p的右值 *p == *0 *(int*)0 = 100;//与上面等价 (* (void(*)()) 0 ) ();//这里直接访问的是0的地址处 //解引用 无参数函数指针类型 强转 无参数 //严格意义上,是错的。因为参数不能传类型 (* (char** (*)(char**, char**))0) (char**, char**); //解引用 二级指针函数指针类型 强转 参数,参数 return 0; }
函数指针数组(4.7.4小节)
char* (*pf[3])(char *);
函数指针数组指针
char* (*(*pf)[3])(char *);