Home > OS >  How to get number of elements of string array via a pointer to a pointer
How to get number of elements of string array via a pointer to a pointer

Time:10-12

See Last line of code: Trying to get number of elements from a array depending on a condition. How do i get the number of elements in an array when using an array like shown below:

using namespace std;
const char *options[3] = {"Option1", "Option2", "Option3"};
const char *options2[2] = {"Option1", "Option2"};

int main() {
  const char **p;

  if (true) {
    p = options;
  } else {
    p = options2;
  }

  printf("%ld\n", sizeof(options));               // returns 24
  printf("%ld\n", sizeof(options2));              // Returnns 16
  printf("%ld\n", sizeof(options) / sizeof(p));   // returns 3
  printf("%ld\n", sizeof(options2) / sizeof(p));  // returns 2

  // How to use only pointer p to get the number of elements
  printf("%ld\n", sizeof(p) / sizeof(p[0]));  // returns 1 and not 3
  return 0;
}

CodePudding user response:

Sorry, but it is simply not possible to get an array's size from just a raw pointer to an array element.

One option is to store the array size in a separate variable alongside the array pointer, eg:

#include <iostream>

const char *options[3] = {"Option1", "Option2", "Option3"};
const char *options2[2] = {"Option1", "Option2"};

int main() {
  const char **p;
  size_t p_size;

  if (some condition is true) {
    p = options;
    p_size = sizeof(options) / sizeof(options[0]); // or better: std::size(options) in C  17 and later
  } else {
    p = options2;
    p_size = sizeof(options2) / sizeof(options2[0]); // or better: std::size(options2)
  }

  std::cout << sizeof(options) << "\n"; // returns 24
  std::cout << sizeof(options2) << "\n"; // returns 16
  std::cout << p_size << "\n"; // returns 3 or 2, based on condition

  return 0;
}

In C 20 and later, you can use std::span instead (in C 14 and C 17, you can use gsl::span from the GSL library), eg:

#include <iostream>
#include <span>

const char *options[3] = {"Option1", "Option2", "Option3"};
const char *options2[2] = {"Option1", "Option2"};

int main() {
  std::span<const char*> p;

  if (some condition is true) {
    p = options;
  } else {
    p = options2;
  }

  std::cout << sizeof(options) << "\n"; // returns 24
  std::cout << sizeof(options2) << "\n"; // returns 16
  std::cout << p.size() << "\n"; // returns 3 or 2, based on condition

  return 0;
}

CodePudding user response:

If you're going to write C , write C . You're starting from one of C's more questionable decisions, and then trying to force C to do the same thing. To twist Nike's phrase, "Just don't do it!"

std::array<char const *, 3> options {"Option1", "Option2", "Option3"};
std::array<char const *, 2> options2 {"Option1", "Option2"};

This makes it easy to retrieve the size of the array in question--and the size is part of the type, so all the work happens at compile time, so it imposes no runtime overhead. For example, if you do something like this:

template <class Array>
void showSize(Array const &a) {
    std::cout << a.size() << "\n";
}

int main() { 
    showsize(options);
    showSize(options2);
}

...what you'll find is that the compiler just generates code to write out the literal 3 or 2:

    mov     esi, 3    // or `mov esi, 2`, in the second case
    call    std::operator<<(std::ostream &, unsigned long)

[I've done a bit of editing to demangle the name there, but that's what the code works out to.]

Here's the un-edited version, in case you care.

If you really insist, you can side-step using an std::array as well:

const char *options[3] = {"Option1", "Option2", "Option3"};
const char *options2[2] = {"Option1", "Option2"};

template <class T, size_t N>
void showSize(T (&array)[N]) {
    std::cout << N << '\n';
}

int main() {
    showSize(options);
    showSize(options2);
}

This doesn't actually use a pointer though--it passes the array by reference, which retains its type information, so the instantiated function template "just knows" the size of the array over which it was instantiated.

20 years ago, I'd have said this was a good way to do things. 10 years ago, I'd have preferred std::array, but realized it was new enough some compilers didn't include it yet. Nowadays, unless you really need to use an ancient (Pre-C 11) compiler, I'd use std::array.

  •  Tags:  
  • c
  • Related