当前位置: 首页 > news >正文

C语言之指针高级--指针操作二维整型、字符型数组、函数指针

指针操作二维整型数组

回顾:在C语言中并不存在真正的二维数组,二维数组的本质 是一维数组的一维数组 ,比如定义一个二维数组 int a[2][3],本质上是 int [3] a[2],表示定义了一个数组类型(具体类型是int [3])的一维数组a[2],在学习指针操作二维数组时,要从其本质出发,否则很可能被绕晕。二维数组 也 符合数组的特点 ,即连续性,有序性,单一性 。

指针定义的语法是

基类型  *  变量名

那么应该需要确定一个基类型是什么的指针,来指向二维数组呢?

如果对二维数组本质比较清楚,那么a[2][3]中,a[0](即所谓的“第一行”)的类型是int [3]这种数组类型,由此可以推断 &a[0] 对应就是 int [3]*,只不过C语言中不允许这种写法,所以正确的写法是int (*) [3],即定义了一个数组类型的指针,就是数组指针。所以我们就可以定义一个这样类型的指针:

int (* p)[3] = a // 表示p指向了二维数组a,p的基类型相当于int [3]

*p <=> a[0] //相当于是内部的一维数祖名

(*p)[0]  <=>  *(*p+0)  <=>  **p    //  都表示a[0][0];

*(*(p+1)+1) <=> a[1][1]
*(*(p+i)+j) <=> a[i][j]

若a是二维数组,则a[i] 代表一维数组名,它只是一个地址,并不代表某一个元素的值,所以a 、a+i、a[i]、*(a+i)、*(a+i)+j、a[i] + j都是地址;而*(a[i] + j)和 *(* (a+i) + j) 是二维数组元素a[i] [j]的值。

注意:

 二维数组的操作
   从二维数组的本质 进行的 ,直接的访问操作也是 一维一维的展开  。

以下是简单使用实例,实现二维数组打印

#include <stdio.h>int main(void)
{int a[2][3] = {1,2,3,4,5,6};int (*p)[3] = a;int i = 0;int j = 0;for (i = 0;i < 2; ++i){for (j = 0; j < 3; ++j){//相当于printf("%d ",a[i][j]);printf("%d ",*(*(p+i) + j));}putchar('\n');}return 0;
}

指针操作二维字符型数组

二维字符型数组主要用来处理字符串,所以指针操作二维字符型数组,就是操作字符串。定义指针变量类似二维整型数组。

但是有两种方式可以给值:

方式1

char s[ ] = {"hello", "world"};

char (*p) [10] = s;  //表示p指向二维数组s

因为二维字符型数组主要操作字符串,所以我们是取一整个字符来进行操作的,所以用指针访问时,直接 *(p+i)就可以取到字符串,这是区别于二维字符型数组的地方。

 

方式2

在一维字符型数组中,我们可以 char * p = “hello”,表示定义了一个指针变量,直接指向了字符串,就是p存了字符串常量区中放hello的地址。那么对于多个存放字符串的地址,我们可以:

char * p1 = “hello”; 

char * p2 = “world”;

char * p3 = “China”;

......

C语言中用数组来批量处理数据,所以定义了一个数组:

char *  p[3] = {"hello", "world",“China”};

因为要存放地址这种类型,所以基类型是char *,也就是指针类型,数组p里面存放的是指针类型的变量----地址。

对于p[3]这个数组来说,里面存放的是各个字符串的地址,也就是存放着地址数据的数组,就是指针数组,注意区别于数组指针。

所以如果要定义一个指针来指向这个数组,可以写成:

char *  *  q = p; //q是二级指针

char *  代表基类型是指针类型

*   代表定义了一个指针类型的变量q,只是一个声明,无其它含义。

应用:

int main (int argc, const char * argv[ ]) 

在C语言和C++语言中,main 函数是程序的入口点,它可以接受两个参数: argc 和 argv[] 。这两个参数允许程序接收从命令行传递给程序的参数。

1. **argc**:这是一个整型变量,表示命令行中传递给程序的参数个数。它包括程序本身的名称,所以至少为1。例如,如果在命令行中运行 program arg1 arg2,argc 的值将是3。

2. **argv[]**:这是一个字符指针数组,每个元素指向一个以null结尾的字符串。argv[0]是程序本身的名称,argv[1]是第一个参数,argv[2]`是第二个参数,依此类推。argv数组的最后一个元素是一个空指针(即NULL或`nullptr),表示参数列表的结束。

### 用法示例:

假设有一个C程序,其main函数定义如下:


int main(int argc, char *argv[]) {
    // 程序代码
    return 0;
}
 

如果在命令行中运行这个程序并传递参数,如下


./myprogram arg1 arg2 arg3
 

那么在程序中,argc的值将是4,argv数组将包含以下元素:

argv[0] -> "./myprogram"
argv[1] -> "arg1"
argv[2] -> "arg2"
argv[3] -> "arg3"
argv[4] -> NULL

在程序中,可以使用这些参数来执行不同的操作,例如


int main(int argc, char *argv[]) {
    if (argc > 1) {
        printf("Number of arguments: %d\n", argc - 1);
        for (int i = 1; i < argc; i++) {
            printf("Argument %d: %s\n", i, argv[i]);
        }
    }
    return 0;
}

这个程序将打印出除了程序名称之外的参数个数,以及每个参数的值。

指针 + 函数


通过指针 的方式 来调用函数 


函数名 --- 代表函数的入口地址 

int add(int a,int b) // 求和函数  
                     // 函数名 - 代表函数的入口地址 
                     // 函数名对应的数据类型 
                     // int (int a,int b) //函数类型 
                     // 代表一类函数
                     // 返回值为int型
                     // 带有两个int型的形参变量 
                     
说明:
 1.可以定义一个函数类型的指针变量 
   来保存函数的入口地址 

int (*p)(int, int ) = add
 2.有了这个指针变量 
   通过指针变量 进行函数调用 

int ret = p(1,2)

一般用于实现函数回调(callback) 

回调函数 --- 通过函数指针调用函数 就是回调。c语言中,使用了函数指针实现回调。

以下是回调的简单实例:

 

#include <stdio.h>int add(int a,int b)
{return a + b;
}int sub(int a,int b)
{return a - b;
}
int mul(int a,int b)
{return a * b;
}
int div(int a,int b)
{return a / b;
}void processData(int a,int b,int(*p)(int,int))
{printf("result: %d\n",p(a,b));
}int main(int argc, const char *argv[])
{processData(1,2,add);processData(1,2,sub);processData(1,2,mul);processData(1,2,div);return 0;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • vscode远程开发
  • C++:std::memory_order_relaxed(宽松内存序)
  • [Vue3] 9 其它API
  • Elasticsearch 搜索高亮功能及示例
  • 谷粒商城实战笔记-179~183-商城业务-检索服务-SearchRequest和SearchResponse构建
  • js中的promise、async/await 用法,详解async、await 语法糖,js中的宏任务和微任务(保姆级教程二)
  • vscode的C/C++环境配置和调试技巧
  • 基于Transformer机制的AI现阶段可能已达峰值
  • xss复现
  • WPF打印控件内容
  • 嵌入式linux系统镜像制作day2
  • 软件工程概述(上)
  • 关注自闭症儿童:走进他们孤独的世界
  • CentOS7安装流程步骤详细教程
  • 数学建模预测类—【多元线性回归】
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • eclipse(luna)创建web工程
  • JavaScript DOM 10 - 滚动
  • Javascript基础之Array数组API
  • JSDuck 与 AngularJS 融合技巧
  • JS题目及答案整理
  • seaborn 安装成功 + ImportError: DLL load failed: 找不到指定的模块 问题解决
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • spring cloud gateway 源码解析(4)跨域问题处理
  • Yeoman_Bower_Grunt
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 移动端 h5开发相关内容总结(三)
  • 走向全栈之MongoDB的使用
  • ionic入门之数据绑定显示-1
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • ​Spring Boot 分片上传文件
  • # centos7下FFmpeg环境部署记录
  • # 利刃出鞘_Tomcat 核心原理解析(八)-- Tomcat 集群
  • # 消息中间件 RocketMQ 高级功能和源码分析(七)
  • #mysql 8.0 踩坑日记
  • #Spring-boot高级
  • #每日一题合集#牛客JZ23-JZ33
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • (9)STL算法之逆转旋转
  • (LLM) 很笨
  • (pycharm)安装python库函数Matplotlib步骤
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (七)Java对象在Hibernate持久化层的状态
  • (三)Kafka离线安装 - ZooKeeper开机自启
  • (一)Java算法:二分查找
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • .net framework 4.8 开发windows系统服务
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • .net 程序发生了一个不可捕获的异常
  • .NET 使用 ILRepack 合并多个程序集(替代 ILMerge),避免引入额外的依赖