【问题标题】:Compact pointer notation with doubles带双精度的紧凑指针表示法
【发布时间】:2011-01-05 09:52:28
【问题描述】:

快速提问。当您访问字符数组时,我知道您可以将指针设置为数组中的第一个元素,然后使用一段时间查看并执行类似

的操作
while (*ptr != '\0') {
   do something
}

现在有 double 或 int 等价的吗?

#define ARRAY_SIZE 10
double someArray[ARRAY_SIZE] = {0};
double *ptr = someArray;
// then not sure what to do here?  I guess I am looking for an equivalent of the above while loop, but don't want to just do:
for (int i = 0; i < ARRAY_SIZE); *ptr++) 
cout << *ptr;

谢谢!

【问题讨论】:

    标签: c++ pointers


    【解决方案1】:

    如果我理解正确,您希望遍历数组并在 *ptr 具有特定值时停止。这并不总是可能的。对于字符数组(字符串),一个常见的约定是让字符串“以空值结尾”;也就是说,它最后会有一个 0 字节('\0')。您可以将这样的哨兵添加到 int 或双值数组中(如果您可以挑选出不会在其他情况下使用的“特殊”值),但这不是一种普遍适用的技术。

    顺便说一句,你的 for 循环可能不是你想要的:

    for (int i = 0; i < ARRAY_SIZE); *ptr++)

    如果你想遍历数组,你需要增加指针 (ptr++),而不是它指向的值 (*ptr++)。

    【讨论】:

    • *ptr++ 递增指针,而不是指向的值,尽管即使我们忽略语法错误,* 就目前而言是无用的。
    • (这里还有一个无限循环,因为你没有增加 i。)
    【解决方案2】:

    目前尚不清楚您在寻找什么。

    第一个循环的结构(“字符数组”版本)由以下事实决定:您不是使用抽象字符数组,而是使用位于其中的 C 字符串那个数组。 C 字符串是由零字符终止的字符序列(在数组中)。这个零字符会终止您的第一个周期。

    使用显式指针来处理double 数组没有问题。您在第二个示例中自己做(尽管有错误)。如果您的 double 数组特别包含由 0.0 值终止的 double 值序列,那么您的 double 循环甚至可能与您的 char 循环完全相同。

    double *ptr = someArray;
    while (*ptr != 0.0)
      cout << *ptr++;    
    

    但通常情况并非如此。零终止 double 数组在实践中并不经常使用。

    通常您会知道数组的大小,这意味着迭代将以不同的方式组织。有很多不同的方法可以做到这一点,所以这在很大程度上取决于个人品味。这里有几个例子

    double *ptr;
    for (unsigned n = ARRAY_SIZE, ptr = someArray; n > 0; --n, ++ptr)
      cout << *ptr;
    

    for (double *ptr = someArray, *ptr_end = ptr + ARRAY_SIZE; ptr != ptr_end; ++ptr)
      cout << *ptr;
    

    double *ptr;
    for (unsigned i = 0, ptr = someArray; i < ARRAY_SIZE; ++i, ++ptr)
      cout << *ptr;
    

    等等。

    【讨论】:

      【解决方案3】:

      在 C 中,字符串根据定义以 '\0' 字符结尾。有一个特殊值(在这种情况下是 '\0' 字符)指示序列的结束有时称为使用标记。在其他数据结构中使用哨兵并不少见,但绝不是通用的。

      如果您有一组以已知值终止的整数或双精度数,那么您可以使用一种技术来遍历该序列,类似于遍历字符串中的字符直到您点击 '\0'。问题是您必须确保序列正确终止与哨兵,并且您的哨兵不能是您可能在序列中找到的值作为实际数据。

      常见的标记值有:

      • 0(零)
      • -1
      • MAX_INT

      但是您可以使用对您的应用程序有意义的任何内容。假设您的应用程序仅处理非负双精度,您可以使用负值作为标记:

      double *ptr = someArray;
      
      while (*ptr >= 0.0) {
          cout << *ptr;
          ++ptr;
      }
      

      使用哨兵终止数组的好处是很容易处理变长数组。

      缺点是:

      • 您的值不能合法地用作数据(如果您决定必须处理负值,上述示例将是一个大问题)
      • 您必须努力确保您的阵列正确终止
      • 在为数组分配空间时,必须确保不要忘记为哨兵分配一个元素。
      • 您必须遍历阵列才能确定它有多大

      使用哨兵的替代方法是携带数组的大小,要么只处理编译时已知的特定大小的数组,要么将数组大小作为参数传递(或将其与数组或指向数组的指针)。

      【讨论】:

        【解决方案4】:

        (这里没有其他答案中未提及的新内容,但我将尝试简洁地提供解释和示例而不会失去清晰度。)

        循环检查'\0' 的char 数组是可行的,因为C 风格的字符串有一个int、double 和大多数其他类型没有的约定:空终止。 ('\0' 字符称为“null”或“NUL”,但很少使用“NULL”以避免与该名称的宏混淆。)

        由于 int 和 double 数组没有这个约定,你必须使用其他东西。以下是最简单的替代方案:

        // pass arrays with their size
        void ex1(double const* data, int size) {
          for (int n = 0; n < size; ++n) {
            use(data[n]);
          }
        }
        
        // use a container class which has a size() method
        void ex2(vector<double> const& v) {
          for (int n = 0; n < v.size(); ++n) {
            use(data[n]);
          }
          // or:
          for (vector<double>::const_iterator i = v.begin(); i != v.end(); ++i) {
            use(*i);
          }
          // or, sometimes a slight tweak:
          for (vector<double>::const_iterator i = v.begin(), end = v.end();
               i != end; ++i
          ) {
            use(*i);
          }
        }
        
        // pass an iterator range, once you are familiar with iterators
        void ex3(double const* begin, int const* end) {
          for (double const* i = begin; i != end; ++i) {
            use(*i);
          }
        }
        

        以及如何使用它们:

        void ex4() {
          double data[] = {3, 5, 42}; // if you don't want to specify the size, then use
          int length = len(data);  // this special len function to get the array length
          // len defined below
        
          ex1(data, length);   // easy to pass with size now
          ex2(vector<double>(data, data + length));   // easy to create a container too
          ex3(data, data + length);
            // notice the container creation takes a similar iterator range
        
          double buncha_zeros[42] = {}; // or even if you specify the length
          length = len(buncha_zeros); // you still don't have to repeat yourself
        }
        
        template<class T, int N>
        N len(T (&)[N]) {
          return N;
        }
        // note: this exists in boost in a better, more general form as boost::size
        

        用“int”或几乎任何其他类型替换“double”,这里的一切都一样。

        【讨论】:

          【解决方案5】:

          没有通用的方法可以知道数组的大小而不将数组的大小存储在某处,除非您可以保证数组中不会出现特定值(例如以空值结尾的“0”字节细绳)。

          【讨论】:

            【解决方案6】:

            是的,双精度数组的指针表示法完全相同。

            这是您的程序的更正版本:

            #include <iostream>
            
            #define ARRAY_SIZE 10
            
            int main(void) {
                double someArray[ARRAY_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
                double *ptr = someArray;
            
                for (int i = 0; i < ARRAY_SIZE; i++, *ptr++) 
                    std::cout << *ptr;
            }
            

            这个程序的输出是:

            0123456789
            

            【讨论】:

              【解决方案7】:

              对于 C 风格的字符串,约定是字符串以 0 结尾。这意味着您不能在 C 风格的字符串中包含 0

              如果您有一个不需要的 double 值,您可以将其存储在数组的末尾,并检查它。例如,您可能可以使用 0 或 NaN(不是数字)。了解如何在 CC++ 中使用 NaN。如果您确实使用非 NaN 数字作为标记,则在比较浮点数是否相等之前,您应该阅读 The pitfalls of verifying floating-point computations

              当然,由于您使用的是 C++,并且不想记住数组的大小,因此您应该考虑改用 std::vector&lt;double&gt;

              【讨论】:

              • 建议可以使用哨兵值然后列出 15 个不同的注意事项,这对于最初的糟糕解决方案似乎没有用处。只需预先提供容器类的解决方案,就可以跳过混淆。
              猜你喜欢
              • 2011-01-12
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-01-07
              • 1970-01-01
              • 2011-02-23
              • 2015-03-26
              • 1970-01-01
              相关资源
              最近更新 更多