C语言 — 指针进阶篇(上)

news2024/11/23 21:33:17

前言

指针基础篇回顾可以详见: 指针基础篇(1)指针基础篇(2)

指针进阶篇分为上下两篇,上篇介绍1 — 4,下篇介绍5 — 6

  1. 字符指针
  2. 数组指针
  3. 指针数组
  4. 数组传参和指针传参
  5. 函数指针
  6. 函数指针数组
  7. 指向函数指针数组的指针
  8. 回调函数

文章目录

  • 前言
  • 一、字符指针
    • 1. 字符指针用法
    • 2. 字符指针面试题
  • 二、指针数组
    • 1. 不同类型的指针数组
  • 三、数组指针
    • 1. 数组指针定义
    • 2. &数组名与数组名的区别
    • 3. 数组指针的使用
  • 四、数组传参和指针传参
    • 1. 一维数组传参
    • 2. 二维数组传参
    • 3. 一级指针传参
    • 4. 二级指针传参
  • 五、总结

一、字符指针

在基础篇中简单介绍过字符指针

1. 字符指针用法

一般使用

int main()
{
	char ch = 'a';
	char* p = &ch;
	*p = 'c';
	return 0;
}

另一种用法

#include<stdio.h>
int main()
{
	const char* psr = "hello word";
	printf("%s\n", psr);
	return 0;
}

输出结果
在这里插入图片描述

输出的结果是hello word,字符串的首字符的地址存放在字符指针psr
即意思是把一个常量字符串的首字符h的地址存放在指针变量 psr

2. 字符指针面试题

演示代码

#include <stdio.h>
int main()
{
 char str1[] = "hello bit.";
 char str2[] = "hello bit.";
 const char *str3 = "hello bit.";
 const 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;
}

输出结果
在这里插入图片描述
结论

这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存
但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。
所以str1和str2不同,str3和str4相同。

二、指针数组

在指针基础篇中提到过,指针数组就是存放指针的数组

1. 不同类型的指针数组

int * arr1[10]; 		//整形指针数组 ,里面存放整形指针
char * arr2[10];		//一级字符指针数组,里面存放字符指针
char ** arr3[10];		//二级字符指针数组,里面存放二级字符指针

三、数组指针

1. 数组指针定义

数组指针区别于上面的指针数组,数组指针是指针指针数组是数组。


数组指针和指针数组是两个不同的概念。

数组指针是指一个指向数组的指针变量。它可以用于访问数组元素,也可以作为函数参数传递,可以定义如下:

int (*arr_ptr)[N]; // 定义一个指向长度为 N 的整型数组的指针

这个指针变量可以指向长度为 N 的整型数组,可以通过以下方式初始化:

int arr[N] = {1, 2, 3, 4, 5};
int (*arr_ptr)[N] = &arr; // 指向数组arr的指针

指针数组是指一个包含指针的数组。它可以用来存储一组指针,也可以通过指针访问数组元素,可以定义如下:

int *ptr_arr[N]; // 定义一个包含N个指向整型的指针的数组

这个数组包含 N 个指针变量,每个指针变量可以指向一个整型变量,可以通过以下方式初始化:

int a = 10, b = 20, c = 30;
int *ptr_a = &a, *ptr_b = &b, *ptr_c = &c;
int *ptr_arr[] = { ptr_a, ptr_b, ptr_c };

以上都是基本的定义和初始化方式,具体使用还需要根据具体场景和需求进行操作。

2. &数组名与数组名的区别

演示代码

#include <stdio.h>
int main()
{
 int arr[10] = {0};
 printf("%p\n", arr);
 printf("%p\n", &arr);
 return 0;
}

输出结果
在这里插入图片描述
结论
总所周知,arr是数组名,数组名是首元素的地址
由输出结果得arr和&arr的输出结果相同,其实这两者是存在区别的


在上面的基础上我们对arr和&arr进行+1操作

演示代码

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("arr = %p\n", arr);
	printf("arr+1 = %p\n", arr + 1);
	printf("&arr= %p\n", &arr);
	printf("&arr+1= %p\n", &arr + 1);
	return 0;
}

输出结果

在这里插入图片描述

结论

由代码得,arr和&arr输出的值是一样的,但意义不同
&arr表示的是数组的地址,而不是数组首元素的地址
在这里插入图片描述

arr表示首元素地址,首元素地址+1,就是跳过一个元素的指针,( int是4个字节 )就是+4
&arr表示数组地址,数组地址+1,就是跳过整个数组的大小,所以&arr+1对于&arr的差值是40

3. 数组指针的使用

数组指针指向的是数组,那数组指针中存放的应该是数组的地址。

代码演示

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	int(*p)[10] = &arr;//(*p)表示p是一个指针变量,[10]表示有10个元素
	return 0;
}

(*p)表示p是一个指针变量,[10]表示有10个元素


数组指针的使用举例

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
 int i = 0;
 for(i=0; i<row; i++)
 {
 for(j=0; j<col; j++)
 {
 printf("%d ", arr[i][j]);
 }
 printf("\n");
 }
}
void print_arr2(int (*arr)[5], int row, int col)
{
 int i = 0;
 for(i=0; i<row; i++)
 {
 for(j=0; j<col; j++)
 {
 printf("%d ", arr[i][j]);
 }
 printf("\n");
 }
}
int main()
{
 int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
 print_arr1(arr, 3, 5);
 //数组名arr,表示首元素的地址
 //但是二维数组的首元素是二维数组的第一行
 //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
 //可以数组指针来接收
 print_arr2(arr, 3, 5);
 return 0;
}

四、数组传参和指针传参

写代码时(以往写冒泡排序时)需要把数组或指针传给函数,下面介绍函数的参数应该如何设计


1. 一维数组传参

#include <stdio.h>
void test(int arr[])//ok
{}
void test(int arr[10])//ok
{}
void test(int *arr)//ok
{}
void test2(int *arr[20])//ok
{}
void test2(int **arr)//ok
{}
int main()
{
 int arr[10] = {0};
 int *arr2[20] = {0};
 test(arr);
 test2(arr2);
}

总结:以上传参都是正确的


2. 二维数组传参

void test(int arr[3][5])//ok
{}
void test(int arr[][])  //NO  形参部分,行可以省略,列不能省略
{}
void test(int arr[][5])	//ok
{}
void test(int *arr)     //NO
{}
void test(int* arr[5])	//NO  形参是指针数组
{}
void test(int (*arr)[5])//ok   形参是数组指针
{}
void test(int **arr)	//NO  二级指针
{}
int main()
{
 int arr[3][5] = {0};
 test(arr);
}

总结:二维数组传参,函数形参的设计只能省略第一个[ ]的数字。因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。这样才方便运算。


3. 一级指针传参

#include <stdio.h>
void print(int *p, int sz)
{
 int i = 0;
 for(i=0; i<sz; i++)
 {
 printf("%d\n", *(p+i));
 }
}
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);
 return 0;
}

总结:传过去的参数和形参是匹配的上即可


4. 二级指针传参

#include <stdio.h>
void test(int** ptr)
{
	printf("num = %d\n", **ptr); 
}
int main()
{
	 int n = 10;
	 int*p = &n;
	 int **pp = &p;
	 test(pp);
	 test(&p);
	 return 0;
}

总结:传pp和&p都可以,相互对应即可

当函数的参数为二级指针的时候,可以接收什么参数?

void test(char **p)
{
 
}
int main()
{
 char c = 'b';
 char*pc = &c;
 char**ppc = &pc;
 char* arr[10];	 
 test(&pc);	 //OK
 test(ppc);	 //OK
 test(arr);   //OK
 return 0;
}

五、总结

本文介绍了字符指针、数组指针、指针数组、数组传参和指针传参。

数组指针是指针,指针数组是数组

arr表示首元素地址,首元素地址+1,就是跳过一个元素的指针,( int是4个字节 )就是+4
&arr表示数组地址,数组地址+1,就是跳过整个数组的大小,所以&arr+1对于&arr的差值是40

二维数组传参,函数形参的设计只能省略第一个[ ]的数字。因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。这样才方便运算。


如这篇博客对大家有帮助的话,希望 三连 支持一下 !!! 如果有错误感谢大佬的斧正 如有 其他见解发到评论区,一起学习 一起进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/747907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode-每日一题【147.对链表进行插入排序】

题目 给定单个链表的头 head &#xff0c;使用 插入排序 对链表进行排序&#xff0c;并返回 排序后链表的头 。 插入排序 算法的步骤: 插入排序是迭代的&#xff0c;每次只移动一个元素&#xff0c;直到所有元素可以形成一个有序的输出列表。 每次迭代中&#xff0c;插入排序…

MySQL分区表详解

目录 分区表介绍 分区适用场景 分区方式 分区策略 常见分区命令 1. 分区表介绍 MySQL 数据库中的数据是以文件的形势存在磁盘上的&#xff0c;默认放在 /var/lib/mysql/ 目录下面&#xff0c;我们可以通过 show variables like %datadir%; 命令来查看&#xff1a; 我们进入…

a标签form表单,转发,重定向

a标签需要写项目名,form表单需要写项目名,转发写请求路径,重定向需要写项目名 // window.location.href"请求路径" 需要写项目名 // window.location"请求路径" 需要写项目名 // document.location.href"请求路径" 需要写项目名 …

业务流程图怎么画?这几种绘制方法看一看

业务流程图怎么画&#xff1f;流程图提供了对业务流程的清晰概述&#xff0c;帮助人们理解工作流程中涉及的活动、决策和步骤。它定义了任务的顺序和依赖关系&#xff0c;使人们能够更好地了解整个流程。通过绘制流程图&#xff0c;可以更容易地识别出潜在的问题、瓶颈和延迟。…

SOPC之NiosⅡ系统(一)

1. 基础概念 1.1 CPU软核与硬核 简单来说 CPU硬核就是在FPGA上的一颗硬件结构固定并且用户不能对其结构进行任何更改、只能进行编程控制的芯片。 CPU软核则是FPGA上本来不存在这样的硬件结构&#xff0c;用户可根据硬件描述语言利用NIOS Ⅱ软核搭建出一个CPU。 1.2 SOC和S…

面试题大杂烩-记不住

1、分库分表图啥 分库是为了解决单库io连接数的瓶颈 分表是为了解决单表效率瓶颈 2、分表后如何limit分页 如果是根据xxx字段进行分表的话 那么shardingjdbc会根据字段进行笛卡尔积计算 去对应表里面执行sql到内存中计算&#xff0c;比如根据用户id进行hash算法进行查表&…

如何选择软文发布平台?软文发布平台的分类、特点及推广策略

在现今的市场竞争中&#xff0c;软文作为一种重要的推广方式&#xff0c;受到了越来越多企业的关注和运用。而软文发布平台&#xff0c;则是软文营销过程中不可或缺的一个环节&#xff0c;不同的软文发布平台会对软文的传播效果产生重要影响。本文将从软文发布平台的分类、特点…

淘宝APP商品详情源数据接口代码分享(API接口系列可高并发)

电商平台APP商品详情源数据接口代码分享如下&#xff1a; 商品数据&#xff1a;淘宝提供了商品的基本信息&#xff0c;包括商品名称、描述、规格、价格、销量、库存等信息。此外&#xff0c;也可以通过淘宝提供的API接口来获取商品的图片、评价、物流信息等详细数据。 公共参数…

深入理解DRF中的Mixin类

DRF官网地址&#xff1a; Home - Django REST framework Generic views - Django REST framework 一、Mixin类介绍 1.1 Mixin类介绍 Mixin类是一种常见的设计模式&#xff0c;在多个类之间共享功能或行为时非常有用。 一个Mixin类通常包含一组方法或属性&#xff0c;可以被…

unity 使用vrtk4的插件 打包htv vive VR客户端包,手柄不生效

背景&#xff1a; 目的&#xff1a;u3d使用vrtk开发pico应用(vrtk是为了到时候无缝衔接后续要买的htc vive pro 2) 先导入了tilia importer&#xff08;也就是vrtk4.0&#xff0c;根据教程模块化使用功能&#xff09;和pico官网下的“PICO Unity IntegrationSDK-214-20230302…

Leetcode-每日一题【138.复制带随机指针的链表】

题目 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点的值。新节…

手捏Java知识点

今天开始面向对象了 面向对象基础 类&#xff1a;由属性和行为组成属性&#xff1a;在类中通过成员变量来体现行为&#xff1a;在类中通过成员方法来体现public class 类名{//成员变量变量1的数据类型 变量1;变量2的数据类型 变量2;...//成员方法方法1;方法2;...}有几个重要的概…

百度iOS端长连接组件建设及应用实践

作者 | 百度消息中台团队 导读 在过去的十年里&#xff0c;移动端技术飞速发展&#xff0c;移动应用逐渐成为主要的便捷访问和使用互联网的方式&#xff0c;承接了越来越多的业务和功能&#xff0c;这也意味着对移动端和服务器之间的通信效率和稳定性提出了更高的要求。为了实现…

ellisys 过滤设备

1、通过关键字过滤 输入蓝牙名称关键字&#xff0c;比如下面输入的是BNCM Transmitter "*BNCM*" || Receiver "*BNCM*" 之后&#xff0c;所有与这个设备相关的信号都会抓取过滤出来 2、通过地址过滤 之后&#xff0c;所有与这个设备相关的信号都会抓取过…

港联证券-深夜,大利好!人民币暴拉超500点,全球股市大反攻

全球商场一夜狂欢。 美国通胀大降温&#xff0c;全球危险资产全线大涨。北京时间7月12日晚间&#xff0c;美国劳工统计局发布数据&#xff0c;美国6月CPI同比涨幅回落至3%&#xff0c;低于预期3.1%&#xff0c;接连第12个月下降&#xff0c;且为2021年3月以来最低。数据发布后&…

02 |「Android Studio 新建项目」

前言 新手入门安卓开发 文章目录 前言一、步骤一、步骤 New Project选择 Empty Activity1)Name:项目的名字; 2)Package name:项目的包名(项目的唯一标识); 3)Save location:项目的保存路径; 4)Language:

QT实现按钮开关Form窗体的效果

实现效果叙述如下&#xff1a; MainWindow中的按钮实现Form窗体的开关&#xff0c;Form窗体的关闭按钮禁用掉&#xff0c;只允许使用窗体按钮进行&#xff0c;且关闭MainWindow按钮时Form窗体随之关闭。 注意&#xff1a; 要想实现关闭MainWindow按钮时Form窗体随之关闭&#x…

【Android知识笔记】系统进程(一)

Android 系统进程有哪些 先来一个整体结构图从宏观上理解Android系统的进程结构布局: 这里我们简单总结一下: 系统的第一个进程其实是0号进程(又叫swapper进程/Idle进程) 0号进程fork出了1号进程(init进程)和2号进程(kthreadd进程) 1号进程是所有普通用户进程的祖先,2号进程…

Docker部署前后端分离项目

✅作者简介&#xff1a;大家好&#xff0c;我是Cisyam&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Cisyam-Shark的博客 &#x1f49e;当前专栏&#xff1a; 项目部署 ✨特色专栏&…

K8s生产环境常见问题处理、答疑(连载、不定期更新)

文章目录 K8s 常见问题处理、答疑[1] calico一直处于未就绪状态[2] 删除dashboard 一直卡在delete[3] k8s-dashboard 修改tocken-ttl避免频繁输入tocken[4] kubectl 快捷指令[5] 解决UTC时间问题[6] kubectl命令自动补全[7] kubectl exec进入pod[8] 使用hostpath解决容器UTC时间…