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:
What am I doing wrong with the shiftChar()
function?
Is there a better/easier way to achieve what I am trying to do?
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.
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.