【问题标题】:problem in returning array address from function in C从C中的函数返回数组地址的问题
【发布时间】:2020-08-09 19:23:48
【问题描述】:

出现“冲突类型”错误,我该怎么办,并且更喜欢一个好的替代方案。主要问题是从函数返回一个数组

#include<stdio.h>
int* freqofchar(char);
int main()
{

    char str[100];
    printf("Enter a sentence below :\n");
    gets(str);
    int* p = freqofchar(str);
    for(int i=0;i<128;i++){
        if(*p>0){
            printf("%c occurred %d times\n",(char) i , *p++);
        }
    }
    return 0;
}
int* freqofchar(char str[]){
    int freq[128] = {0};
    for(int i = 0;str[i] != '\0';i++){
        freq[ str[i] ]++;
    }
    return freq;
}

【问题讨论】:

  • 你不能返回一个静态分配的数组,因为一旦函数返回它就会被释放(更一般地说,在它的声明范围之外被释放)。相反,在函数外部分配这个数组,并将它传递给 then 函数(它将用数据填充它)。
  • 所以重写函数
  • 这是订单???
  • 下一次,使用所有警告和调试信息编译您的代码。使用GCC 尝试使用gcc -Wall -Wextra -g 进行编译。还可以考虑使用Clang static analyzer 和/或Frama-C。阅读C dynamic memory allocationflexible array members。另见this
  • 顺便说一句,gets 已过时且使用起来总是很危险。了解undefined behavior in Cbuffer overflow

标签: c pointers scope c-strings function-definition


【解决方案1】:

函数参数在函数声明中声明为类型char

int* freqofchar(char);
                ^^^^^

但在函数声明中,同样是它的定义

int* freqofchar(char str[]){
                ^^^^^^^^^ 

参数被声明为具有char [] 类型(编译器将其调整为char * 类型)。

这个错字是编译器消息的原因。

但在任何情况下,函数至少应该被声明为

unsigned int * freqofchar( const char [] );

将频率定义为有符号整数类型是没有意义的,并且参数应具有限定符const,因为传递的字符串在函数中没有被更改。

程序具有未定义的行为,因为该函数返回指向具有自动存储持续时间的本地数组的指针,该数组在退出函数后将不再存在。

int* freqofchar(char str[]){
    int freq[128] = {0};
    //...
    return freq;
}

您应该动态分配数组或使用存储说明符static 声明它。在最后一种情况下,您需要在每次调用函数时将数组元素重置为零。

函数gets 是一个不安全的函数,C 标准不再支持。而是使用标准 C 函数fgets

这是一个演示程序。

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

unsigned int* freqofchar( const char *);

int main( void )
{
    enum { N = 100 };
    char str[N];
    str[0] = '\0';

    printf( "Enter a sentence below : " );

    fgets( str, N, stdin );

    // remove the appended new line character '\n'
    str[ strcspn( str, "\n" ) ] = '\0';

    unsigned int *p = freqofchar( str );

    for ( size_t i = 0; i < 128; i++ )
    {
        if ( p[i] )
        {
            printf( "'%c' occurred %u times\n", ( char )i , p[i] );
        }
    }

    free( p );

    return 0;
}

unsigned int * freqofchar( const char str[] )
{
    enum { N = 128 };

    unsigned int *freq = calloc( N, sizeof( unsigned int ) );

    while ( *str ) ++freq[ ( size_t )*str++ ];

    return freq;
}

它的输出可能看起来像

Enter a sentence below : Hello World!
' ' occurred 1 times
'!' occurred 1 times
'H' occurred 1 times
'W' occurred 1 times
'd' occurred 1 times
'e' occurred 1 times
'l' occurred 3 times
'o' occurred 2 times
'r' occurred 1 times

如果用静态存储说明符定义函数,那么它的定义可以如下所示。

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

unsigned int* freqofchar( const char *);

int main( void )
{
    enum { N = 100 };
    char str[N];
    str[0] = '\0';

    printf( "Enter a sentence below : " );

    fgets( str, N, stdin );

    // remove the appended new line character '\n'
    str[ strcspn( str, "\n" ) ] = '\0';

    unsigned int *p = freqofchar( str );

    for ( size_t i = 0; i < 128; i++ )
    {
        if ( p[i] )
        {
            printf( "'%c' occurred %u times\n", ( char )i , p[i] );
        }
    }

    return 0;
}

unsigned int * freqofchar( const char str[] )
{
    enum { N = 128 };

    static unsigned int freq[N];

    memset( freq, 0, N * sizeof( unsigned int ) );

    while ( *str ) ++freq[ ( size_t )*str++ ];

    return freq;
}

【讨论】:

    【解决方案2】:

    您看到的错误是由于函数原型与其实际定义不匹配。

    但是,您也有其他问题:

    • gets 函数已从 C 标准中删除(因为),这是有充分理由的,在任何情况下都不应使用。您可以改用fgets 来读取输入。但如果fgets 读入,则需要删除换行符。

    • 您正在返回一个指向局部变量的指针,一旦函数freqofchar 返回,其生命周期就无效,这是未定义的行为。您可以改为传递另一个参数。通常您可能需要考虑动态分配(例如通过malloc),但在这种情况下 - 您只需要一个小数组 - 主函数的本地数组,它具有automatic storage duration,因此它的生命周期在main() 并且它可以安全地传递给 freqofchar 函数,而不会因为对象(freq 是此处引用的对象)的生命周期在freqofchar() 中使用时仍然有效 -更适合这里。

    以下是固定解决方案的外观:

    #include<stdio.h>
    #include<string.h>
    
    void freqofchar(char*, int*);
    
    int main()
    {
        char str[100] = {0};
        int freq[256] = {0};
    
        printf("Enter a sentence below :\n");
        fgets(str, sizeof str, stdin);
    
        /* Remove the newline if present. */
        char *p = strchr(str, '\n');
        if (p) *p = '\0';
    
        freqofchar(str, freq);
    
        for(size_t i = 0;i < sizeof freq; i++) {
            if(freq[i]) {
                printf("%c occurred %d times\n", i, freq[i]);
            }
        }
        return 0;
    }
    
    void freqofchar(char str[], int freq[])
    {
        for(int i = 0;str[i] != '\0';i++) {
            freq[ str[i] ]++;
        }
    }
    

    【讨论】:

    • str 可以包含字符 > 128...,所以int freq[256] 会更合适。
    • @snr-ReinstateMonica 这与长度无关,而与用户输入 ascii 值大于 12 的字符的可能性有关,例如 à(这在非英语系统上经常发生)。
    • 我在之前的评论中“大于128
    • IMO 这个答案没有显示如何从函数返回“新鲜”对象。 IMO 有点过时了
    • 变量是否是本地的不是正确使用的概念。使用static 在函数内部定义的数组将是本地的,但可以返回。正确的概念是数组由 C 实现自动管理(具有自动存储持续时间),并且它的生命周期在函数返回时结束。
    【解决方案3】:
    1. 您犯了 C 编程中最常见的错误之一。您返回指向函数返回后不存在的对象的指针。

    可能的解决方案

    一个。使用动态分配的内存

    int* freqofchar(char *str)
    {
        int *freq = malloc(128 * sizeof(*freq)); // or if you want to zero it calloc
    
        /* ... */    
        return freq;
    }
    

    但您需要在不需要时释放分配的内存。

    b.使用静态数组或全局数组

    int* freqofchar(char *str)
    {
        static freq[128];
    
        /* ... */    
        return freq;
    }
    

    static freq[128];
    
    int* freqofchar(char *str)
    {
    
        /* ... */    
        return freq;
    }
    

    此解决方案的缺点:函数不可重入,freq 数组无法传递给异步任务和函数,因为如果再次调用函数可以更改它。 初始化仅在第一次调用该函数之前发生一次。

    c。将其包裹在 union 或 struct 中并返回整个对象

    struct freqstruct {
        int freq[128];
    };
    
    struct freqstruct freqofchar(char *str)
    {
        struct freqstruct freq = {0};
    
        /* ... */    
        return freq;
    }
    

    缺点 - 复制到 struct 中的整个数组。这不是很明智的内存和性能。

    1. 您的定义与函数的声明不匹配。这表明你没有付出足够的努力。
    int* freqofchar(char *str)
    
    int* freqofchar(char *str)
    {
         /* ... */
    }
    

    或 - 但我个人不喜欢这种表示法,因为它使初学者认为传递的是数组而不是指针。

    int* freqofchar(char str[])
    
    int* freqofchar(char str[])
    {
         /* ... */
    }
    
    1. 此函数不是 const corrent - 参数 str 应为 const char *strconst char str[]

    【讨论】:

      【解决方案4】:

      你有两个问题:

      1) 冲突类型:

      int* freqofchar(char)
      

      在声明中,但是

      int* freqofchar(char str[])
      

      在定义中。

      2) 您正在从 freqofchar 返回分配在堆栈上的频率

      【讨论】:

      • 一个好的答案不仅会指出问题,还会指出如何解决每个问题。
      • 正确使用的概念是数组的生命周期在函数返回时结束,而不是分配在堆栈上。
      猜你喜欢
      • 1970-01-01
      • 2014-07-25
      • 2014-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-11
      • 2020-04-05
      相关资源
      最近更新 更多