【问题标题】:Strange behavior in a function that moves items inside an array在数组内移动项目的函数中的奇怪行为
【发布时间】:2021-05-05 00:47:38
【问题描述】:

我有一个这样调用的函数

arraypointer = move(arraypointer, item, remove_location, move_location)

(它在另一个函数中调用自己,arraypointer 被大量使用。)

如果我有这样的数组

1 2 3 0 4 5 6

我打电话给

arraypointer = move(arraypointer, 0, 3, 0)

它会返回

0 1 2 3 4 5 6

这里是代码

int* move(const int arrz[], const int mvalue, const int rlocation, const int mlocation) {
    static int uselessarray[7] = { 0 };
    uselessarray[0] = arrz[0];
    uselessarray[1] = arrz[1];
    uselessarray[2] = arrz[2];
    uselessarray[3] = arrz[3];
    uselessarray[4] = arrz[4];
    uselessarray[5] = arrz[5];
    uselessarray[6] = arrz[6];

    static int returnarray[7];
    returnarray[0] = 1;
    int i = 0; int j = 0;

    
    
    for (i = 0; i < 7; i++) {
        if (i == mlocation) {
            j++;
            returnarray[mlocation] = mvalue;
        }
        if (i == rlocation) {
        }
        else {
            returnarray[j] = uselessarray[i];
            //returnarray[j] = arrz[i];
            j++;
        }
        
    }
    return returnarray;
}

如果我不初始化uselessarray 并从arrz 复制数据。然后当我更改returnarray 时,它也会更改arrz。即使arrz 是一个常数,我也绝不要求更改它。它完全符合我在上面粘贴的代码的要求。但我想弄清楚为什么我首先需要uselessarray。为什么它不能只使用我最初传递给它的数组。为什么我不要求它改变时它会改变?

我试过了

  1. 将大小添加到arrz
  2. 制作arrz static 而不是const
  3. 制作 returnarray 而不是 static(这更糟)
  4. 使用= { 0,0,0,0,0,0,0 }returnarray 初始值

除了声明uselessarray 有效之外别无他法。这是怎么回事?

【问题讨论】:

  • 您的数组有 6 个元素,但您正在尝试访问 7 个。
  • 看起来像是一个值的简单副本(到临时变量中),然后是 memory move,然后将(临时)值复制到其最终位置。
  • @dbush 该示例有 6 个。但在实际代码中它是 7 个。很抱歉造成混乱。
  • @Someprogrammerdude 老实说,我不知道在这种情况下如何使用内存移动。你可能是对的,它会更合适。有没有 youtube 视频或我可以看的东西。人们发布的很多编程参考都用我不懂的语言说话。
  • @NickMatveev 您使用什么语言,C 还是 C++?请选择一个。

标签: c++ arrays c function pointers


【解决方案1】:

适用于您的示例的函数可能如下所示:

void move(int arrz[], const int mvalue, const int rlocation, const int mlocation)
{
    // Get a copy of the value to "move"
    int value = arrz[mlocation];

    // Move (part) of the array one step to the right
    memmove(&arrz[rlocation + 1], &arrz[rlocation], (mlocation - rlocation) * sizeof(int));

    // And copy the value to its destination place
    arrz[rlocation] = value;
}

[Working example here]

请注意,此代码在原始数组中进行就地更改。如果您需要创建一个新数组并返回,那么我建议您将新数组作为指向其第一个元素的指针传入。

虽然这适用于问题中显示的示例,但它可能不适用于我们未知的其他情况。

【讨论】:

  • 打算试一试。给我几分钟。顺便说一句,谢谢。
  • 好的,我只是查看了代码。我并不总是想移动到第一个元素。有时我会将 5 中的值替换为 3 中的值 所以就像 1 2 4 5 3 6 7 move(arrz, 3, 5, 3) 1 2 3 4 5 6 7 它需要从第 5 个位置移除,并且添加到第三个位置。同时保持阵列的其余部分完好无损。
【解决方案2】:

考虑到安全问题,我为此问题开发了一个解决方案。作为一般的做法,在子程序中不干预原始序列内容和地址。我已经考虑了我开发的解决方案中可能出现的一些错误元素。以下链接可用于测试程序:GodBolt

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

/// It is used for the simplest form of adaptation to varying array sizes.
#define ARRAY_SIZE 7

/**
 * @brief   It will be used to handle some errors that may occur in the program.
 */
enum errorCode
{
    faultless = 0,                  // No error occurred in the program
    sourceArrayError = -1,          // Source array doesn't point to an address.
    destinationArrayError = -2,     // Destination array doesn't point to an address.
    locationTargetError = -3,       // Location target exceeds array size.
    copyError = -4,                 // Copy error has occurred.
    sizeError = -5,                 // Array sizes are not equal.
};

/**
 * @brief   The error message is printed to the stderr stream to show the cause
 *          of the error in the console.
 * @param   signum is the error number argument.
 */
void errorHandler( int signum )
{
    switch( signum )
    {
        case sourceArrayError:
            fprintf( stderr, "Source array doesn't point to an address." );
            break;

        case destinationArrayError:
            fprintf( stderr, "Destination array doesn't point to an address." );
            break;

        case locationTargetError:
            fprintf( stderr, "Location target exceeds array size." );
            break;

        case copyError:
            fprintf( stderr, "Copy error has occurred." );
            break;

        case sizeError:
            fprintf( stderr, "Array sizes are not equal." );
            break;

        default:
            fprintf( stderr, "An unknown error has occurred." );
            break;
    }

    exit( EXIT_FAILURE );
}

/**
 * @brief   Subprogram that can be used for safe transport of the contents of an array.
 * @param   originalArray The content of the originalArray array is passed to the program as
 *          an address to be manipulated. Since the array originalArray is passed to the
 *          subprogram as "const int * const", its address and content cannot be changed.
 * @param   resultArray Is the array where the result data will be stored.
 * @param   size Refers to the size of the original directory.
 * @param   rLocation Refers to the target location.
 * @param   mLocation Refers to the position of the data to be moved in the array.
 * @return  Returns the verification code if no error occurs in the program.
 */
int move( const int * const originalArray, int * const resultArray, const int size, const int rLocation, const int mLocation )
{
    /// If the array doesn't point to an address, terminate the program.
    if( originalArray == NULL )
    {
        errorHandler( sourceArrayError );
    }

    /// If the array doesn't point to an address, terminate the program.
    if( resultArray == NULL )
    {
        errorHandler( destinationArrayError );
    }

    /// Terminate the program if the position of the element to be read in the array is outside the array dimensions.
    if( mLocation >= size )
    {
        errorHandler( locationTargetError );
    }

    /// Terminate the program if the array size argument passed to the subprogram is greater than the current array size.
    if( ARRAY_SIZE < size )
    {
        errorHandler( sizeError );
    }

    /// The data in the position passed on an argument is being moved.
    /// originalArray should only be used as a rvalue in the program.
    resultArray[ 0 ] = originalArray[ mLocation ];

    /// The first episode of the original array is copied to the new array.
    if( memcpy( &resultArray[ rLocation + 1 ], &originalArray[ rLocation ], ( mLocation - rLocation ) * sizeof( int ) ) == NULL )
    {
        errorHandler( copyError );
    }

    /// The second part of the original array is copied to the new array.
    if( memcpy( &resultArray[ mLocation + 1 ], &originalArray[ mLocation + 1 ], ( ARRAY_SIZE - ( mLocation + 1 ) ) * sizeof( int ) ) == NULL )
    {
        errorHandler( copyError );
    }

    return faultless;
}

/**
 * @brief   Main Program
 * @return  Returns 0 if the program is successful.
 */
int main( void )
{
    int originalArray[ ARRAY_SIZE ] = { 1, 2, 3, 0, 4, 5, 6 };
    /// Any intervention to the original array isn't acceptable for high security purposes.

    int resultArray[ ARRAY_SIZE ] = { 0 };
    /// The new order will be stored in this array for testing purposes.

    unsigned int index = 0;
    /// It will be used for counter checking in the for loop.

    int errorCode;
    /// Will be used to provide error checking.

    errorCode = move( originalArray, resultArray, ARRAY_SIZE,  0, 3 );
    /// The 4 th element of the array will be moved to the memory address where the 1st
    /// element of the array is stored. Other array elements will be right shifted.

    /// If there was no error in the subprogram, print the contents of the array to the console.
    if( errorCode == faultless )
    {
        for( index = 0 ; index < ARRAY_SIZE ; ++index )
        {
            printf( "originalArray[ %u ]: %d\t\t", index, originalArray[ index ] );

            printf( "resultArray[ %u ]: %d\n", index, resultArray[ index ] );
        }
    }

    return EXIT_SUCCESS;
}

【讨论】:

    【解决方案3】:

    为了测试你的功能,我添加了这个:

    #include <stdio.h>
    
    void pa(int *a, int n) {
            int i;
            for (i = 0; i < n; i++) {
                    printf("%d%s", a[i], i == n-1 ? "\n" : ", ");
            }
    }
    int main() {
            int a[] = { 1, 2, 3, 0, 4, 5, 6 };
            int *p;
            p = move(a, 0, 3, 0);
            pa(a, 7);
            pa(p, 7);
            return 0;
    }
    

    而且,它按预期工作也就不足为奇了。但是,您正在做的事情(来自您的 github 转储)更像是这样的:

    int main() {
                int a[] = { 1, 2, 3, 0, 4, 5, 6 };
                int *p;
                p = move(a, 0, 3, 0);
                pa(a, 7);
                pa(p, 7);
                p = move(p, 1, 2, 3);
                pa(p, 7);
                return 0;
        }
    

    请注意,第二次调用 move 提供了 p,它是第一次调用 move 返回的静态值。这是你问题的根源。

    ps:您应该查看在 so 上发帖的规则。

    【讨论】:

    • 非常感谢。我也会审查规则。
    猜你喜欢
    • 2018-06-08
    • 1970-01-01
    • 2020-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多