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

news2024/11/23 11:46:42

一、数组传参简介

在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 4

void 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 4

void 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 4

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 * 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.coloradmin.cn/o/1953584.html

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

相关文章

【网络】应用层协议(自定义协议)(序列和反序列化)

应用层协议&#xff08;自定义协议&#xff09;&#xff08;序列和反序列化&#xff09; 一、引言--应用层的使用二、应用层1、网络版本计算器&#xff08;1&#xff09;协议定制和序列反序列化&#xff08;2&#xff09;网络版计算器协议定制i、封装有效载荷&#xff08;默认上…

数据结构——单链表OJ题(上)

目录 一、移除链表元素 1.思路 2.注意 3.解题 二、反转链表 思路1&#xff1a;三指针翻转法 &#xff08;1&#xff09;注意 &#xff08;2&#xff09;解题 思路2&#xff1a;头插法 &#xff08;1&#xff09;注意 &#xff08;2&#xff09;解题 三、链表的中间结…

depcheck 前端依赖检查

介绍 depcheck 是一款用于检测项目中 未使用依赖项 的工具。 depcheck 通过扫描项目文件&#xff0c;帮助你找出未被引用的依赖&#xff0c;从而优化项目。 优势&#xff1a; 简单易用: 仅需几个简单的命令&#xff0c;就能够扫描并列出未使用的依赖项&#xff0c;让你快速了…

The Schematic workflow failed. See above.

在使用 ng new 新建Angular项目的时候会报一个错误&#xff1a;The Schematic workflow failed. See above. 解决办法&#xff1a; 只需要在后面加上 --skip-install 参数&#xff0c;就不会报错了。 ng new myapp --skip-install

打工人电脑里都需要的远程控制软件有哪些?这4款不能错过

不巧前几天台风&#xff0c;实在没办法到公司&#xff0c;但是项目还得继续&#xff0c;这时候远程控制电脑的技巧可谓是帮了我大忙了。不知道平常的你偶尔会不会也需要远程控制电脑的操作&#xff0c;如果有就继续看下去吧。 1.向日魁远程控制 直通车>>https://down.o…

AJAX-Promise 详解

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 前言 一、Promise基本概念 1.1 定义 1.2 状态 1.3 构造函数 二、Promise基本用法 2.1 then() 2.2 ca…

ThinkPHP一对一关联模型的运用(ORM)

一、序言 最近在写ThinkPHP关联模型的时候一些用法总忘&#xff0c;我就想通过写博客的方式复习和整理下一些用法。 具体版本&#xff1a; topthink/framework&#xff1a;6.1.4topthink/think-orm&#xff1a;2.0.61 二、实例应用 1、一对一关联 1.1、我先设计了两张表&#x…

根据题意写出完整的css,html和js代码【购物车模块页面及功能实现】

🏆本文收录于《CSDN问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!! 问题描述 根据题意写出完…

基于微信小程序+SpringBoot+Vue的社区超市管理系统(带1w+文档)

基于微信小程序SpringBootVue的社区超市管理系统(带1w文档) 基于微信小程序SpringBootVue的社区超市管理系统(带1w文档) 为了让商品信息的管理模式进行升级&#xff0c;也为了更好的维护商品信息&#xff0c;社区超市管理系统的开发运用就显得很有必要&#xff0c;因为它不仅可…

C# 植物大战僵尸

Winform 版本开发 高效率、流畅植物大战僵尸 git地址&#xff1a;冯腾飞/植物大战僵尸

go语言day19 使用git上传包文件到github Gin框架入门

git分布式版本控制系统_git切换head指针-CSDN博客 获取请求参数并和struct结构体绑定_哔哩哔哩_bilibili &#xff08;gin框架&#xff09; GO: 引入GIn框架_go 引入 gin-CSDN博客 使用git上传包文件 1&#xff09;创建一个github账户&#xff0c;进入Repositories个人仓…

我在百科荣创企业实践——简易函数信号发生器(6)

对于高职教师来说,必不可少的一个任务就是参加企业实践。这个暑假,本人也没闲着,报名参加了上海市电子信息类教师企业实践。7月8日到13日,有幸来到美丽的泉城济南,远离了上海的酷暑,走进了百科荣创科技发展有限公司。在这短短的一周时间里,我结合自己的教学经验和企业的…

buu做题(8)

[安洵杯 2019]easy_web 查看源代码可以发现一长串的base64编码 就是页面上的一张图片 回到原页面,url上面也有一些奇怪的参数 经过两次base64和一次hex 解密后得到 555.png 应该就是包含着页面上的这张图片 然后尝试将index.php 按照这样的方式编码, 看看能不能包含到 TmprMl…

后端解决跨域(Cross-Origin Resource Sharing)(三种方式)

注解CrossOrigin 控制层的类上或者方法上加注解CrossOrigin 实现接口并重写方法 Configuration public class CorsConfig implements WebMvcConfigurer {Overridepublic void addCorsMappings(CorsRegistry registry) {// 设置允许跨域的路径registry.addMapping("/**&qu…

算法通关:006_4二分查找:寻找数组中的峰值

文章目录 描述主要代码全部代码运行结果总结 二分法不一定只能用在有序数组中。 描述 leetcode&#xff1a;162 主要代码 //二分法查找峰值public static int findPeakElement(int[] arr){if (arr.length 1){//randomArray()不会出现arr null的情况return 0;}//先检查 0…

LabVIEW操作系列1

系列文章目录 我的记录&#xff1a; LabVIEW操作系列 文章目录 系列文章目录前言五、特殊用法5.1 取值范围表示5.2 对输入值取值范围进行限定5.3 控制多个While循环停止运行。5.4 获取按钮上的文本5.5 获取按钮上的文本【进阶】 六、使用步骤1.引入库2.读入数据 七、其余功能7.…

二叉树以及堆的实现

树 树的定义及概念 树是⼀种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09; 个有限结点组成⼀个具有层次关系的集合。把它叫做树是因为它看起来像⼀棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 有⼀个特殊的结点&#xff0c;称…

[Meachines] [Easy] Admirer Adminer远程Mysql反向+Python三方库函数劫持权限提升

信息收集 IP AddressOpening Ports10.10.10.187TCP:21,22,80 $ nmap -p- 10.10.10.187 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 22/tcp open ssh OpenSSH 7.4p1 Debian 10deb9u7 (protocol 2.0) | ssh-hostkey: | …

Redis:十大数据类型

键&#xff08;key&#xff09; 常用命令 1. 字符串&#xff08;String&#xff09; 1.1 基本命令 set key value 如下&#xff1a;设置kv键值对&#xff0c;存货时长为30秒 get key mset key value [key value ...]mget key [key ...] 同时设置或者获取多个键值对 getrange…

万物互联,触手可及“2024南京智慧城市,物联网,大数据展会”

在金秋送爽的11月&#xff0c;南京这座历史悠久而又充满活力的城市&#xff0c;即将迎来一场科技盛宴——2024南京智慧城市、物联网、大数据展会。这不仅是一场技术的集会&#xff0c;更是未来生活蓝图的预览&#xff0c;它汇聚了全球顶尖的科技企业、创新者及行业精英&#xf…