为什么我的C函数返回字符串变量作为指针而不是值?

x8diyxa7  于 2023-06-05  发布在  其他
关注(0)|答案(1)|浏览(117)

在C中,我有以下函数:

//Get Readability: Coleman-Liau Index Formula: ((0.0588 * Average # of letters per 100 words) - (0.296 * Average # of sentences per 100 words) - 15.8)
string get_readability(float avg_letters, float avg_sentences)
{
    //Store as int so that it rounds the resulting index number
    int index = (0.0588 * avg_letters) - (0.296 * avg_sentences) - 15.8;

    //printf("Index: %i\n", index);//debug

    //Return the grade level
    if (index < 1)
    {
        return "Before Grade 1";
    }
    else if (index > 16)
    {
        return "Grade 16+";
    }
    else
    {
        //Create char array for result in order to concat the index number
        char result[8] = "Grade ";
        char grade[2];
        sprintf(grade, "%i", index);//Convert int to string
        strcat(result, grade);
        //Convert char array to string in order to return results
        string result_str = result;

        return result_str;
    }
}

当我调用它并将其存储在一个变量中时,它是正确的,我可以立即打印出来,没有任何问题:

//Get reading level
//string reading_level = get_readability(letter_avg, sentence_avg);
string reading_level = get_readability(464.29f, 28.57f);

//printf("%i letters\n%i words\n%i sentences\n%f letter avg\n%f sentence avg\n", letter_count, word_count, sentence_count, letter_avg, sentence_avg);//debug
    
printf("%s\n", reading_level);

但是如果我在设置变量和打印变量之间执行多个printf语句,那么变量将更改为“”,并且只打印一个空行。

//Get reading level
//string reading_level = get_readability(letter_avg, sentence_avg);
string reading_level = get_readability(464.29f, 28.57f);

//printf("%i letters\n%i words\n%i sentences\n%f letter avg\n%f sentence avg\n", letter_count, word_count, sentence_count, letter_avg, sentence_avg);//debug
printf("\n");
printf("\n");
printf("\n");

printf("%s\n", reading_level);

查找这个问题,似乎问题可能是当函数返回result_str时,它返回了指向该变量的指针,并且在调用printf几次之后,它被重写了。但是我真的不明白这是怎么发生的,因为我没有用 * 传递任何东西。

mgdq6dx1

mgdq6dx11#

你的问题是

char result[8] = "Grade ";
...
string result_str = result;

return result_str;

首先,string类型是CS50所特有的;它不是标准C语言的一部分。C没有实际的 string 数据类型。
其次,CS50 string类型也不是一个实际的 string 数据类型-它是一个 pointer 类型的typedef名称(别名):

typedef char *string;

C语言中的字符串只是一个包含零值终止符的字符值序列-字符串"hello"表示为序列{'h', 'e', 'l', 'l', 'o', 0}。字符串 * 存储 * 在字符类型的数组中,但字符数组也可以存储 * 非 * 字符串的序列(没有0值终止符,或者有多个0值字节)。
除非它是sizeof_Alignof或一元&运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则类型为“T的N元素数组”的 expression 将被转换,或“decay”,指向类型为“指向T的指针”的表达式,表达式的值将是数组第一个元素的地址。当你将数组表达式作为参数传递给函数时,比如

char arr[N];
foo( arr );

上面的转换规则将arr替换为指针表达式,这样函数调用看起来更像

foo( &arr[0] );

而函数接收的是指针,而不是数组。这适用于所有数组类型,而不仅仅是字符数组。
所以当你写的时候

string result_str = result;

你将result数组的第一个元素的 * 指针 * 存储到result_str,而不是它的内容,并通过扩展将该地址返回给调用者。
第三,result数组是get_readability的本地数组,一旦函数退出,* 就不再存在 *。数组占用的内存仍然存在,但现在可供其他函数使用。只要在get_readability之后没有调用任何其他函数,内存就保持不变,代码看起来像预期的那样工作,但是一旦调用printf( "\n" );,内存就会被覆盖。
这里有两条路可走。
第一种(IMO首选)方法是将目标缓冲区作为参数传递给get_readability(将string的任何示例替换为char *,因为string在CS50环境之外无法工作):

/**
 * We're returning a pointer to the target buffer just
 * for convenience's sake.
 */
char *get_readability( float avg_letters, float avg_sentences, char *result, size_t result_size )
{
  ...
  /**
   * Using snprintf to make sure we don't write past the
   * end of the result array.
   */
  snprintf( result, result_size, "Grade %d", index );
  return result;
}

你会称之为

char result[8];
...
printf( "reading level: %s\n", get_readability( 464.29f, 28.57f, result, sizeof result ) );

同样,我们返回result指针只是为了方便;您还可以执行以下操作:

char result[8];
get_readability( 464.29f, 28.57f, result, sizeof result );
printf( "Reading level: %s\n", result );

第二个(不太受欢迎的)选项是在get_readability中动态分配内存,并返回一个指针:

char *get_readability( float avg_letters, float avg_sentences )
{
  ...
  char *result = calloc( 8, sizeof *result );
  if ( result )
    snprintf( result, 8, "Grade %d", index );
  else
    // memory allocation failure, handle as appropriate
  return result;
}

并称之为

char *result = get_readablity( 464.29f, 28.57f );
printf( "Reading level: %s\n", result );
...
free( result );

此方法的缺点:

  • 当您完成它时,您必须使用free库函数显式地释放分配的内存;
  • 为了能够做到这一点,你必须将返回的指针值保存到某个变量中--如果你写了类似
printf( "Reading level: %s\n", get_readability( 464.29f, 28.57f ) );

的东西,那么你就失去了对那个动态分配的缓冲区的任何引用,它会一直挂起,直到程序退出。

你可以做一些像

char *result;
printf( "Reading level: %s\n", (result = get_readability( 464.29f, 28.57f ) );
...
free( result );

这样的事情,但它不会给你带来那么多好处,而且你很可能会被维护你代码的任何人谋杀。

相关问题