-4

What is the most efficient way of accomplishing the insertion of one element before others in an array without having to rearrange all elements with a for loop. I have tried a circular buffer implementation, but it doesn't work and I am getting frustrated. I made the question simpler, so it will hopefully be easier for everyone.

int * buffer = (int *) calloc(L_buffer, sizeof(int));
int i, j, K, out;

while(1) {

    i = rand();
    K = 3;

    /* Calculate output */

    out = buffer[i]  buffer[i+1]  + buffer[K];

    /* Shift array elements by one and insert new value */

    for(j = 0; j < L_buffer-1; j++) 
        buffer[j+1] = buffer[j];

    buffer[0] = new_value;
}

Solution: I edited the solution of @Eric Postpischil to suit my needs. By extending the function to the following, I can advance the current index in both directions and always wrap around.

int * cb_element(CircularBuffer *cb, ptrdiff_t i) {

    if ( cb->current < 0)
        cb->current += cb->size;

    ptrdiff_t index = cb->current + i;

    if ((size_t) index >= cb->size)
        index -= cb->size;


    return &cb->array[index];
}

So I can

out = *cb_element(&cb, i) + *cb_element(&cb, i+1);

cb.current--;
*cb_element(&cb, 0) = new_value;

Really neat!

neolith
  • 699
  • 1
  • 11
  • 20
  • 1
    What do you mean by "but it doesn't work" and "kind of works"? – Scott Hunter Mar 14 '21 at 12:40
  • This is for an audio effect. The first version just distorts the signal completely. The last version generates a proper effect, but there are some glitches like popping noises. Maybe the last version actually is right. If so, the bug lies somewhere else. – neolith Mar 14 '21 at 12:42
  • This doesn't look like correct to me: `if (--p_dynamic < p_start)` – M. Nejat Aydin Mar 14 '21 at 12:46
  • Why not simply ... `p[counter++ % length]`? (`counter` for `i` or `K` or ...) – pmg Mar 14 '21 at 12:47
  • @M.NejatAydin Why not? I first decrement and if the moving pointer is lower than the first address I go to the last address in the buffer – neolith Mar 14 '21 at 12:47
  • @neolith How do you make sure that the `p_dynamic` will be within the bounds of the array? For example, if `p_dynamic == &array[0]` then it will contain an invalid address after `--p_dynamic` – M. Nejat Aydin Mar 14 '21 at 12:53
  • @M.NejatAydin Look at what I wrote after /* and after every iteration */ – neolith Mar 14 '21 at 12:54
  • Please take the [tour] and read [ask]. Then, carefully reread your question: It asks about "accomplishing this" without ever defining what "this" really is. Please [edit] your question to clarify that. As it stands, it's impossible to answer without guessing. – Ulrich Eckhardt Mar 14 '21 at 21:35
  • The post was very detailed and confusing before. I already edited it earlier to make it easier to understand for others. I did it once more. I hope it is clearer now. – neolith Mar 14 '21 at 22:29

2 Answers2

0

There is probably a more elegant way than doing it with so many lines.

"mod" the array index access with % L_array using unsigned math. Further, if L_array is an unsigned power-of-2, % L_array results in simply emitted and code.

output = p_dynamic[i] + p_dynamic[(i+1)% L_array] + p_dynamic[(i+2)% L_array];

Note: --p_dynamic < p_start is never true unless code relies on undefined behavior. The valid values to compare p_dynamic against are array[0]...array[length], That does not include array[-1].

Insertion looks suspicious for a circular buffer. There should be no reason for a for loop to shift. Instead adjust references to the ends of the buffer.

Consider using below:

array = calloc(L_array, sizeof *array);
size_t length = 0; // current length
size_t begin = 0;  // Index of eldest sample
size_t end = 0;    // Index to store the next sample

To insert

if (length < L_array) {
  length++;
  array[end] = new_value;
  end = (end+1)%L_array;
}

To extract

if (length > 0) {
  length--;
  sample = array[begin];
  begin = (begin+1)%L_array;
}

To sample 3 elements starting at i

if (length >= 3) {
  output = array[(begin+i) % L_array] + array[(begin+i+1) % L_array] +
      array[(begin+i+2) % L_array];
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • I just tried the solution with the modulo a few seconds before and it also has glitches. I also can't compare the pointer to an array index, because I allocated memory with calloc. Therefore I stored the first and last entry right after allocation and am using the pointer to the allocated memory is p_dynamic. – neolith Mar 14 '21 at 13:13
  • I cannot work with arrays here, because all of this happens in an interrupt routine. A circular buffer is the only way to make this in time. The processor has 225 MHz. – neolith Mar 14 '21 at 13:56
  • @neolith The suggested coding approach does not use _arrays_, but a pointer and 3 integers effecting a circular buffer. I have successfully coded many times solitons like this in interrupts service routines. Given data is adjusted in interrupts, any non-ISR access and manipulation of the buffer warrant protection from interrupts for a short time. The CPU speed is not an issue. – chux - Reinstate Monica Mar 14 '21 at 14:17
  • I made the question easier, because I am getting confused myself. Hope it helps. Sorry if your effort so far has been for nothing. – neolith Mar 14 '21 at 14:44
0

int *p_dynamic = array[0]; int *p_start = array[0]; int *p_end = array[L_array - 1];

You have not told us what these variables are intended to do, so we do not know if you are using them in a sensible way for their intended purposes.

if (--p_dynamic < p_start)

When p_dynamic points to array[0], the behavior of --p_dynamic is not defined by the C standard. The standard defines pointer arithmetic only from elements 0 of an array to just beyond the last element of the array.

int K = 3; int i = some_int; int output; output = array[i] + array[i+1] + array[K];

Again, you have not told us the intent, so we do not know what you are trying to achieve here. Why is K 3? Is that a fixed constant in every use of your code or just an example? Does it have any relationship to i? Do you mean array[K] to mean the actual element array[3] will always be added, or do you mean that the element at offset 3 from the current start of the circular buffer will be added?

if (p_dynamic[K] > p_end && p_dynamic[i] < p_end && p_dynamic[i+1] < p_end)

This makes no sense because p_dynamic is a pointer to an int, so p_dynamic[K] is an int, but p_end is a pointer to an int, so p_dynamic[K] > p_end attempts to compare an int to a pointer. I suspect you are attempting to ask whether the element p_dynamic[K] would be beyond the end of the array. You might do that with p_dynamic + K > p_end except, as with --p_dynamic, if it is beyond the end of the array, the pointer arithmetic is not defined by the C standard.

Simplify your code by making a function to help you access array elements. Make a structure that describes the circular buffer. To start simply, I will assume you have a fixed buffer size of Size eleemnts.

typedef struct
{
    int array[Size];
    ptrdiff_t current;
} CircularBuffer;

The member current will be the index (subscript) of the element that is currently oldest in the buffer, the current “start” of the circular buffer. (The ptrdiff_t type is defined in the header <stddef.h>. It may be used for array subscripts.

Then make a function that gives you a pointer to the element with index i:

int *CBElement(CircularBuffer *cb, ptrdiff_t i)
{
    ptrdiff_t index = i + current;
    if (Size <= index) index -= Size;
    return &cb->array[index];
}

Then you can refer to array element K, for example, as *CBElement(&cb, K):

output = *CBElement(&cb, i) + *CBElement(&cb, i+1) + *CBElement(&cb, K);

You can also put new values in the array:

*CBElement(&cb, i) = NewValue;

To advance the buffer, update the current member in the obvious way and put a new value in the new last element (*CBElement(&cb, Size-1)).

This should simplify the task of getting the code working. After it is working, you can consider optimizations and refinements of the buffer implementation.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • I edited the code accordingly. This is not an array but allocated memory. Thus I can move the pointer freely. I will try to see if I can write a function. I think this problem is too complex without the full code. But when you post the full code, people complain that you should describe it. – neolith Mar 14 '21 at 14:12
  • 1
    Re “Thus I can move the pointer freely”: The C standard does not define pointer arithmetic or comparisons outside the bounds of an array regardless of whether the array is allocated statically, automatically, or dynamically. – Eric Postpischil Mar 14 '21 at 14:45
  • I made the question easier, Thank you for the effort so far, but I just got more confused – neolith Mar 14 '21 at 14:45
  • Our prof told us to implement a circular buffer this way and it worked. I didn't know it was undefined behavior. – neolith Mar 14 '21 at 14:46
  • I am trying your solution, but since I have a dynamically allocated memory section I don't know how to implement the structure. – neolith Mar 14 '21 at 16:55
  • I tried to implement it your way. It seems like the cleanest solution, but I really don't see how to advance the current ptrdiff here. I have to go backwards and it can't take on negative values. It does advance in the positive direction but only once and then it stays at 1. – neolith Mar 14 '21 at 19:25
  • @neolith: To use the structure with a dynamically allocated array, change the `array` member to `int *array;` and initialize it to point to the dynamically allocated memory. `current` never needs to be negative. It is always the index of the “start” of the circular buffer, so it has a value from 0 up to the index of the last element in the allocated array. – Eric Postpischil Mar 14 '21 at 19:52
  • But current has to be changed somehow. I put cb.current—; and then put the element with *CBelement(&cb, 0) = new_value; I also don’t need to advance the current index, I need to decrement it to emulate the behavior of pushing all elements by one and pushing a value before them. – neolith Mar 14 '21 at 19:57
  • I think the problem is that this is not a classical circular buffer. I need to insert in the backward direction and read in the forward direction. – neolith Mar 14 '21 at 20:00