在 Char 数组中向右移动字符的最佳方法

Best Way To Shift Characters To The Right In Char Array

我试图在 Linux (Ubuntu 18.04) 上将 char 数组的元素向右移动,并试图为此创建一个函数。我基本上想将 x 数量的元素添加到数组的开头,并将其余数据移动 x (向右)。如果新元素 + 旧有效元素超过 char 大小,我希望函数 return 出错并且不做任何移位。我还制作了一个指向 char 数组的 char 指针,我使用结构来设置 char 数据。

这是我做的一个测试程序:

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

struct struct1
{
    char str[10];
    int myNum;
};

struct struct2
{
    char str[5];
};

int shiftChar(char *arr, int size, int length)
{

    for (int i = 0; i < length; i++)
    {
        // I know I am calculating this incorrectly. Though, not sure how I should go about checking if the new size will exceed the length of the char array.
        if (length < ((i + 1) + size))
        {
            return -1;
        }

        // If element is 0, we shouldn't shift it (usually represents garbage value?). Not sure how to detect whether an element of a char array was filled with actual data or not.
        if (arr[i] == 0)
        {
            continue;
        }

        arr[i + size] = arr[i];
        fprintf(stdout, "Replacing %c with %c at %d => %d\n\n", arr[i + size], arr[i], i, i + size);
    }

    for (int i = 0; i < size; i++)
    {
        arr[i] = 0;
    }

    return 0;
}

int main()
{
    char buffer[256];
    struct struct1 *struct1 = (struct struct1 *) (buffer);
    struct struct2 *struct2 = (struct struct2 *) (buffer + sizeof(struct struct1));

    struct1->myNum = 5;
    strncpy(struct1->str, "Hello!", 6);

    strncpy(struct2->str, "TST", 3);

    fprintf(stdout, "Buffer => ");

    for (int i = 0; i < (sizeof (struct struct1) + sizeof(struct struct2)); i++)
    {
        fprintf(stdout, "%c", buffer[i]);
    }

    fprintf(stdout, "\n\n");

    if (shiftChar(buffer, 6, 256) != 0)
    {
        fprintf(stdout, "Error shifting char array.\n");

        //exit(1);
    }

    struct1 = (struct struct1 *) (buffer + 6);
    struct2 = (struct struct2 *) (buffer + sizeof(struct struct1) + 6);

    fprintf(stdout, "struct1->str => %s\n", struct1->str);

    exit(0);
}

这是一个示例输出:

...
Error shifting char array.
struct1->str => Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hell`����

我知道我做错了,但我不确定我做错了什么,或者我是否应该采取不同的方式。

我的主要问题是:

  1. 我对 shiftChar() 函数做错了什么?

  2. 是否有 better/easier 方法来实现我想要做的事情?

  3. 有没有办法检查 char 数组中的元素是否有垃圾值(例如,尚未填充的值)?我想我可以使用 memset() 之类的方法将缓冲区设置为所有 0,但是如果我有一个结构 int 指向值为“0”的缓冲区数据会发生什么'.如果我检查值是否等于“0”,我想这会被移位排除。

我也对此进行了研究,但我遇到的大多数线程都是针对 C++ 或向左移动元素的。我无法为我的问题找到可靠的解决方案。我将在我正在制作的另一个程序中使用该解决方案(如果有的话),我需要在开头将 struct iphdr 的大小添加到现有缓冲区字符(数据已经通过 [=24 填充) =] 和 struct udphdr) 这样我就可以创建和发送 IPIP 数据包(网络编程)。我也明白我可以制作一个全新的 char 并从旧缓冲区复制数据(同时保持第一个 sizeof(struct iphdr) 元素空闲),但我想这对我的情况来说会是一个相当大的性能打击,因为我我将不得不每秒执行此操作数千次,最好只修改现有缓冲区 char.

我是 C 编程新手。因此,我确定我遗漏了一些东西。

如果您需要任何其他信息,请告诉我,非常感谢您的帮助!

感谢您的宝贵时间。

我不知道什么是最好的方法,但我想你想要这样的东西。

char *prependstr(char *str, const char *pstr)
{
    size_t pstrlen = strlen(pstr);
    memmove(str + pstrlen, str, strlen(str) + 1);
    memcpy(str,pstr, pstrlen);
    return str;
}

int main()
{
    char x[256] = "";
    char pstr[] = "Hello00!!";
    int i = 0;

    do
    {   
        sprintf(pstr, "Hello%02d!!", i++);
        printf("%s\n", prependstr(x,pstr)); 
    }while(strlen(pstr) + strlen(x) + 1 < sizeof(x));
}

https://godbolt.org/z/Lx5U6D

这是我最初发布的函数的修改版本,它似乎适用于我的情况:

void shiftChar(char *arr, int size, int dataLen)
{
    for (int i = (dataLen - 1); i >= 0; i--)
    {
        memmove(arr + i + size, arr + i, 1);
    }

    for (int i = 0; i < size; i++)
    {
        memcpy(arr + i, "0", 1);
    }
}

更改如下:

  1. 我使用memmove()将现有数据向右移动xmemcpy()用0填充第一个x字节.

  2. 我没有将数据从 0 移动到数据长度(负 1)的 for 循环,而是从数据长度(负 1)移动到 0。这是因为元素根据 size 参数的值被替换为已经替换的数据。

  3. 我不检查函数中是否存在任何关于超出大小的错误。我打算在使用该功能之前执行此操作。

完整的测试程序如下:

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

void shiftChar(char *arr, int size, int dataLen)
{
    for (int i = (dataLen - 1); i >= 0; i--)
    {
        memmove(arr + i + size, arr + i, 1);
    }

    for (int i = 0; i < size; i++)
    {
        memcpy(arr + i, "0", 1);
    }
}

struct struct1
{
    char str[10];
    int myNum;
};

struct struct2
{
    char str[5];
};

int main()
{
    char buffer[256];
    struct struct1 *struct1 = (struct struct1 *) (buffer);
    struct struct2 *struct2 = (struct struct2 *) (buffer + sizeof(struct struct1));

    struct1->myNum = 5;
    strncpy(struct1->str, "Hello!", 6);

    strncpy(struct2->str, "TST", 3);

    fprintf(stdout, "Buffer => ");

    for (int i = 0; i < (sizeof (struct struct1) + sizeof(struct struct2)); i++)
    {
        fprintf(stdout, "%c", buffer[i]);
    }

    fprintf(stdout, "\n\n");

    shiftChar(buffer, 6, sizeof(struct struct1) + sizeof(struct struct2));

    fprintf(stdout, "New Buffer => ");

    for (int i = 0; i < 6 + (sizeof (struct struct1) + sizeof(struct struct2)); i++)
    {
        fprintf(stdout, "%c", buffer[i]);
    }

    struct1 = (struct struct1 *) (buffer + 6);
    struct2 = (struct struct2 *) (buffer + sizeof(struct struct1) + 6);

    fprintf(stdout, "\n\nstruct1->str => %s\n", struct1->str);

    exit(0);
}

这是输出:

Buffer => Hello!����TST

New Buffer => 000000Hello!����TST

struct1->str => Hello!

感谢@P__J__ 和@Lee Daniel Crocker 推荐 memmove()memcpy()

如果您发现任何可以改进的地方,请随时发表评论!

谢谢。

除非您将 arr 限制为 nul-terminated C 字符串,否则您需要进行更多检查以确保您只移动数组中的初始化字符不超过数组中字符右侧的元素。此外,除非您有一个 nul-terminated C 字符串,否则您不能依赖 strlen() 来获取数组中的字符数。您必须将其作为附加参数传递,否则您将调用 Undefined Behavior 将未终止数组传递给 strlen().

当编写一个在有限的存储量内操作任何东西的函数时,它通常有助于拿起铅笔和纸并画出您正在使用的内容并标记您将需要的变量(纸和铅笔比快得多ASCII 艺术)。没什么特别的,但你可以使用类似的东西:

    |<--------------- size ---------------->|
    |                                       |
    +---+---+---+---+---+---+---+---+---+---+
    | a | b | c | d |   |   |   |   |   |   |
    +---+---+---+---+---+---+---+---+---+---+
    |<---- nchr --->|
    |<-------- shift ------>|

这让您可以思考您尝试做的事情的局限性。上面,给定一个包含 nchr 个字符的 size 元素的数组,如果你想将内容移动 shift 个元素,那么最大可用的移动是 size - nchr 个元素。否则,您将超出数组范围。此外,如果您尝试移动零个元素,则没有理由执行任何测试,只是 return 错误。

通过最少的测试来限制偏移并且不响应零偏移,您可以执行以下操作:

int shiftchar (char *arr, size_t size, size_t nchr, size_t shift)
{
    size_t max = size - nchr;   /* max shift is size - no. of filled chars */

    if (!shift) {       /* validate positive shift */
        fputs ("error: zero shift requested\n", stderr);
        return 0;       /* return failure */
    }

    if (shift > max) {  /* check if shift exceeds no. of chars available */
        fputs ("error: shift exceeds array bounds\n", stderr);
        return 0;       /* return failure */
    }

    memmove (&arr[shift], arr, shift);      /* shift chars in arr to right */

    return 1;   /* return success */
}

一个循环尝试在 10 元素字符数组中的 09 元素之间移动的简短示例可以是:

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

int shiftchar (char *arr, size_t size, size_t nchr, size_t shift)
{
    size_t max = size - nchr;   /* max shift is size - no. of filled chars */

    if (!shift) {       /* validate positive shift */
        fputs ("error: zero shift requested\n", stderr);
        return 0;       /* return failure */
    }

    if (shift > max) {  /* check if shift exceeds no. of chars available */
        fputs ("error: shift exceeds array bounds\n", stderr);
        return 0;       /* return failure */
    }

    memmove (&arr[shift], arr, shift);      /* shift chars in arr to right */

    return 1;   /* return success */
}

int main (void) {

    for (size_t i = 0; i < 10; i++) {
        char arr[10] = "0123456789";
        if (shiftchar (arr, 10, i, i)) {
            memset (arr, 'z', i);
            printf ("%.10s\n", arr);
        }
    }
}

示例Use/Output

如果我理解您的规格,应该只在可以在不超出范围的情况下移动数组中的字符数的情况下完成转换,否则 returning 错误:

$ ./bin/shiftchar
error: zero shift requested
z023456789
zz01456789
zzz0126789
zzzz012389
zzzzz01234
error: shift exceeds array bounds
error: shift exceeds array bounds
error: shift exceeds array bounds
error: shift exceeds array bounds

将这些想法与您收到的其他答案相结合,您应该能够编写满足您所有需求的 shift 函数。还要记住,如果处理 char 以外的其他内容,则需要将移动的字节数乘以该数量,例如

    memmove (&arr[shift], arr, shift * sizeof *arr);      /* shift elements in arr to right */

如果您还有其他问题,请告诉我。