char数组的左移操作

Left shift operation on char array

我准备了一个示例程序,将 char 数组的第二个元素移动到第一个位置,将第三个元素移动到第二个位置等。

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

现在我想重复这个操作,但是用移位操作,但它并没有像我预期的那样真正起作用。

#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 '[=11=]'
    std::cout << tab[1] << std::endl; //2
    std::cout << std::endl;
    return 0;
}

如果数组名是指向第一个元素的指针,我希望对数组名的左移操作会指向数组的第二个元素,但事实并非如此。任何想法如何用移位操作重复第一个指针操作?

这个*p = *p << 8;不移动指针,它移动指针指向的对象中的值。 (char 被隐式​​转换为 int,右移 8 位位置,然后隐式转换回 char。然后该值(在具有 8 位 char 的平台上为 0)被放回 *p.)

因为它指向一个字符,并且字符(可能)是 8 位的,我认为*做一个 << 8 是 UB。

* 98.3% 肯定。

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.

这种期望是没有根据的。位移位操作对整数类型有效,对指针类型无效。

来自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?

您不应尝试这样做。我肯定这是未定义的行为。

最好使用数组元素的指针或索引来迭代数组。

我本来建议使用 std::swap,但我猜你不是在寻找简单的方法,所以这里是:

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

打印:

2
3

想法是将 0 与 1、1 与 2、2 与 3 等交换,直到第一个元素成为最后一个元素,然后将其设为空终止符。或者也许你不想要空终止符,然后注释掉 tab[i] = 0;

问题是移位和指针运算之间没有关系。

首先,让我们看看为什么第一个代码有效而第二个无效:

假设我有一个名为 'p' 的指针,其值为 4,指向数组 {e,x,a,m,p,l,e} 的第一个位置。这意味着您打印 p 和 *p 的值,您将分别得到“4”和 "e"。发生这种情况是因为 'e' 是您的指针指向的值,而“4”是您的指针指向的内存地址。如果将指针的值加 +1,则指向该数组的第二个位置。因此,如果您第二次打印 p 和 *p 的值,您将分别得到“5”和 "x"。

当您编写命令行 *p = *p << 8 时,您实际上是将指向的值向左移动 8 位。这意味着如果 p 指向第一个位置,则 'e' (ASCII 值等于十进制的 101 或十六进制的 0x65)将被移动 8 位并且变为 0x0,这是一个空字符。然而,指针的值将保持不变。如果您打印 p 和 *p 值,您将分别得到“4”和 null 值。

另一方面,如果您编写 p = p << 8,您将更改指针值(以及它指向的内容)。如果 p 的值为“4”,它将变为 移位后的“1024”。

您想要的功能使用指针算法比使用位移更容易实现。

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

表达式中的数组指示符被隐式地(除了极少数例外)确实被转换为指向其第一个元素的指针

因此在这个声明中

char *p = tab;

数组指示符 tab 被转换为指向其第一个元素的指针,第一个元素的地址被分配给指针 p

然而在这个表达式语句中

*p = *p << 8;

指针p指向的第一个字符*p的值实际上乘以等于2的8次方的值,即乘以256。而值字符被分配给指针。

所以在此操作之后指针具有无效值,因此程序具有未定义的行为。

你需要的是将数组中从索引1开始的所有元素复制到索引0对应的位置。

同样在第一个程序中,您使用的是字符串,但在第二个程序中,数组不包含字符串。

如果您想要左移包含字符串的字符数组的元素,那么代码看起来就像在演示程序中显示的函数中实现的那样。

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

程序输出为

123456
23456
3456
456
56
6

关于:

*p = *p << 8;

这是获取数组的第一个字节(最初包含 0x31)

并将字节移动 8 位(因此它现在包含 0x00)

然后将结果存储回数组的第一个字节。

这不是移动整个数组。

在查看了您的问题和您提供的代码之后;正如其他人所说,您无法使用位移位或流操纵器运算符来推进指针。基本的指针运算就足够了。如果您想要一些更精细的封装,那么请查看我在此处创建的 2 套 class 模板所做的工作。第一个只需要 size_t 并且总是 char,第二个需要 type Tsize_t。这些是简单的结构,只包含一个内部数组(出于演示目的留在 public 中)、大小、默认构造函数和可变初始化构造函数。创建这些 classes 后,我没有执行 "pointer arithmetic",而是为两个 classes 创建了两个 pre & post inc/dec operators。在这些运算符中,我没有执行 "pointer-arithmetic",而是使用内部数组以您想要的行为来操作数据。这是代码:

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

请注意内部数组是 public,并且所有循环和 [] 或数组访问都使用内部 SIZE 变量,因为没有数组边界检查。这只是表明您可以使用 ++() & --() operators 来执行您在任一顺序中描述的相同内容。

现在,如果您愿意,您可以稍微更改 Pre --Pre ++ 以在任一方向上环绕,这样如果您正在做 --(),第一个元素就会移动到最后而不是用 0 替换它,如果你正在做 ++() 最后一个元素将成为第一个元素,但我会把它作为一个挑战留给你。