【问题标题】:Best Way To Shift Characters To The Right In Char Array在字符数组中向右移动字符的最佳方法
【发布时间】:2020-03-05 18:02:40
【问题描述】:

我正在尝试在 Linux (Ubuntu 18.04) 上的 C 中将 char 数组的元素向右移动,并尝试为此创建一个函数。我基本上想将x 数量的元素添加到数组的开头,并将其余数据移动x(向右)。如果新元素 + 旧有效元素超过 char 大小,我希望函数返回错误并且不进行任何移位。我还创建了一个指向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. 有没有更好/更简单的方法来实现我想要做的事情?

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

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

我是 C 编程新手。因此,我确定我缺少一些东西。

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

感谢您的宝贵时间。

【问题讨论】:

  • 只要数组足够大以适应大小的增加,memmove() 是移动字节的最佳方式。
  • @LeeDanielCrocker 谢谢!我制作了一个函数,我很快就会发布它,它使用 memmove()memcpy()

标签: c char shift


【解决方案1】:

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

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

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

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

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

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

使用/输出示例

如果我理解您的规范,那么只有在可以在不超出界限的情况下移动数组中的字符数时才应该完成移动,否则返回错误:

$ ./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 */

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

【讨论】:

  • 很抱歉耽搁了时间,感谢您非常详细的回复和建议!我将把它设置为答案,因为它绝对是一个比我做的更好的功能,因为它包括错误检查等。
  • 谢谢。从长远来看,花时间添加每个验证最终会节省时间。确保执行其余代码所依赖的任何类型计算的每个函数还提供有意义的返回,允许您验证成功/失败是关键。祝你编码好运!
【解决方案2】:

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

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

【讨论】:

  • 谢谢你!我制作了另一个与上面有点相似的函数,但不是在前面添加整个字符串,而是在开头添加 x 字节数量并将数据移过来(类似于我发布的函数)。我很快就会在这里发布这个功能。
【解决方案3】:

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

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. 而不是 for 循环将数据从 0 移动到数据长度(减 1),我从数据长度(减 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()

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

谢谢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-28
    • 1970-01-01
    • 2016-11-22
    • 1970-01-01
    • 2018-08-16
    • 2021-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多