​【程序猿必备:指针与数组的高级技能秘籍】(下)

简介: ​【程序猿必备:指针与数组的高级技能秘籍】

【程序猿必备:指针与数组的高级技能秘籍】(中):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 *);


相关文章
|
20天前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
30 3
|
19天前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
30 2
|
28天前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
31 1
|
1月前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。
|
1月前
|
存储
如何通过指针数组来实现二维数组?
介绍了二维数组和指针数组的概念及其区别,详细讲解了如何使用指针数组模拟二维数组,包括定义与分配内存、访问和赋值元素、以及正确释放内存的步骤,适用于需要动态处理二维数据的场景。
|
1月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
1月前
魔法指针 之 二级指针 指针数组
魔法指针 之 二级指针 指针数组
19 1
|
1月前
|
存储
一篇文章了解区分指针数组,数组指针,函数指针,链表。
一篇文章了解区分指针数组,数组指针,函数指针,链表。
19 0
|
1月前
|
编译器 C语言
【C语言】指针篇-深入探索数组名和指针数组- 必读指南(2/5)
【C语言】指针篇-深入探索数组名和指针数组- 必读指南(2/5)
|
3月前
|
搜索推荐 C语言
指针与数组
指针与数组
59 9
下一篇
无影云桌面