【问题标题】:Why does function call change value of string in main function?为什么函数调用会改变主函数中字符串的值?
【发布时间】:2021-01-04 07:54:06
【问题描述】:

在代码中,我想反转字符串。

但是,函数调用也改变了main函数中字符串A的值(函数是正确的,但我不知道为什么函数会改变主函数中字符串的实际值)

如何在不从main 修改A 的情况下反转revstr 中的字符串?

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

void revstr(char A[], int l);

int main(){
    char A[20];
    
    scanf("%s", A);
    int l=strlen(A);
    revstr(A,l);
    printf("%s", A);
    return 0;
}
void revstr(char A[], int l){
    int x=0,y,temp;
    y=l-1;
    while(x<y){
        temp=A[x];
        A[x]=A[y];
        A[y]=temp;
        x++;
        y--;
        
    }
    printf("%s", A);
}

【问题讨论】:

    标签: arrays c pass-by-reference implicit-conversion function-declaration


    【解决方案1】:

    您正在将数组传递给函数调用(即指向数组第一个元素的指针),并且从被调用的函数中,您实际上是在更改内存位置的内容。

    对这些内存位置的更改不是原始数组的副本,而是发生在实际数组本身中。因此,被调用函数所做的更改也反映调用函数。实际数组本身正在被修改,因此当您尝试在 main() 函数中打印数组时,它的内容已经被修改,并且修改后的内容正在被打印。

    如果您想稍后引用未修改的版本,您需要在调用者中保留原始数组的副本。

    【讨论】:

      【解决方案2】:

      请注意,您所说的字符串实际上是一个字符数组。因此,在函数中,您实际上并没有传递字符串,而是 char 数组的第一个元素在内存中的位置(指针)。

      因此,函数中操作的是存储在该特定位置的任何内容。

      解决此问题的一种方法是将一个额外的空数组传递给存储反转字符串的函数。

      【讨论】:

        【解决方案3】:

        当一个函数被调用时,参数被计算并且每个参数都被分配了相应参数的值。

        例如,您可以想象函数 revstr 的定义及其在 main 中的调用方式如下(为了清楚起见,我将函数参数重命名为 F_AF_l

        int main(){
        char A[20];
        
        scanf("%s", A);
        int l=strlen(A);
        revstr(A,l);
        //...
        
        
        void revstr( /*char F_A[], int F_l */ )
        {
            char F_A[] = A;
            int F_l = l;
            //...
        

        即函数参数由参数表达式的值初始化。

        但是这里有个问题。在此声明中

            char F_A[] = A;
        

        我们正在尝试使用类似的数组指示符初始化一个数组

        char A[] = "Hello";
        char F_A[] = A;
        

        这样的初始化是不正确的。您可以使用字符串文字或初始化器的花括号列表来初始化字符数组。

        那么这个问题在 C 中是如何解决的呢?

        首先,编译器将具有数组类型的函数参数调整为指向数组元素类型的指针。因此这个函数声明

        void revstr(char A[], int l);
        

        被编译器调整为声明

        void revstr(char *A, int l);
        

        并且两个声明都声明了相同的一个函数。您可以在程序中包含函数的两个声明,尽管编译器会发出一条消息,指出存在冗余函数声明。

        另一方面,当数组用作参数表达式时,它也会隐式转换为指向其第一个元素的指针。

        其实你有

        void revstr( char *A, int l );
        
        int main(){
        char A[20];
        
        scanf("%s", A);
        int l=strlen(A);
        revstr( &A[0], l );
        //...
        

        因此,在函数中,您正在处理一个指针,该指针指向在 main 中声明的数组 A 的元素所在的内存范围。使用此指针,您正在更改这些元素,即 main 中声明的原始数组。

        实际上数组A 的元素是通过指向它们的指针传递给函数的。这种在 C 中通过指向对象的指针来传递对象称为通过引用传递。使用指针,您可以直接访问指针所指向的对象。

        注意,函数revstr的声明和定义最好如下面的演示程序所示。

        #include <stdio.h>
        #include <string.h>
        
        char * revstr( char *s, size_t n )
        {
            if ( n != 0 )
            {
                for ( size_t i = 0; i < --n; i++ )
                {
                    char c = s[i];
                    s[i] = s[n];
                    s[n] = c;
                }
            }
            
            return s;
        }
        
        int main(void) 
        {
            char s[20];
            
            scanf( "%19s", s );
            
            size_t n = strlen( s );
            
            puts( revstr( s, n ) );
        
            return 0;
        }
        

        程序输出可能看起来像

        Stackoverflow
        wolfrevokcatS
        

        即函数revstr的第二个参数应该是size_t类型而不是int类型,因为size_t是函数strlen的返回类型。

        函数应该返回指向反向数组第一个字符的指针。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-08-07
          • 2015-11-11
          • 1970-01-01
          • 2010-09-25
          • 2015-10-14
          • 2013-10-29
          • 2012-09-29
          • 1970-01-01
          相关资源
          最近更新 更多