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

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

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


相关文章
|
10月前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
115 3
|
10月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
10月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
10月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
10月前
|
容器
在使用指针数组进行动态内存分配时,如何避免内存泄漏
在使用指针数组进行动态内存分配时,避免内存泄漏的关键在于确保每个分配的内存块都能被正确释放。具体做法包括:1. 分配后立即检查是否成功;2. 使用完成后及时释放内存;3. 避免重复释放同一内存地址;4. 尽量使用智能指针或容器类管理内存。
|
10月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
194 4
|
10月前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
112 2
|
10月前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
101 1
|
11月前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。
|
11月前
|
存储
如何通过指针数组来实现二维数组?
介绍了二维数组和指针数组的概念及其区别,详细讲解了如何使用指针数组模拟二维数组,包括定义与分配内存、访问和赋值元素、以及正确释放内存的步骤,适用于需要动态处理二维数据的场景。