在 c 的函数中修改按值传递的结构体

Struct passed by value is modified in function in c

提问人:th3 wolf 提问时间:11/4/2023 最后编辑:Vlad from Moscowth3 wolf 更新时间:11/5/2023 访问量:123

问:

我有这段代码,我需要在不修改它的情况下对结构数组进行排序。我按值传递了结构数组,并使用带有索引的 int 数组对结构进行排序。要对数组进行排序,我还需要对结构进行排序,该结构在函数中按值传递,如果我没有错的话,如果您在函数中修改按值传递的值,它在 main 中仍然相同,但在 main 中它会发生变化,当我尝试使用排序的索引数组打印结构数组时,它不会打印它排序,因为结构的数组是sorted(正如我之前所说,要对索引进行排序,我需要修改结构体的数组)。

我希望如果我将结构数组作为值传递,它不会被修改。

这是函数,之后是我使用该函数的代码块。

void Opartenza(struct Corse c[], int d, int **O)
{
    int temp = 0;
    O[0] = (int *)malloc(d * sizeof(int));
    for(int i = 0; i < d; i++)
        O[0][i] = i;
    for(int i = 0; i < d; i++)
    {
        for(int j = 0; j < d - 1; j++)
        {
            if(strcmp(c[j].partenza, c[j + 1].partenza) > 0)
            {
                temp = O[0][j];
                O[0][j] = O[0][j + 1];
                O[0][j + 1] = temp;
                c[d] = c[j];
                c[j] = c[j + 1];
                c[j + 1] = c[d];
            }
        }
    }
}
case r_o_partenza:
                Opartenza(corse, dim, &Ordinamento);
                for(int i = 0; i < dim; i++)
                    printf("%s\n", corse[i].partenza);
                break;

如您所见,我尝试打印结构数组,并将其排序为索引数组。

数组 C 引用 式转换 按值传递

评论

1赞 Some programmer dude 11/4/2023
如果您想知道这个论点,请了解作为参数实际上与 .您不按值传递数组或其元素,而是传递指向数组第一个元素的指针。因此,您可以修改数组的元素,这些更改将在调用函数中生效。cstruct Corse c[]struct Corse *c
2赞 user7860670 11/4/2023
“我按值传递了结构数组”——这在 C 中是不可能的。 在代码中声明为指向的指针。cstruct Corse
2赞 n. m. could be an AI 11/4/2023
“我按值传递了结构数组”不,你没有。数组不能在 C 中按值传递。
1赞 Some programmer dude 11/4/2023
请花一些时间为变量使用正确的名称。什么?什么?什么?为什么有不同的大小写,有些变量是小写的,有些是大写的?如果使用语义相关的名称和一致的大小写,则代码将变得更易于理解。cdO
5赞 Weather Vane 11/4/2023
@th3wolf最好永远不要使用像O或l(oh或ell)这样的名字。它太难读了,太容易出错了,当你犯了错误时很难发现。单个字母已经够糟糕了,但看起来像数字的字母肯定更糟。

答:

3赞 dragon 11/4/2023 #1

在 C 语言中,数组总是通过引用传递,无论如何,因为在内部索引数组会编译为指针算术。这意味着,传递带有数组表示法的数组或指向数组的指针,实际上归结为同一件事。如果你真的希望你的数组不被修改,请先复制它。

评论

0赞 th3 wolf 11/4/2023
谢谢,我真的忘记了数组是通过引用传递的事实。
1赞 Weather Vane 11/4/2023
不是“总是”或“无论如何”。当结构按值传递时,作为结构成员的数组按值传递。
1赞 dragon 11/4/2023
是的,但是您传递的是结构,而不是数组。
0赞 John Bollinger 11/4/2023
从技术上讲,数组根本不会作为函数参数传递。不可能用 C 语言表达它,或者编写一个可以预期它的函数。如果指定数组的左值出现在函数调用的参数列表中,则它将衰减为指向第一个元素的指针(就像在大多数其他上下文中一样),并且指针按值传递。净效果类似于通过引用传递,但不是一回事。
0赞 John Bollinger 11/4/2023
FWIW,您也不能将索引运算符应用于数组。索引运算符仅针对一对操作数定义,其中一个操作数是指针(不是数组),另一个是整数。但同样,你甚至不能表达对数组的索引操作,因为数组衰减机制适用于这种情况。当然,在适当的时候,它会模拟索引数组本身。
1赞 Vlad from Moscow 11/4/2023 #2

在 C 语言中,通过引用传递意味着通过指向对象的指针间接传递对象。因此,取消引用传递的指针,您可以直接访问指针指向的对象。 这是一个演示程序

#include <stdio.h>

enum { N = 10 };

void f( int ( *pa )[N] )
{
    printf( "The size of the passed array is %zu\n", sizeof( *pa ) );
}

int main( void )
{
    int a[N];

    f( &a );
}

程序输出可能如下所示

The size of the passed array is 40

在 C 中,数组是不可修改的左值。因此,即使您将通过引用传递数组,也无法更改它。您只能更改其元素。也就是说,没有为数组定义赋值运算符。

例如,你不能写

char a[1] = { 'a' };
char b[1] = { 'b' };

a = b;

从 C 标准 (6.3.2.1 左值、数组和函数指示符

1 左值是一个表达式(对象类型不是 void) 可能指定一个对象;65) 如果左值没有 指定一个对象 在计算时,行为是未定义的。 当一个对象被说成具有特定类型时,该类型是 由用于指定对象的 lValue 指定。可修改的 lvalue 是一个没有数组类型的 lvalue,没有 不完整类型,没有 const 限定类型,如果它是 结构或联合,没有任何成员(包括递归地, 所有包含的集合体或联合体的任何成员或元素),并带有 常量限定类型。

另一方面,如果数组不是常量限定的,则可以更改数组的元素。

表达式中使用的数组指示符(极少数例外)被隐式转换为指向其第一个元素的指针。

从 C 标准 (6.3.2.1 左值、数组和函数指示符

3 除非它是 sizeof 运算符的操作数,或一元 & 运算符,或者是用于初始化数组的字符串文字,一个 类型为“array of type”的表达式将转换为表达式 类型为“pointer to type”,指向 数组对象,并且不是左值。如果数组对象有寄存器 存储类,则行为未定义。

因此,当您按值传递数组,然后用作参数表达式时,它会隐式转换为指向其第一个元素的指针。

此外,具有数组类型的函数参数也由编译器调整为指向数组元素类型的指针。

例如,这些函数声明

void f( int a[100] );
void f( int a[10] );
void f( int a[1] );
void f( int a[] );
void f( int *a );

是等价的,并声明相同的一个函数。

因此,如果您将按值传递一个数组,那么它将被隐式转换为指向其第一个元素的指针,并且使用指向传递数组的第一个元素的指针和指针算术,您可以更改传递数组的任何元素。