【问题标题】:Left shift operation on char arraychar数组的左移操作
【发布时间】:2017-11-25 22:19:42
【问题描述】:

我已经准备了一个示例程序,它将 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
}

现在我想重复这个操作,但使用 shift 操作,但它并没有像我预期的那样工作。

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

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

【问题讨论】:

  • 根据您的用途,我可以为您提供“吸盘解决方案”;制作第二个名为“start”的指针,并使其每次都向所需方向移动(即使其++)。优点:恒时操作。缺点:实际上并没有移动东西。
  • 位移和指针没有关系,如果这就是你要问的。
  • 好的,但我想使用位移操作而不是指针++。
  • 这不是位移的用途。这就像试图开车上班一样。
  • 为什么要使用位移而不是指针?

标签: c++ arrays string char shift


【解决方案1】:

如果数组名是指向第一个元素的指针,我希望左移 对数组名的操作将指向数组的第二个元素

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

因此在此声明中

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

【讨论】:

    【解决方案2】:

    如果数组名是指向第一个元素的指针,我希望对数组名的左移操作会指向数组的第二个元素,但事实并非如此。

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

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

    操作数应为整数或非范围枚举类型,并执行整数提升。

    任何想法如何用移位操作重复第一个指针操作?

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

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

    【讨论】:

      【解决方案3】:

      我本来建议使用 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;

      【讨论】:

        【解决方案4】:

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

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

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

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

        另一方面,如果你写了p = p &lt;&lt; 8,你会改变指针的值(以及它指向的东西)。如果 p 的值为“4”,它将变为 移位后的“1024”。

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

        【讨论】:

          【解决方案5】:

          关于:

          *p = *p << 8;
          

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

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

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

          这不是移动整个数组。

          【讨论】:

            【解决方案6】:

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

            由于它指向一个字符,并且字符(可能)是 8 位的,我认为 * 执行 &lt;&lt; 8 是 UB。

            * 98.3% 肯定。

            【讨论】:

            • 如果不是因为char 在任何算术运算之前被提升为int,那将是UB。
            • 感谢星星带来 1.7% 的不确定性!
            • “它改变了指针指向的内容”的措辞不清楚。我建议“它移动指针指向的对象中的值。”
            【解决方案7】:

            查看您的问题和您提供的代码后;正如其他人所说,您无法使用位移或流操作符来推进指针。基本的指针算术就绰绰有余了。如果你想要一些更精细的封装,然后通过创建 2 组类模板来查看我在这里所做的事情。第一个只需要size_t 并且始终是char,第二个需要type Tsize_t。这些是简单的结构,只包含一个内部数组(出于演示目的而保留为公共)、大小、默认构造函数和可变参数初始化构造函数。一旦我创建了这些类,我没有执行“指针算术”,而是为这两个类创建了 pre &amp; post inc/dec operators。在这些运算符中,我没有执行“指针算术”,而是使用内部数组以您想要的行为来操作数据。代码如下:

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

            ma​​in.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;
            }
            

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-01-27
              • 2019-08-16
              • 2014-11-16
              • 1970-01-01
              • 1970-01-01
              • 2020-04-14
              • 2019-09-06
              相关资源
              最近更新 更多