【C语言】深入探讨数组传参

一、数组传参简介

在C语言中,数组传参是一个常见的操作,尤其是在处理大量数据或需要多次访问相同数据集时。理解如何传递数组以及这些方法之间的差异是编写高效和安全代码的关键。在这篇博客中,我们将详细讨论C语言中数组传参的几种常见方法,并探讨它们的优缺点。

二、形参和实参 

1.基本概念

在C语言中,数组作为函数的形参和实参时,会涉及到一些独特的语法和行为。了解这些差异和概念对编写正确和高效的C语言程序非常重要。我们将深入探讨C语言中的数组形参和实参,包括它们的定义、使用方式,以及在实际开发中的注意事项。数组作为形参和实参时有一些特殊的规则,这些规则源自C语言数组的本质,即数组名在大多数情况下被视为指向数组第一个元素的指针。

1.1 数组形参

当数组作为函数的形参时,实际上传递的是一个指针,而不是整个数组。换句话说,数组的形参声明中虽然可以写成数组的形式,但本质上是指针类型。

形参的几种表示方式:

void foo(int arr[], int size);    // 使用数组表示法
void foo(int arr[10], int size);  // 可以带有长度(会被忽略)
void foo(int *arr, int size);     // 使用指针表示法

在上述三种声明方式中,形参arr都是指向int类型的指针。即使在第一种和第二种方式中,看似是在声明一个数组,但实际上它们都被视为指向int类型的指针。

1.2 数组实参

当调用函数并传递数组时,传递的实际上是数组的地址,即指向数组第一个元素的指针。

//示例代码
#include <stdio.h>void printArray(int arr[], int size) {for(int i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");
}int main() {int arr[] = {1, 2, 3, 4, 5};int size = sizeof(arr) / sizeof(arr[0]);printArray(arr, size);  // 传递数组的起始地址return 0;
}在main函数中,printArray(arr, size);这行代码中
arr作为实参传递给printArray函数,实际上是传递了数组的起始地址。

2.形参和实参之间的关键差异

2.1 数组大小信息的丢失

当数组作为形参传递时,数组的大小信息会丢失。这是因为形参接收到的只是一个指针,而不是整个数组。因此,无法直接通过形参得知数组的长度。

//示例
void printArray(int arr[]) {printf("Size of arr in function: %zu\n", sizeof(arr));
}int main() {int arr[] = {1, 2, 3, 4, 5};printf("Size of arr in main: %zu\n", sizeof(arr));printArray(arr);return 0;
}

以vs为例:

解释:在main函数中,sizeof(arr)给出的是整个数组的大小(假设int为4字节,数组有5个元素,所以是20字节)。但在printArray函数中,sizeof(arr)返回的是指针的大小(假设为8字节),这表明arr在函数内部实际上是一个指针。

1.2 使用const修饰符

如果函数不需要修改数组的内容,可以使用const修饰符来保护数组数据。这不仅可以避免不必要的修改,还能提升代码的可读性和安全性。

//示例
void printArray(const int arr[], int size) {for(int i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");
}
使用const修饰符后,arr指向的数组内容在函数内部将不可修改,如果尝试修改编译器会报错。

3.数组作为实参和形参的实际使用建议

  • 传递数组大小:由于数组的大小信息在传递时丢失,通常需要额外传递一个参数来指定数组的大小,以避免越界访问。
  • 使用const修饰符:如果数组内容不应被修改,建议使用const修饰符来保护数据。
  • 使用结构体包装数组:在某些情况下,可以使用结构体来包含数组和它的大小,这样不仅可以传递数组,还可以保留数组的大小信息。
//示例
#include <stdio.h>typedef struct {int *arr;int size;
} ArrayWrapper;void printArray(ArrayWrapper wrapper) {for(int i = 0; i < wrapper.size; i++) {printf("%d ", wrapper.arr[i]);}printf("\n");
}int main() {int arr[] = {1, 2, 3, 4, 5};ArrayWrapper wrapper = {arr, 5};printArray(wrapper);return 0;
}
这种方式不仅可以传递数组,还能确保大小信息的传递,避免手动传递多个参数。

4.小结

在C语言中,理解数组作为形参和实参时的行为和限制是非常重要的。这不仅影响到代码的正确性和安全性,还影响到性能。通过正确地传递数组和使用合适的修饰符,可以编写出更高效、更安全的代码。在实际开发中,常常需要根据具体的场景选择合适的数组传参方式,并注意数组边界和内存安全问题。

三、一维数组传参

C语言中的一维数组是一种非常常用的数据结构,用于存储相同类型的多个元素。在编写函数时,我们经常需要将数组作为参数传递给函数。

1.基本概念

在C语言中,一维数组是一组连续的内存块,每个内存块存储相同类型的数据。数组中的元素可以通过数组名和索引来访问,如arr[0]表示数组arr的第一个元素。数组的索引从0开始。

int arr[5] = {1, 2, 3, 4, 5};
上面的代码定义了一个包含5个整数的数组arr。

2.数组传参的方式

在C语言中,将一维数组作为参数传递给函数有几种不同的方法。下面将介绍这些方法并说明它们的使用场景。

2.1 通过指针传递数组

最常见的方式是通过指针来传递数组。由于数组名本身就代表数组的首地址(即指向第一个元素的指针),因此可以直接将数组名作为参数传递给函数。

#include <stdio.h>void printArray(int *arr, int size) {for (int i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");
}int main() {int arr[5] = {1, 2, 3, 4, 5};printArray(arr, 5);return 0;
}
在printArray函数中,arr是一个指针,指向数组的第一个元素。
函数通过指针和数组的大小size来访问数组的所有元素。

2.2 使用数组指针传递数组

另一个方法是使用数组指针来传递数组。这种方法在函数参数中明确指定了数组的大小。

#include <stdio.h>void printArray(int arr[5]) {for (int i = 0; i < 5; i++) {printf("%d ", arr[i]);}printf("\n");
}int main() {int arr[5] = {1, 2, 3, 4, 5};printArray(arr);return 0;
}
在这种情况下,arr是一个指向包含5个整数的数组的指针。
注意,数组大小是函数的一部分,因此这种方法只能用于已知大小的数组。

2.3 使用数组作为参数传递

实际上,C语言中的数组传参和指针传参是等价的。当我们在函数参数中使用数组时,编译器会自动将其转换为指向数组首元素的指针。因此,下面的代码与前面的例子等效:

#include <stdio.h>void printArray(int arr[]) {for (int i = 0; i < 5; i++) {printf("%d ", arr[i]);}printf("\n");
}int main() {int arr[5] = {1, 2, 3, 4, 5};printArray(arr);return 0;
}
在这种方法中,arr实际上是一个指向整数的指针。
虽然没有指定数组的大小,但在函数实现中仍然需要知道数组的大小。

3.数组传参的注意事项

  • 数组大小问题: 当将数组作为参数传递给函数时,必须确保函数能够访问数组的所有元素。这通常通过传递数组的大小(如上例中的size参数)来实现。
  • 避免数组越界: 访问数组元素时,要确保索引在有效范围内,避免访问超出数组边界的内存。
  • 常量数组和指针: 如果数组不应该在函数中被修改,可以使用const关键字将数组或指针声明为常量,以防止意外修改。
void printArray(const int *arr, int size) {// 函数体
}
使用const关键字可以提高代码的安全性和可读性。

4.小结

在C语言中传递一维数组有多种方式,包括通过指针、使用数组指针以及直接使用数组作为参数。每种方式都有其适用的场景和注意事项。理解这些传参方式及其背后的机制有助于编写高效且安全的代码。在实际开发中,根据具体需求选择合适的传参方式是非常重要的。

5.更多 

四、二维数组传参

在C语言中,二维数组是一个非常有用的数据结构,用于存储和操作矩形矩阵形式的数据。在编写函数时,我们有时需要将二维数组作为参数传递给函数。

1. 直接传递数组

这是最常见也是最简单的一种方式。我们可以直接将二维数组传递给函数,函数接收一个对应类型的二维数组作为参数。

#include <stdio.h>#define ROWS 3
#define COLS 4void printArray(int arr[ROWS][COLS], int rows, int cols) {for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {printf("%d ", arr[i][j]);}printf("\n");}
}int main() {int array[ROWS][COLS] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}};printArray(array, ROWS, COLS);return 0;
}
说明
在上述代码中,printArray函数接收了一个二维数组arr,以及数组的行数和列数。
需要注意的是,在函数定义中,二维数组的第二维(列数)必须是固定的。
这是因为C语言需要知道每一行的大小,以便正确地访问数组元素。

2. 使用指针

在C语言中,数组名本质上是指向数组第一个元素的指针。因此,我们可以使用指针来传递二维数组。这里的关键点在于理解二维数组在内存中的存储方式。

#include <stdio.h>#define ROWS 3
#define COLS 4void printArray(int (*arr)[COLS], int rows) {for (int i = 0; i < rows; i++) {for (int j = 0; j < COLS; j++) {printf("%d ", arr[i][j]);}printf("\n");}
}int main() {int array[ROWS][COLS] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}};printArray(array, ROWS);return 0;
}
说明
在这个例子中,我们使用了指向数组的指针int (*arr)[COLS]来接收二维数组。
这个表示法告诉编译器,arr是一个指向含有COLS个整数的数组的指针。
这样,我们就可以在函数内部使用指针访问二维数组的元素。

3. 使用双重指针(指针的指针)

虽然我们可以使用双重指针来传递二维数组,但这在实际中并不常见,因为它不能直接处理真正的二维数组,而是更适用于传递动态分配的数组。

#include <stdio.h>
#include <stdlib.h>void printArray(int **arr, int rows, int cols) {for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {printf("%d ", arr[i][j]);}printf("\n");}
}int main() {int rows = 3, cols = 4;int **array = (int **)malloc(rows * sizeof(int *));for (int i = 0; i < rows; i++) {array[i] = (int *)malloc(cols * sizeof(int));}// Initialize the array初始化数组int count = 1;for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {array[i][j] = count;}count += cols;  // 每一行递增 cols}printArray(array, rows, cols);// Free the allocated memory释放分配的内存for (int i = 0; i < rows; i++) {free(array[i]);}free(array);return 0;
}
说明
在这个例子中,我们动态分配了一个二维数组,并使用双重指针int **arr来传递它。
对于每一行,我们分配了一个指向整数的指针,从而形成了一个指针的数组。
这种方法在处理动态二维数组时非常有用,但对于固定大小的静态数组,这种方法显得过于复杂。

4. 使用单个指针传递一维表示的二维数组

如果我们对内存使用有特殊要求,或者希望更好地控制内存布局,可以将二维数组展平成一维数组,然后使用一个指针来传递。

#include <stdio.h>#define ROWS 3
#define COLS 4void printArray(int *arr, int rows, int cols) {for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {printf("%d ", arr[i * cols + j]);}printf("\n");}
}int main() {int array[ROWS * COLS] = {1, 2, 3, 4,5, 6, 7, 8,9, 10, 11, 12};printArray(array, ROWS, COLS);return 0;
}
说明
在这个例子中,我们将一个二维数组展平为一个一维数组,并使用单个指针来传递。
访问元素时,我们使用公式i * cols + j来计算线性索引。
这种方法在处理大数据时可以带来一定的性能优势,因为它避免了多重指针间接访问的开销。

5.小结

在C语言中,传递二维数组有多种方法,每种方法都有其独特的适用场景和优缺点。直接传递数组和使用指针是最常见的方式,它们适用于固定大小的静态数组。双重指针和展平为一维数组的方法更适用于动态数组或特定的内存布局需求。

在实际应用中,选择哪种方法主要取决于具体的需求,如是否需要动态分配内存、数组的大小是否固定、以及性能和可读性等因素。希望这篇博客能够帮助你更好地理解和使用C语言中的二维数组传参方式。

6.更多 

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

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

相关文章

docker 构建 qemu

docker 编译 安装 qemu 安装依赖软件 apt-get install -y zlib1g-dev pkg-config libglib2.0-dev libmount-dev libpixman-1-dev apt-get install -y zlib1g-dev pkg-config libglib2.0-dev libmount-dev libpixman-1-devsudo apt-get install ptyhon3.7 sudo apt-get insta…

rhce THE homework of first

ssh远程免密登录成功 下载httpd和nginx 关闭防火墙 查看selinux的状态 为服务器配置ip 填充网站的内容 添加服务器配置

Python爬虫入门01:在Chrome浏览器轻松抓包

文章目录 爬虫基本概念爬虫定义爬虫工作原理爬虫流程爬虫类型爬虫面临的挑战 使用Chrome浏览器抓包查看网页HTML代码查看HTTP请求请求头&#xff08;Request Header&#xff09;服务器响应抓包的意义 爬虫基本概念 爬虫定义 爬虫&#xff08;Web Crawler 或 Spider&#xff0…

MongoDB教程(二十三):关于MongoDB自增机制

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、MongoD…

数字孪生在奥运会上的应用

数字孪生是一种精确的虚拟空间表示&#xff0c;能够实时模拟场馆内的各种变化或场景。国际奥委会正在确定高价值的应用案例和技术要求&#xff0c;将应用案例分为六个主要领域&#xff1a;场馆规划、利益相关者支持与参与、操作准备、粉丝体验、遗产和运营效率。每个案例将基于…

ABC363:D - Palindromic Number(回文,构造)

问题陈述 如果一个非负整数 &#x1d44b;X 的十进制表示(不含前导零)是一个回文数&#xff0c;那么这个非负整数 &#x1d44b;X 就叫做回文数。 例如&#xff0c; 363363 、 1234432112344321 和 00 都是回文数。 求 &#x1d441;N /th最小的回文数。 限制因素 1≤&…

软件环境安装-通过Docker安装Mysql

通过Docker安装Mysql 一、拉取镜像二、启动三、测试mysql 一、拉取镜像 docker pull mysql二、启动 docker run --name docker_mysql --restartalways -v /home/project/mysql:/var/lib/mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORDroot -d mysql三、测试mysql 进入容器&…

虚拟dom和diff算法

React的虚拟DOM&#xff08;Virtual DOM&#xff09;和Diff算法是React框架中非常重要的两个概念&#xff0c;它们共同协作以实现高效的UI更新。以下是对React虚拟DOM和Diff算法的详细解析&#xff1a; React虚拟DOM 定义&#xff1a; 虚拟DOM是React中的一个核心概念&#…

【前端 13】Vue快速入门

Vue快速入门 在现代Web开发中&#xff0c;尽管通过HTML、CSS和JavaScript我们能够构建出美观且功能丰富的页面&#xff0c;但随着项目规模的增大&#xff0c;这种传统的开发方式在效率上逐渐显得力不从心。为了提高开发效率&#xff0c;前端开发者们引入了多种框架和库&#x…

Greenplum数据库中常见的连接错误及解决方法

一、连接超时报错 报错信息&#xff1a;FATAL: connection terminated due to connection timeout解决方法&#xff1a;增加连接超时时间&#xff0c;可以修改Greenplum数据库配置文件中的连接超时设置。适当增加连接超时时间可避免连接因超时而中断。 二、连接被拒绝报错 报…

MySQL环境的配置文件json

突然了解到&#xff0c;使用json文件去进行环境的配置&#xff0c;这样修改参数的时候就只需要去改json文件中的内容&#xff0c;不需要去修改代码中的内容&#xff0c;其他人的MySQL和我的MySQL也不同&#xff0c;这时其他人只需要修改json文件中的内容&#xff0c;清晰明了&a…

Springboot-websocket实现及底层原理

引入依赖 Spring Boot 中的 WebSocket 依赖于 Spring WebFlux 模块&#xff0c;使用了 Reactor Netty 库来实现底层的 WebSocket 通信。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</ar…

基于STC8H系列单片机的定时器系统

基于STC8H系列单片机的定时器系统 STC8H4K64TL单片机介绍STC8H4K64TL单片机管脚图&#xff08;48个引脚&#xff09;STC8H4K64TL单片机串口仿真与串口通信STC8H4K64TL单片机管脚图&#xff08;32个引脚&#xff09;STC8H4K64TL单片机管脚图&#xff08;20个引脚&#xff09;STC…

Apollo使用(3):分布式docker部署

Apollo 1.7.0版本开始会默认上传Docker镜像到Docker Hub&#xff0c;可以按照如下步骤获取 一、获取镜像 1、Apollo Config Service 获取镜像 docker pull apolloconfig/apollo-configservice:${version} 我事先下载过该镜像&#xff0c;所以跳过该步骤。 2、Apollo Admin S…

计算机网络-配置路由器ACL(访问控制列表)

配置访问控制列表ACL 拓扑结构 拓扑结构如下&#xff1a; 要配置一个ACL&#xff0c;禁止PC0访问PC3&#xff0c;禁止PC4访问PC0&#xff0c;其它正常。 配置Router0 配置接口IP地址&#xff1a; interface fastethernet 0/0 ip address 192.168.1.1 255.255.255.0 no shu…

VUE 基础(一)

(直接在vscode上运行就可以&#xff0c;建一个html文件) 1 el的使用 Vue会管理el选项命中的元素及其内部的后代元素 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content…

第三部分 图论 - 第2章 最小生成树

定义 首先我们先了解下生成树的定义&#xff1a; 无向图中&#xff0c;一个连通图的最小连通子图称作该图的生成树&#xff08;不能带环&#xff0c;保持连通&#xff0c;但边要尽可能的少&#xff09;。 有n个顶点的连通图的生成树有n个顶点和n-1条边。 那么最小生成树和生成…

自动驾驶 机器人 slam 定位

自动驾驶机器人中的SLAM&#xff08;Simultaneous Localization and Mapping&#xff0c;同时定位与地图构建&#xff09;技术是一种关键技术&#xff0c;旨在使机器人在未知环境中自主导航。SLAM技术涉及机器人在移动过程中通过传感器&#xff08;如激光雷达、摄像头、IMU等&a…

决策树 和 集成学习、随机森林

决策树是非参数学习算法&#xff0c;可以解决分类问题&#xff0c;天然可以解决多分类问题&#xff08;不同于逻辑回归或者SVM&#xff0c;需要通过OVR&#xff0c;OVO的方法&#xff09;&#xff0c;也可以解决回归问题&#xff0c;甚至是多输出任务&#xff0c;并且决策树有非…

国内NAT服务器docker方式搭建rustdesk服务

前言 如果遇到10054,就不要设置id服务器!!! 由于遇到大带宽,但是又贵,所以就NAT的啦,但是只有ipv4共享和一个ipv6,带宽50MB(活动免费会升130MB~) https://bigchick.xyz/aff.php?aff322 月付-5 循环 &#xff1a;CM-CQ-Monthly-5 年付-60循环&#xff1a;CM-CQ-Annually-60官方…