pobu
pobu

Reputation: 402

Left shift operation on char array

I have prepared an example program that moves second element of an char array to first position, third element to second position etc.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
int main(void) {
    char *str = (char *) malloc(10);
    strcpy(str, "123456");
    printf("%s\n", str); //123456
    str++;
    printf("%s\n", str); //23456
    str++;
    printf("%s\n", str); //3456
    str++;
    printf("%s\n", str); //456
    std::cout << str[0]; //4
}

Now I would like to repeat this operation but with shift operation, but it does not really work as I expect.

#include<iostream>
int main(void){
    char tab[6] = {'1','2', '3', '4', '5', '6'};
    char *p = tab;
    for(int i = 0; i < 6; i++){
        std::cout << tab[i]; //123456
    }
    std::cout << std::endl;
    *p = *p << 8;
    std::cout << tab[0] << std::endl; //0 '\0'
    std::cout << tab[1] << std::endl; //2
    std::cout << std::endl;
    return 0;
}

If array name is a pointer to first element, I expect that left shift operation on array name would point at the second element of an array, but it does not. Any ideas how to repeat the first pointer operation with the shift operation?

Upvotes: 0

Views: 6674

Answers (7)

Francis Cugler
Francis Cugler

Reputation: 7905

After looking at your question and the code you provided; as others have stated you are not able to advance a pointer using the bit shift or stream manipulator operators. Basic pointer arithmetic would be more than enough. If you want something a little more elaborate with a bit of encapsulation then check out what I've done here by creating 2 sets of class templates. The first takes just a size_t and is always char, the 2nd takes both type T & a size_t. These are simple structs that just contain an internal array (left in public for demonstration purposes), the size, a default constructor and variadic initialization constructor. Once I created these classes, instead of performing "pointer arithmetic" I instead created both pre & post inc/dec operators for both classes. Within these operators instead of doing "pointer-arithmetic" I just used the internal arrays to manipulate the data with the behavior that you were desiring. Here is the code:

Arrays.h

#ifndef ARRAYS_H
#define ARRAYS_H

template<size_t n>
struct charArray {
    static const size_t SIZE = n;
    char arr_[SIZE];

    charArray() {}

    template<class... U>
    charArray( U ... pack ) : arr_ { pack... } {
        static_assert(sizeof...(U) <= n, "too many values");
    }
};
template<size_t n>
inline charArray<n>& operator--( charArray<n>& arr );

template<size_t n>
inline charArray<n> operator--( charArray<n>& arr, int );

template<size_t n>
inline charArray<n>& operator++( charArray<n>& arr );

template<size_t n>
inline charArray<n> operator++( charArray<n>& arr, int );

template<typename T, size_t n>
struct myArray {
    static const size_t SIZE = n;
    T arr_[SIZE];

    myArray() {}

    template<class... U>
    myArray( U ... pack ) : arr_ { pack... } {
        static_assert(sizeof...(U) <= n, "too many values");
    }

};
template<typename T, size_t n>
inline myArray<T, n>& operator--( myArray<T,n>& arr );

template<typename T, size_t n>
inline myArray<T, n> operator--( myArray<T, n>& arr, int );

template<typename T, size_t n>
inline myArray<T, n>& operator++( myArray<T, n>& arr );

template<typename T, size_t n>
inline myArray<T, n> operator++( myArray<T, n>& arr, int );

#include "Arrays.inl"

#endif // ARRAYS_H

Arrays.inl

template<size_t n>
inline charArray<n>& operator--( charArray<n>& arr ) {
    int i = 0;
    for ( ; i < arr.SIZE - 1; ++i ) {
        char temp = arr.arr_[i];
        arr.arr_[i] = arr.arr_[i + 1];
        arr.arr_[i + 1] = temp;
    }
    arr.arr_[i] = ' ';

    // return reference
    return arr;
}

template<size_t n>
inline charArray<n> operator--( charArray<n>& arr, int ) {
    charArray<n> temp = arr;
    --arr;
    return temp;
}

template<size_t n>
inline charArray<n>& operator++( charArray<n>& arr ) {
    int i = arr.SIZE-1;
    for ( ; i > 0; --i ) {
        char temp = arr.arr_[i];
        arr.arr_[i] = arr.arr_[i-1];
        arr.arr_[i-1] = temp;
    }
    arr.arr_[0] = ' ';

    // return reference
    return arr;
}

template<size_t n>
inline charArray<n> operator++( charArray<n>& arr, int ) {
    charArray<n> temp = arr;
    ++arr;
    return temp;
}

template<typename T, size_t n>
inline myArray<T, n>& operator--( myArray<T,n>& arr ) {

    int i = 0;
    for ( ; i < arr.SIZE - 1; ++i ) {
        T temp = arr.arr_[i];
        arr.arr_[i] = arr.arr_[i + 1];
        arr.arr_[i + 1] = temp;
    }
    arr.arr_[i] = static_cast<T>( 0 );

    return arr;
}

template<typename T, size_t n>
inline myArray<T, n> operator--( myArray<T, n>& arr, int ) {
    myArray<T, n> temp = arr;
    --arr;
    return temp;
}

template<typename T, size_t n>
inline myArray<T, n>& operator++( myArray<T, n>& arr ) {

    int i = arr.SIZE-1;
    for ( ; i > 0; --i ) {
        T temp = arr.arr_[i];
        arr.arr_[i] = arr.arr_[i-1];
        arr.arr_[i-1] = temp;
    }
    arr.arr_[0] = static_cast<T>(0);

    return arr;
}

template<typename T, size_t n>
inline myArray<T, n> operator++( myArray<T, n>& arr, int ) {
    myArray<T, n> temp = arr;
    ++arr;
    return temp;
}

Arrays.cpp

#include "Arrays.h";

main.cpp

#include <iostream>
#include "Arrays.h"

int main() {
    // Start with the char array, initilize it & print to screen
    std::cout << "Working with charArray<6>.\n";
    charArray<6> cArr = { 'a', 'b', 'c', 'd', 'e', 'f' };
    for ( int i = 0; i < cArr.SIZE; i++ ) {
        std::cout << cArr.arr_[i] << " ";
    }
    std::cout << std::endl;

    // Pre - Decrement
    --cArr;

    // Reprint
    std::cout << "After Pre-Decrement operator.\n";
    for ( int i = 0; i < cArr.SIZE; i++ ) {
        std::cout << cArr.arr_[i] << " ";
    }
    std::cout << std::endl;

    // Post - Decrement
    cArr--;

    // Reprint
    std::cout << "After Post-decrement operator.\n";
    for ( int i = 0; i < cArr.SIZE; i++ ) {
        std::cout << cArr.arr_[i] << " ";
    }
    std::cout << std::endl;

    // Pre - Decrement again
    std::cout << "Pre-decrement again after post-decrement.\n";
    --cArr;
    for ( int i = 0; i < cArr.SIZE; i++ ) {
        std::cout << cArr.arr_[i] << " ";
    }
    std::cout << std::endl << std::endl;

    // ==================================================================

    // Do Same for templated arrays:
    std::cout << "Working with myArray<char,6>.\n";
    myArray<char, 6> arr = { 'a', 'b', 'c', 'd', 'e', 'f' };
    for ( int i = 0; i < arr.SIZE; i++ ) {
        std::cout << arr.arr_[i] << " ";
    }
    std::cout << std::endl;

    // Pre - Decrement
    --arr;

    std::cout << "After Pre-decrement operator.\n";
    for ( int i = 0; i < arr.SIZE; i++ ) {
        std::cout << arr.arr_[i] << " ";
    }
    std::cout << std::endl;

    // Post - decrement
    arr--;

    std::cout << "After Post-decrement operator.\n";
    for ( int i = 0; i < arr.SIZE; i++ ) {
        std::cout << arr.arr_[i] << " ";
    }
    std::cout << std::endl;

    // Pre - Decrement again
    std::cout << "Pre-decrement again after post-decrement.\n";
    --arr;
    for ( int i = 0; i < arr.SIZE; i++ ) {
        std::cout << arr.arr_[i] << " ";
    }
    std::cout << std::endl << std::endl;


    // ==============================================================

    std::cout << "Working with myArray<int,4>.\n";
    myArray<int, 4> arr2 = { 1,2,3,4 };
    for ( int i = 0; i < arr2.SIZE; i++ ) {
        std::cout << arr2.arr_[i] << " ";
    }
    std::cout << std::endl;

    // Pre - Decrement
    --arr2; 

    std::cout << "After Pre-decrement operator.\n";
    for ( int i = 0; i < arr2.SIZE; i++ ) {
        std::cout << arr2.arr_[i] << " ";
    }
    std::cout << std::endl;

    // Post - Decrement
    arr2--;

    std::cout << "After Post-decrement operator.\n";
    for ( int i = 0; i < arr2.SIZE; i++ ) {
        std::cout << arr2.arr_[i] << " ";
    }
    std::cout << std::endl;

    // Pre - Decrement again
    std::cout << "Pre-decrement again after post-decrement.\n";
    --arr2;
    for ( int i = 0; i < arr2.SIZE; i++ ) {
        std::cout << arr2.arr_[i] << " ";
    }
    std::cout << std::endl << std::endl;


    std::cout << "===========================================================.\n";
    // ====================================================================


    std::cout << "Testing charArray<5> With Pre-Increment.\n";
    charArray<5> cArr2 = { 'j', 'k', 'l', 'm', 'n' };
    for ( int i = 0; i < cArr2.SIZE; i++ ) {
        std::cout << cArr2.arr_[i] << " ";
    }
    std::cout << std::endl;

    std::cout << "Pre - Increment\n";
    ++cArr2;

    std::cout << "After Pre-increment operator.\n";
    for ( unsigned i = 0; i < cArr2.SIZE; i++ ) {
        std::cout << cArr2.arr_[i] << " ";
    }
    std::cout << std::endl << std::endl;

    std::cout << "Testing myArray<float, 7> With Pre-Increment.\n";
    myArray<float, 7> fArr = { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f };
    for ( int i = 0; i < fArr.SIZE; i++ ) {
        std::cout << fArr.arr_[i] << " ";
    }
    std::cout << std::endl;

    std::cout << "Pre - Increment\n";
    ++fArr;

    std::cout << "After Pre-increment operator.\n";
    for ( unsigned i = 0; i < fArr.SIZE; i++ ) {
        std::cout << fArr.arr_[i] << " ";
    }
    std::cout << std::endl << std::endl;


    std::cout << "Post-increment operator.\n";
    cArr2++;
    fArr++;

    std::cout << "After Post-Increment.\n";
    for ( int i = 0; i < cArr2.SIZE; i++ ) {
        std::cout << cArr2.arr_[i] << " ";
    }
    std::cout << std::endl;

    for ( int i = 0; i < fArr.SIZE; i++ ) {
        std::cout << fArr.arr_[i] << " ";
    }
    std::cout << std::endl;


    // Pre-Increment again

    std::cout << "Pre-increment again.\n";
    ++cArr2;
    ++fArr;
    std::cout << "Pre-Increment after post-increment.\n";
    for ( int i = 0; i < cArr2.SIZE; i++ ) {
        std::cout << cArr2.arr_[i] << " ";
    }
    std::cout << std::endl;

    for ( int i = 0; i < fArr.SIZE; i++ ) {
        std::cout << fArr.arr_[i] << " ";
    }
    std::cout << std::endl;



    std::cout << "\nPress any key and enter to quit." << std::endl;
    char c;
    std::cin >> c;

    return 0;
}

Mind the fact that the internal arrays are public, and that all loops and [] or array access is using the internal SIZE variable as there is no array bounds checking. This just demonstrates that you can use the ++() & --() operators to do the same thing that you was describing in either order.

Now if you wanted to you could change the Pre -- & Pre ++ slightly to do a wrap around in either direction so that if you were doing --() the first element would move to the end instead of replacing it with 0 and if you are doing ++() the last element would become the first, but I'll leave that as a challenge to you.

Upvotes: 0

Eljay
Eljay

Reputation: 5321

This *p = *p << 8; does not shift the pointer, it shifts the value in the object the pointer points to. (The char is implicitly converted to an int, right-shifted by 8 bit positions, and implicitly converted back to a char. Then that value, which will be 0 on platforms that have 8-bit char, is put back into *p.)

Since it points to a char, and the char is (probably) 8-bit, I think* doing a << 8 is UB.

* 98.3% sure.

Upvotes: 0

user3629249
user3629249

Reputation: 16540

regarding:

*p = *p << 8;

this is taking the first byte of the array (which initially contained 0x31)

and shifting the byte by 8 bits (so it now contains 0x00)

Then storing the result back into the first byte of the array.

This is NOT shifting the whole array.

Upvotes: 0

Vlad from Moscow
Vlad from Moscow

Reputation: 310950

If array name is a pointer to first element, I expect that left shift operation on array name would point at the second element of an array

Arrays designators in expressions are implicitly (with rare exceptions) are indeed converted to pointers to their first elements

Thus in this declaration

char *p = tab;

the array designator tab is converted to pointer to its first element and the address of the first element is assigned to the pointer p.

However in this expression statement

*p = *p << 8;

the value of the first character, *p, pointed to by the pointer p is in fact multiplied by the value equal to 2 in power 8 that is by 256. And the value of the character is assigned to the pointer.

So after this operation the pointer has invalid value and as result the program has undefined behavior.

What you need is to copy all elements of the array starting from the index 1 to the position that corresponds to the index 0.

Also in the first program you are using a string but in the second program the array does not contain a string.

If you want to shift left elements of a character array that contains a string then the code can look as it is implemented in the function shown in the demonstrative program.

#include <iostream>
#include <cstring>

char * shift_left(char *s)
{
    size_t n = std::strlen(s);

    std::memmove(s, s + 1, n);

    return s;
}


int main()
{
    char s[] = "123456";

    std::cout << s << std::endl;

    for (size_t i = 0, n = std::strlen(s); i != n; i++ )
    {
        std::cout << shift_left( s ) << std::endl;

    }

    return 0;
}

The program output is

123456
23456
3456
456
56
6

Upvotes: 1

lsouza
lsouza

Reputation: 11

The problem is that there is no relation between bit shifting and pointer arithmetic.

First, let's see why the first code works and the second does not:

Imagine I have a pointer called 'p' with value 4 pointing to the first position of the array {e,x,a,m,p,l,e}. This means that you print the values of p and *p you will get "4" and "e" respectively. This happens because 'e' is the value your pointer is pointing to and '4' is the memory address your pointer is pointing to. If you add +1 to the value of the pointer, you are going to point to the second position of this array. Hence, if you print the values of p and *p for a second time, you get "5" and "x" respectively.

When you write the command line *p = *p << 8 you are actually shifting the value you are pointing to by 8 bits for the left. This means that if p is pointing to the first position, the 'e' (with ASCII value equal to 101 in decimal or 0x65 in hexadecimal base) would be shifted by 8 bits and become a 0x0, which is a null character. The value of the pointer however, would remain the same. If you print p and *p values you would get "4" and null values respectively.

On the other hand, if you wrote p = p << 8 you would change the pointer value (and consequently what it is pointing to). If p had value "4", it would become a "1024" after the shift.

The functionality you want is much easier achievable with pointer arithmetic than with bit shifting.

Upvotes: 0

Killzone Kid
Killzone Kid

Reputation: 6240

I was gonna suggest to use std::swap, but I guess you are not looking for an easy way, so here it goes:

#include <iostream>

int main()
{
    int const size = 6;
    char tab[size] = { '1', '2', '3', '4', '5', '6' };
    int i = 0;
    for (; i < size - 1; ++i)
    {
        char temp = tab[i];
        tab[i] = tab[i + 1];
        tab[i + 1] = temp;
    }
    tab[i] = 0;

    std::cout << tab[0] << std::endl;
    std::cout << tab[1] << std::endl;

    return 0;
}

Prints:

2
3

The idea is to swap 0 with 1, 1 with 2, 2 with 3...etc until first element is last, then make it a null terminator. Or maybe you don't want null terminator, then just comment out tab[i] = 0;

Upvotes: 0

R Sahu
R Sahu

Reputation: 206577

If array name is a pointer to first element, I expect that left shift operation on array name would point at the second element of an array, but it does not.

That expectation is ill-founded. Bit shift operations are valid for integral types, not pointer types.

From https://timsong-cpp.github.io/cppwp/n3337/expr.shift#1:

The operands shall be of integral or unscoped enumeration type and integral promotions are performed.

Any ideas how to repeat the first pointer operation with the shift operation?

You should not attempt to do this. I am positive it is undefined behavior.

Iterating over an array is best accomplished using a pointer or an index to the elements of the array.

Upvotes: 0

Related Questions