0

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;
}
11
  • 2
    Dividing sizeof(options) by sizeof(p) doesn't make sense. It's a different pointer type from *options. Sure, it may give the desired result, but it's extremely misleading. Use sizeof(options) / sizeof(*options). As for using p to get the size, you can't. p is just a pointer. Its size doesn't depend on its value. sizeof(p)/sizeof(p[0]) is going to be sizeof(char **)/sizeof(char *), which is a constant, and is going to be 1 on any normal architecture. Commented Oct 12, 2022 at 2:36
  • I think it's impossible in C. There might be a way to do it in C++, by means of a template trick. Commented Oct 12, 2022 at 2:38
  • 1
    C++ is not C. Please don't tag C. Commented Oct 12, 2022 at 2:40
  • What you're asking for is not possible (not in C that is, I didn't notice you're using C++). options and options2 are statically declared arrays, but p is declared simply as a pointer and stores no information about the memory it is pointing to, and sizeof(p) just returns the size of an integer. Commented Oct 12, 2022 at 2:42
  • 1
    @DavidC.Rankin "you can get the array size" - no, you can't. All size information is lost when using a raw pointer to an array element. There is no way at all to extract the array size via such a pointer. Which is exactly what the OP is asking for. Commented Oct 12, 2022 at 3:24

2 Answers 2

1

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;
}
Sign up to request clarification or add additional context in comments.

Comments

1

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.

1 Comment

Thanks for the indepth explanation. I come from a C/embedded C environment where I have not used more of the tradtinal solutions used in C++. This made things more easy to understand. I will need to review my codebase and see where I can improve on this. Thanks <3

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.