0

I am attempting to shift the elements of a char array to the right in C on Linux (Ubuntu 18.04) and attempted to make a function for this. I basically want to prepend x amount of elements to the beginning of the array and shift the rest of the data by x (to the right). If the new elements + old valid elements exceed the char size, I would like the function to return an error and don't do any shifting. I also made a char pointer that points towards the char array and I use structures to set the char data.

Here is a program I made as a test:

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

Here's an example output:

...
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`����

I know I am doing this incorrectly, but I'm unsure what I'm doing wrong or if I should be going about this differently.

My main questions are:

  1. What am I doing wrong with the shiftChar() function?

  2. Is there a better/easier way to achieve what I am trying to do?

  3. Is there a way to check if elements in a char array have a garbage value (e.g. a value that hasn't been filled in yet)? I suppose I could set the buffer to all 0's using something like memset(), but what happens if I have a structure with an int pointing to the buffer data with the value '0'. I'd imagine that would be excluded with the shifting if I checked if the value equals '0' or not.

I've done research on this as well, but most of the threads I've came across were for C++ or shifting elements to the left. I wasn't able to find a solid solution for my issue. I will be using the solution (if any) in another program I am making where I'll need to add the size of a struct iphdr to an existing buffer char at the beginning (with data already filled in via struct iphdr and struct udphdr) so I can create and send IPIP packets (network programming). I also understand I could make an entirely new char and just copy data over from the old buffer (while keeping the first sizeof(struct iphdr) elements free), but I'd imagine that would be quite a performance hit in my situation since I'm going to be having to do this thousands of times per second and it'd be better to just modify the existing buffer char.

I am new to C programming. Therefore, I'm sure there are things I'm missing.

If you need any additional information, please let me know and any help is highly appreciated!

Thank you for your time.

2
  • 1
    As long as the array is large enough to accommodate the increase in size, memmove() is the best way to shift the bytes over. Commented Mar 5, 2020 at 18:16
  • @LeeDanielCrocker Thank you! I've made a function I will be posting soon that uses memmove() and memcpy(). Commented Mar 6, 2020 at 2:43

3 Answers 3

1

Unless you are limiting arr to being a nul-terminated C-string, then you need a few more checks to be able to ensure you only move the initialized characters in the array by no more than the elements that remain to the right the characters in the array. Additionally, unless you have a nul-terminated C-string, you cannot rely on strlen() to obtain the number of characters in the array. You will have to pass that as an additional parameter, otherwise you will invoke Undefined Behavior passing a non-terminated array to strlen().

When writing a function that manipulates anything within a limited amount of storage, it often helps to grab pencil and paper and draw out what you are working with and label the variables you will need (paper and pencil is much faster than ASCII art). Nothing fancy, but you can use something like:

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

Which allows you to think through what the limits are on what you are attempting to do. Above, given an array of size elements containing nchr characters, if you want to shift the contents by shift elements, then the maximum shift available is size - nchr elements. Otherwise, you will exceed your array bounds. Additionally, if you attempt to shift by zero elements, then there is no reason in performing any tests, simply return error.

With the minimum tests to limit the shift and not respond to a shift of zero, you can do something like:

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 */
}

A short example that loops attempting to shift between 0 and 9 elements in a 10 element character array could be:

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

Example Use/Output

Which if I understood your specifications, should only complete the shift if it was possible to shift that number of characters within the array without exceeding the bounds, otherwise returning error:

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

Incorporate these thoughts with the other answers you have received and you should be able to write a shift function that meets all of your needs. Also remember, if dealing with something other than char you will need to multiply the number of bytes you shift by that amount, e.g.

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

Let me know if you have further questions.

Sign up to request clarification or add additional context in comments.

2 Comments

I'm sorry for the delay and thank you for the very detailed reply along with the advice! I am going to set this as the answer since it's definitely a better function than the one I was making since it includes error checks, etc.
Thank you. Taking the time to add each validation ultimately saves time in the long run. Ensuring each function that performs any type of computation relied on by the rest of your code also provides a meaningful return allowing you to validate success/failure is key. Good luck with your coding!
1

I do not know what is the best way but I think you want something like this.

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

1 Comment

Thank you for this! I made another function somewhat similar to the above, but instead of prepending an entire string, I prepend x amount of bytes to the beginning and shift the data over (similar to the function I posted). I will be posting the function here soon.
0

Here's an altered version of the function I initially posted that appears to work in my case:

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

Here are the changes:

  1. I use memmove() to move the existing data to the right by x and memcpy() to fill in the first x bytes with 0's.

  2. Instead of the for loop that moves the data going from 0 to data length (minus 1), I go from data length (minus 1) to 0. This is because elements were being replaced with already replaced data depending on the value of the size argument.

  3. I don't check for any errors regarding exceeding size in the function. I plan to do this before using the function.

Here is the full test program:

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

And here is the output:

Buffer => Hello!����TST

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

struct1->str => Hello!

Thank you to @P__J__ and @Lee Daniel Crocker for recommending memmove() and memcpy()!

If you see any improvements that can be made, please feel free to comment!

Thank you.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.