c语言函数形参为数组时退化为指针的说明和注意事项

目录

数组做函数形参时,会退化为指针

数组做函数形参时,需要再指定一个长度参数

数组做函数形参时,实参也可以传入指针,但要保持类型一致

数组做函数形参时,实参的成员地址不能为空

例子1:指针数组做函数形参

例子2:二维数组做形参


数组做函数形参时,会退化为指针

在C语言中,当数组作为函数的参数时,它会退化为指向的第一个元素的指针。这是因为C语言不支持数组类型的大小,只能通过指针来操作数组。

具体来说:

  • 比如当形参为 char *data[]  时,会退化为 char ** ,也就是指向字符指针的指针,也就是二级指针。
  • 比如形参为 char data[] 时,会退化为 char * ,也就是指向字符指针,也就是一级指针。
  • 比如形参为 char data[][10] 这种不定长的二维数组时,会退化为 char (*)[10] ,也就是指向长度为10的字符数组的指针,也就是数组指针。

这种特性使得在函数内部对数组的修改能够影响到函数外部的数组,因为函数内部实际上是通过指针来操作这些数组的。

退化为指针验证说明

#include <stdio.h>  
#include <string.h>
  
int test(int data[], int size) 
{  
    printf("%d\n", sizeof(data)); // 返回指针占用内存大小
    // 在32位系统中,一个指针通常是4个字节。在64位系统中,一个指针通常是8个字节。

    return 0;  
}  
  
int main() 
{  
    int name[10] = {'h', 'e'};
    printf("%d\n", sizeof(name)); // 将会打印40,因为是得到整个数组占用的内存大小

    test(name, 10);

    return 0;  
}

数组做函数形参时,需要再指定一个长度参数

为什么在c语言中,数组做函数形参时,需要再设计一个长度参数?

因为在C语言中,当数组作为函数的形参时,实际上传递的是数组的首元素的地址,而不是整个数组本身。

由于数组名在表达式中会被转换成指向其首元素的指针,因此在函数参数中声明为数组类型的参数,实际上会被编译器处理为指向数组首元素的指针。这意味着函数内部无法直接知道数组的大小(即元素的数量),因为指针类型不包含数组大小的信息。

这使得在函数内部遍历或操作数组时,需要一种方式来明确数组的长度,以避免越界访问或操作不完整的数组。

尝试在函数内部计算数组的长度将会出错,示例如下:

#include <stdio.h>  
   
int sumArray(int arr[]) 
{
    int len = sizeof(arr)/sizeof(arr[0]);  
    printf("len=%d\n", len);  // 1

    int sum = 0;  
    for(int i = 0; i < len; i++) {  
        sum += arr[i];  
    }  
    return sum;  
}  
  
int main() 
{  
    int myArray[] = {10, 20, 30, 40, 50};  
    printf("Sum of array elements: %d\n", sumArray(myArray));  
    return 0;  
}

运行后结果len=1,求和为10?而我们预取len=5,求和150。主要在与len的计算出错了,因为arr退化为了指针,所以sizeof(arr)的值将为8,sizeof(arr[0])也是8,所以计算长度出来为1。而实际上sizeof(myArray)的值应当为40。

warning: 'sizeof' on array function parameter 'arr' will return size of 'int *' 

正确设计函数应当如下:

#include <stdio.h>  
  
// 需要额外的长度参数来指明数组的大小  
int sumArray(int arr[], int size) 
{  
    int sum = 0;  
    for(int i = 0; i < size; i++) {  
        sum += arr[i];  
    }  
    return sum;  
}  
  
int main() 
{  
    int myArray[] = {10, 20, 30, 40, 50};  
    int size = sizeof(myArray) / sizeof(myArray[0]); // 计算数组的长度  
    printf("Sum of array elements: %d\n", sumArray(myArray, size));  
    return 0;  
}

数组做函数形参时,实参也可以传入指针,但要保持类型一致

  • 比如函数形参为int arr[],实参可以为char *
  • 比如形参为 char *data[] ,实参可以为 char **
  • 比如形参为 char data[][10],实参可以为char (*)[10]
#include <stdio.h>  
  
void printStrings(char *strings[], int size) 
{  
    for (int i = 0; i < size; i++) {  
        printf("%s\n", strings[i]);  
    }  
}  
  
int main() 
{  
    // 定义一个字符串数组  
    char *strArray[] = {"Hello", "World", "from", "C"};  
    int size = sizeof(strArray) / sizeof(strArray[0]);  
  
    // 你可以直接传递 strArray 给 printStrings
    // 因为 strArray 的类型与 printStrings 的参数兼容  
    printStrings(strArray, size);  
  
    // 但如果你想通过一个 char ** 变量来做这件事,你可以这样做:  
    char **ptrToArray = strArray; 
    // ptrToArray 现在指向 strArray 的第一个元素(即第一个字符串的地址)  
  
    // 注意:此时传递 ptrToArray 给 printStrings 仍然有效,  
    // 因为 ptrToArray 和 strArray 指向的是同一个内存位置(即第一个字符串的地址)  
    // 但重要的是要知道,函数内部不知道数组的大小,所以你需要额外传递 size  
    printStrings(ptrToArray, size);  
  
    return 0;  
}

数组做函数形参时,实参的成员地址不能为空

例子1:指针数组做函数形参

比如函数形参为 char *data[],函数实参需要传递一个指针数组,要求指针数组的成员即指针不能为空。在函数内部访问或者修改一个空指针将会出错。

比如来看一个例子,先看下面的代码,data为传出参数,函数作用是在将result的全部成员初始化为"Tom",请问是否会出问题?

#include <stdio.h>  
#include <string.h>
  
int InitName(char *data[], int size) 
{  
    for (int i = 0; i < size; i++) {
        strcpy(data[i], "Tom");
    }  

    return 0;  
}  
  
int main() 
{  
    char *result[10];
    InitName(result, 10);

    return 0;  
}

答案是会的,因为result这个指针数组,当你声明 char *result[10]; 后,如果没有显式地对 result 数组中的每个元素(即每个指针)进行初始化,那么这些元素的值是未定义的。这意味着它们可能包含任何值,包括看似有效的内存地址(在这种情况下,它们就是野指针),或者0(在这种情况下,它们可以被视为空指针,尽管这通常是通过显式初始化来实现的)。

对空指针调用strcpy一定会出错。

为了修复这个问题,需要在 main 函数中为每个指针分配足够的内存来存储字符串 "Tom"(包括终止的空字符 '\0')。这可以通过使用 malloc 函数来实现。另外,应该检查 malloc 的返回值以确保内存分配成功。

#include <stdio.h>  
#include <stdlib.h>
#include <string.h>  

#define MAX_STR_LEN 100
int InitName(char *data[], int size)   
{    
    for (int i = 0; i < size; i++) {  
        if (data[i] != NULL) { 
            strcpy(data[i], "Tom");  
        } else {  
            printf("data[%d] is NULL, skipping...\n", i);  
        }  
    }    
    return 0;    
}    
  
int main()   
{    
    char *result[10];  
    for (int i = 0; i < 10; i++) {  
        result[i] = malloc(MAX_STR_LEN + 1);  
        if (result[i] == NULL) {  
            perror("malloc failed");   
            return 1; 
        }  
    }  
  
    InitName(result, 10);  
  
    for (int i = 0; i < 10; i++) {  
        free(result[i]);  
    }  
  
    return 0;    
}

例子2:二维数组做形参

请看以下例子是否会出问题?

#include <stdio.h>  
#include <string.h>
  
int InitName(char data[][20], int size) 
{  
    for (int i = 0; i < size; i++) {
        strcpy(data[i], "Tom");
    }  

    return 0;  
}  
  
int main() 
{  
    char result[10][20];
    InitName(result, 10);

    // 打印结果以验证  
    for (int i = 0; i < 10; i++) {  
        printf("%s\n", result[i]);  
    }  

    return 0;  
}

答案是不会的,这是因为result做为二维数组,其成员全部内存已在栈上分配好,且调用InitName函数时,其生命周期仍然存在,所以不会出错。


end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值