为什么函数调用会改变主函数中字符串的值?

问题描述

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

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

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

#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);
}

解决方法

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

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

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

,

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

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

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

,

当一个函数被调用时,参数会被评估,并且每个参数都会被赋予相应参数的值。

例如,如果函数 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 是函数 {{1} 的返回类型}}。

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