开发者

Compact pointer notation with doubles

开发者 https://www.devze.com 2022-12-17 13:31 出处:网络
Quick question.When you are accessing a character array, I know you can set the pointer to the first element in the array, and use a while look and do something like

Quick question. When you are accessing a character array, I know you can set the pointer to the first element in the array, and use a while look and do something like

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

Now is there a double or int equivalent?

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

thank开发者_StackOverflows!


If I understand you correctly, you want to iterate through the array and stop when *ptr has a certain value. That's not always possible. With a character array (string), a common convention is to have the string be "null-terminated"; that is, it will have a 0 byte ('\0') at the end. You can add such a sentinel to an int or double-valued array (if you can single out a "special" value that won't otherwise be used), but it's not a generally applicable technique.

By the way, your for-loop is probably not what you want:

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

If you want to iterate through the array, you'll need to increment the pointer (ptr++), not the value to which it points (*ptr++).


It is not exactly clear what you are looking for.

The structure of the first cycle (the "character array" version) is determined by the fact that you are not working with an abstract character array, but rather working with a C-string residing in that array. A C-string is a sequence of characters (in an array) terminated by a zero character. This zero character is what terminates your first cycle.

There's no problem with using an explicit pointer to work with a double array. You are doing it yourself in your second example (albeit with errors). If your double array specifically contains a sequence of double values terminated by a 0.0 value, then your double cycle might even look exactly as your char cycle.

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

But normally it won't be the case. Zero-terminated double arrays is not something that is used very often in practice.

Normally you'll know the size of your array, which means that the iteration will be organized differently. There are many different ways to do it, so it is largely a matter of personal taste. Here are several examples

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

or

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

or

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

and so on.


In C a string is terminated by a '\0' character by definition. Having a special value (in this case the '\0' character) indicate the end of a sequence is sometimes known as using a sentinel. It's not uncommon to use sentinels in other data structures, but it's by no means universal.

If you have a set of ints or doubles that terminate with a known value, then you can use a technique to walk through that sequence similar to walking though the characters in a string until you hit '\0'. The catch is that you have to ensure the sequence it properly terminated with the sentinel, and your sentinel cannot be a value that you might find in the sequence as an actual piece of data.

Common sentinel values are:

  • 0 (zero)
  • -1
  • MAX_INT

but you can use whatever makes sense for your application. Say your application deals only with non-negative doubles, you could use a negative value as a sentinel:

double *ptr = someArray;

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

The advantage of using sentinels to terminate an array is that it's easy to deal with variable length arrays.

The drawbacks are:

  • you have a value that can't be legitimately used as data (the above example is a big problem if you decide you do have to handle negative values)
  • you must take pains that your arrays are properly terminated
  • when allocation the space for the array, you have to make sure not to forget to allocate an element for the sentinel.
  • you have to walk the array to figure out how big it is

The alternative to using a sentinel is to carry the array's size around, either by dealing only with arrays of a certain size known at compile time or by passing the array size as a parameter (or packaging it in a structure along with the array or pointer to the array).


(Nothing new here that isn't said in other answers, but I'm going to try to succinctly provide explanation and examples without losing clarity.)

Looping through a char array checking for '\0' works because C-style strings have a convention that int, double, and most other types don't have: null-termination. (That '\0' character is called "null" or "NUL", but rarely "NULL" to avoid confusion with the macro by that name.)

Since int and double arrays do not have this convention, you must use something else. Here are the simplest alternatives:

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

And how you can use them:

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

Replace "double" with "int", or almost any other type, and everything here works the same.


There is no general purpose way of knowing the size of an array without storing the size of the array somewhere, unless you can guarantee a particular value will not occur in the array (such as the "0" byte in a null terminated string).


Yes, there is exactly the same pointer notation with arrays of doubles.

Here's a corrected version of your program:

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

The output of this program is:

0123456789


For C-style strings, the convention is that a string ends with a 0. This means that you can't have a 0 in a C-style string.

If you have a double value that you don't need, you can store that at the end of your array, and check for it. For example, you might be able to use 0, or NaN (Not a number). See how to use NaN in C or in C++. If you do use a non-NaN number as a sentinel, you should read The pitfalls of verifying floating-point computations before you compare floating point numbers for equality.

Of course, since you're using C++, and you don't want to remember your array's size, you should think about using std::vector<double> instead.

0

精彩评论

暂无评论...
验证码 换一张
取 消