This question already has an answer here:
When passing an array to a function in C++, why won't sizeof() work the same as in the main function?
(1 answer)
Closed 1 year ago.
Forgive me for this possibly dumb question. Consider this:
int foo(int* arr) {
std::cout << arr << "(" << sizeof(arr) << ")";
}
int main()
{
int x[] = {0, 1, 2, 3, 4};
foo(x);
std::cout << " " << x << "(" << sizeof(x) << ")";
}
Output: 0x7c43ee9b1450(8) 0x7c43ee9b1450(20) - Same address, different size.
My understanding is that the function argument is an address specific to the first element of the array, so the size is 8 bytes, and the same should be true for the variable in main too; So how come the size of the variable outside of the function represent the whole array (4 bytes int times 5 elements = 20)? How could I possibly determine from inside the function how large an array actually is?
This is because the types are not the same inside and out side the function.
If you make sure the type is the same inside and outside the function you should get the same result.
int foo(int (&arr)[5])
{
std::cout << arr << "(" << sizeof(arr) << ")";
return 0;
}
The problem is that arrays decay into pointers at the drop of a hat. So if you pass an array to a function it will easily be converted into a pointer. That is what is happening here.
int foo(int* arr)
// ^^^^ Notice this is not an array.
// It is simply a pointer to an integer
// The array has decayed into a pointer to the
// first element in the array.
{
std::cout << arr << "(" << sizeof(arr) << ")";
return 0;
}
How could I possibly determine from inside the function how large an array actually is?
This is actually a real problem with C. In C they solved this by getting you to pass the size of the array as a second parameter:
int foo(int* arr, std::size_t size);
Then call it from main as:
foo(arr, sizeof(arr)/sizeof(arr[0])); // This always works as it done
// at compile time and not runtime
In C++ we don't usually use C-arrays but prefer std::vector or std::array as the size is easily retrievable. Generally we use a container type C as they are duck types of Container:
template<typename C>
int foo(C& container)
{
std::cout << "(" <<container.size() << ")";
return container.size();
}
When passing an array like that you loose ALL the size information.
So the called function is flying blind with regard to the array size.
In C++ is makes much more sense to use std::array (fixed size arrays), and std::vector (changing size arrays). It is much clearer what your intent is when you pass them to functions. There will be less mistakes and less memory access issues in your code.
I hope I don't scare you too much with the template version.
The vector variant is more easy, but may use a bit more memory at runtime.
#include <array>
#include <vector>
#include <iostream>
template<size_t N>
size_t foo(const std::array<int,N>& arr)
{
for (const auto n : arr) std::cout << n << " ";
std::cout << "(" << arr.size() << ")" << std::endl;
return arr.size();
}
size_t foo(const std::vector<int>& arr)
{
for (const auto n : arr) std::cout << n << " ";
std::cout << "(" << arr.size() << ")" << std::endl;
return arr.size();
}
int main()
{
std::array<int,5> x{ 0, 1, 2, 3, 4 };
std::vector<int> y{ 0, 1, 2, 3, 4 };
auto size_x = foo(x);
auto size_y = foo(y);
}
Related
This question already has answers here:
What is the purpose of sized array as function argument is c and c++?
(2 answers)
Closed 1 year ago.
Does defining a sized array in a function signature (as opposed to the more commonly used unsized array or pointer syntax) have any bearing at all? My compiler is ignoring it completely, as the following sample code shows (which runs, although it prints some garbage values when a smaller-sized array is passed).
#include <iostream>
using namespace std;
void printArray(int intArray[5]) {
for (int i = 0; i < 5; i++) {
cout << intArray[i] << " ";
}
cout << endl;
}
int main()
{
int array1[1] = {1}; // Smaller array size than in the function signature
cout << "\nInvocation 1\n";
printArray(array1);
int array2[4] = {1, 2, 3, 4}; // Smaller array size than in the function signature
cout << "\nInvocation 2\n";
printArray(array2);
int array3[8] = {1, 2, 3, 4, 5, 6, 7, 8}; // Larger array size than in the function signature
cout << "\nInvocation 3\n";
printArray(array3);
return 0;
}
Does defining a sized array in a function signature (as opposed to the more commonly used unsized array or pointer syntax) have any bearing at all?
No, not the way you do it.
This would on the other hand make intArray a reference to the array you pass as an argument:
void printArray(int (&intArray)[5]) {
for (int i = 0; i < 5; i++) {
cout << intArray[i] << " ";
}
cout << endl;
}
A more generic form would be to make it a function template. This would then be instantiated once for each array size you call it with:
template<std::size_t N>
void printArray(int (&intArray)[N]) {
for (int i = 0; i < N; i++) {
cout << intArray[i] << " ";
}
cout << endl;
}
Does defining a sized array in a function signature (as opposed to the more commonly used unsized array or pointer syntax) have any bearing at all?
No, it has not, the passed array argument will always decay to a pointer to its first element, placing a size is indeed pointless from the compiler standpoint, it will ignore it, having void printArray(int intArray[5]){...}, void printArray(int intArray[]){...} or void printArray(int* intArray){...} will be basically the same.
My compiler is ignoring it completely, as the following sample code shows (which runs, although it prints some garbage values when a smaller-sized array is passed).
Which makes sense because in the loop you are accessing elements outside the bounds of the array, so the behavior is undefined.
This is how I would do it using std::array
#include <array>
#include <iostream>
template<typename type_t, std::size_t N>
void printArray(const std::array<type_t,N>& values)
{
for (const auto& value : values)
{
std::cout << value << " ";
}
std::cout << std::endl;
}
void printArray_s(const std::array<int, 5>& values)
{
for (const auto& value : values)
{
std::cout << value << " ";
}
std::cout << std::endl;
}
// helper function to avoid having to type value of N in std::array (syntactic sugar)
// used for array3
template<typename array_type, typename... args_t>
constexpr auto make_array(const args_t&&... values)
{
return std::array<array_type, sizeof...(args_t)>{ static_cast<array_type>(values)... };
}
int main()
{
// Smaller array size than in the function signature
std::array<int,1> array1{ 1 };
std::cout << "\nInvocation 1\n";
//printArray_s(array1); // <== will correctly give compiler error
// Smaller array size than in the function signature
std::array<int, 4> array2{ 1, 2, 3, 4 };
std::cout << "\nInvocation 2\n";
printArray(array2);
// Larger array size than in the function signature
auto array3 = make_array<int>(1, 2, 3, 4, 5, 6, 7, 8 );
std::cout << "\nInvocation 3\n";
printArray(array3);
return 0;
}
I had the online coding interview today and I really struggled while trying to calculate the size of the array.
Could you please help me with how can I measure the sizeof array here? I tried my best but no luck please help here.
#include<iostream>
#include<map>
#include<vector>
using namespace std;
void arraysize(int* a) {
cout << "size1: "<<sizeof(a) << endl;
cout << "size2: " << sizeof(a[0]) << endl;;
}
int main()
{
int array1[] = { 1,2,3,4,5,6,7,8 };
arraysize(array1);
return 0;
}
Result:
size1: 4
size2: 4
In most cases, when you pass an array to a function, the array will be converted to a pointer. This is called an array-to-pointer decay. Once this decay happens, you lose the size information of the array. That is, you can no longer tell the size of the original array from the pointer.
However, one case in which this conversion / decay will not happen is when we pass a reference to the array. We can take advantage of this property to get the size of an array.
#include<iostream>
template<typename T, size_t N>
size_t asize(T (&array)[N])
{
return N;
}
int main()
{
int array1[] = { 1,2,3,4,5,6,7,8 };
std::cout << asize(array1) << std::endl; // 8
return 0;
}
In the above case, to the template function asize, we pass a reference to an array of type T[N], whose size is N. In this case, it is array type int[8]. So the function returns N, which is size 8.
C style array's decay to pointer's when passed to a function like this.
The first cout statement is printing the size of a pointer on your machine.
The second cout statement is printing the size of an integer.
Use one of the following solutions in order to pass the size of the array to the function.
template<std::size_t N>
void ArraySize( int ( &array )[ N ] )
{
std::cout << "Array size: " << N << '\n';
}
void ArraySize( int* array, std::size_t size )
{
std::cout << "Array size: " << size << '\n';
}
template<std::size_t N>
void ArraySize( std::array<int, N>& array )
{
std::cout << "Array size: "<< array.size( ) << '\n';
}
sizeof(a) returns the number of bytes in array,
sizeof(int) returns the number of bytes in an int,
ergo
sizeof(a)/sizeof(int) returns the array length
Easiest way to get the size of an array:
#include <iostream>
using namespace std;
int main(void) {
int ch[5], size;
size = sizeof(ch) / sizeof(ch[0]);
cout << size;
return 0;
}
Output: 5
simply divide sizeof(array1) by sizeof(int). it will give you total element in array. because sizeof(array1) will give total bytes in the array. for example sizeof(array1) = int * 8 because your array is int so int is 4 byte answer is 4*8 = 32.Now you have to divide it again by 4 cause its in byte.
cout << "Size of the Array is : " << sizeof(array1)/sizeof(int) << endl;
put above code in your main function to get result
I am looking for better solutions on how to organize and access my data.
My data is a set of structures (_array_10 and _array_20 in the example below) that contain std::array of different sizes (see my_data below).
Ideally, I would like to access it as it was an array of structs with different lengths, but this is not allowed, since different lengths are different types.
The solution I have below works, but I find it extremely ugly (specially the array of void *).
Q1. Any ideas on how to have a safer, more efficient/portable, or at least less ugly solution?
Q2. Is the proposed solution without templates portable? It relies on the fact that the length is stored before the rest of the data, since casting the pointer to an object with wrong length would mess the access to all fields that come after the first field of variable length.
My limitations include:
C++11
standard libraries
no std::vector
memory usage prevents me from being able to simply allocate an array of my_data with the maximum possible length
the bulk of the data (_array_10, _array_20, etc) will be placed in a memory area reserved specially for it
Using data_view and template require knowledge of the length of the arrays in build time. It would be great if we could avoid it.
Question edited to include the solution proposed by Guillaume Racicot
#include <iostream>
#include <array>
std::array<void *, 2> _ptrs;
template <int length>
struct my_data
{
int array_length;
std::array<int, length> something;
std::array<int, length> data;
my_data()
{
array_length = length;
}
};
struct my_data_view
{
int array_length;
const int * something;
const int * data;
template <int length>
my_data_view(my_data<length> const & data_in) :
array_length(length),
something(data_in.something.data()),
data(data_in.data.data())
{}
};
template <int length>
void
print_element(int array_idx, int element)
{
my_data<length> * ptr = reinterpret_cast<my_data<length> *>(_ptrs[array_idx]);
std::cout << "array " << length << ", data[" << element << "] = " << ptr->data[element] << ".\n";
}
void
print_element(int array_idx, int element)
{
my_data<1> * ptr = reinterpret_cast<my_data<1> *>(_ptrs[array_idx]);
int length = ptr->array_length;
int data_to_print = 0;
switch (length)
{
case 10:
{
data_to_print = reinterpret_cast<my_data<10> *>(_ptrs[array_idx])->data[element];
break;
}
case 20:
{
data_to_print = reinterpret_cast<my_data<20> *>(_ptrs[array_idx])->data[element];
break;
}
}
std::cout << "array " << length << ", data[" << element << "] = " << data_to_print << ".\n";
}
void
print_element(my_data_view view, int element)
{
int length = view.array_length;
int data_to_print = view.data[element];
std::cout << "array " << length << ", data[" << element << "] = " << data_to_print << ".\n";
}
int
main()
{
my_data<10> _array_10;
my_data<20> _array_20;
_ptrs[0] = static_cast<void *>(&_array_10);
_ptrs[1] = static_cast<void *>(&_array_20);
_array_10.data[5] = 11;
_array_20.data[5] = 22;
std::cout << "using template\n";
print_element<10>(0, 5);
print_element<20>(1, 5);
std::cout << "\nwithout template\n";
print_element(0, 5);
print_element(1, 5);
std::cout << "\nusing data_view\n";
print_element(my_data_view(_array_10), 5);
print_element(my_data_view(_array_20), 5);
}
You could create a dynamic view class that don't allocate:
struct my_data_view
{
int array_length;
std::span<int> something;
std::span<int> data;
template<int length>
my_data_view(my_data<length> const& data) :
array_length{length}, something{data.something}, data{data.data}
{}
};
Spans simply are a pointer and a size. If you don't have access to std::span (which is from C++20) you can simply replace those member with int* and use array_length for the size.
This my_data_view type is used like that:
void
print_element(my_data_view view, int element)
{
int length = view.array_length;
int data_to_print = view.data[element];
std::cout << "array " << length << ", data[" << element << "] = " << data_to_print << ".\n";
}
This is the code that will work both with std::span and simple int*.
This question already has answers here:
error: no matching function for call to 'begin(int*&)' c++
(3 answers)
Closed 6 years ago.
I'd like to get length of an array. I have this code:
#include <iterator>
#include <iostream>
#include <string>
using namespace std;
void printArrLen(int arr[]);
int testArr [3] = {1, 4, 5};
int main() {
printArrLen(testArr);
cout << "main/testArr; Memory address: " << testArr << ", value: " << *testArr << endl;
cout << end(testArr) << endl;
}
void printArrLen(int arr[]) {
cout << "printArrLen/arr; Memory address: " << arr << ", value: " << *arr << endl;
cout << "printArrLen/testArr; Memory address: " << testArr << ", value: " << *testArr << endl;
// This works:
cout << end(testArr) << endl;
// This doesn't work - no matching function for call to 'end(int*&)':
// cout << end(arr) << endl;
// Doesn't work:
// cout << "arrLen: " << end(arr) - begin(arr) << endl;
}
Output:
printArrLen/arr; Memory address: 0x601318, value: 1
printArrLen/testArr; Memory address: 0x601318, value: 1
0x601324
main/testArr; Memory address: 0x601318, value: 1
0x601324
Uncommenting cout << end(arr) << endl; in printArrLen gives no matching function for call to 'end(int*&)'
I'm aware that begin/end(arr) wont work if arr is a pointer.
Why do they work on testArr in printArrLen and main, if the testArr seems to be a pointer too? How it can be proven in printArrLen that testArr is not a pointer and arr is while they both seem to contain a memory address?
I'm aware that begin/end(arr) wont work if arr is a pointer. Why do they work on testArr in printArrLen and main, if the testArr seems to be a pointer too?
Pointers are not arrays.
In your code,
testArr is array which has been initialised with 3 elements.
arr is array parameter. Array parameter is special in the sense that the array decays to pointer, so what is actually passed to the function printArrLen is a pointer (to arr first element).
Btw, you don't have to provide array size when you initialise it. This would do as well (and better):
int testArr[] = {1, 4, 5};
I'd like to get length of an array.
[...]
void printArrLen(int arr[]);
Not like this. arr in this function is actually a pointer to the first element of the array. Or more precisely, it may point to the first element of an array. It's just an int* and could point anywhere. In any case, there is no size information anymore inside of the function. You simply cannot get it.
The [] syntax is just there to confuse you. But don't take my word on it -- ask your compiler by trying this piece of code:
void printArrLen(int arr[]) {}
void printArrLen(int* arr) {}
You will see that it will complain about a redefinition.
int testArr [3] = {1, 4, 5};
testArr, in contrast to the arr parameter above, is an array, and carries the size information in its type.
int main() {
printArrLen(testArr);
Here you pass to the function a pointer to the first element of testArr, i.e. an int* pointing to the "1" element.
// This works:
cout << end(testArr) << endl;
Because testArr is an array.
// This doesn't work - no matching function for call to 'end(int*&)':
// cout << end(arr) << endl;
Because arr is a pointer.
Use std::vector if the array's size is only known at runtime, or std::array if it's already known at compile time. Both containers always know their own size.
How it can be proven in printArrLen that testArr is not a pointer
and arr is while they both seem to contain a memory address?
This question makes less sense than it seems.
Consider this:
int main()
{
int i = 0;
double d = 0.0;
}
Now, how can you "prove" that i is not a double but d is?
The answer is that you do not have to "prove" it, because, obviously, you already know.
Technically, there is another answer to your question, of course, and that is using typeid...
#include <typeinfo>
#include <typeindex>
#include <iostream>
void printArrLen(int arr[]);
int testArr [3] = {1, 4, 5};
int anotherTestArr [3] = {1, 4, 5};
int yetAnotherTestArr [4] = {1, 4, 5, 6};
int main() {
printArrLen(testArr);
}
void printArrLen(int arr[]) {
std::cout << (std::type_index(typeid(arr)) == std::type_index(typeid(testArr))) << "\n";
std::cout << (std::type_index(typeid(anotherTestArr)) == std::type_index(typeid(testArr))) << "\n";
std::cout << (std::type_index(typeid(yetAnotherTestArr)) == std::type_index(typeid(testArr))) << "\n";
}
This does not have any direct use for you, but it is of great educational value. It will print:
0
1
0
This example demonstrates that arrays of different sizes are different types, and pointers are different types from all array types.
testArr is not a pointer, it is an array with 3 elements.
arr is a pointer - there is not enough knowledge to make begin and end work, because the compiler does not know that it's pointing to an array (and what the size of the hypothetical array is).
My suggestion is: use either std::array or std::vector, depending on what you need to do. If you want to use old-school arrays, change printArrLen to take an array reference:
template <size_t N>
void printArrLen(int (&arr)[N]) {
/* ... */
}
wandbox example
I have a black box C++ function which I don't have access to its source code:
void blackbox(vector<int> &input);
This function modifies the element of the input vector in an unknown manner.
The problem I have now is that I want to apply the black box function only for a partial segment of a vector, for example,
the last 500 elements of a vector. So, this is the routine that I wrote to attain this goal:
vector<int> foo (5,1000);
vector<int> bar (foo.end()-500,foo.end());
blackbox(bar);
swap_ranges(foo.end()-500,foo.end(),bar.begin());
This code may work, but is there a better way to do this?
It would be good if I can define a vector reference only for a segment of
an existing vector, instead of creating a copy.
I am not so comfortable with the copying and swapping parts in the above code; since this routine is
invoked so frequently, I think the repeated copying and swapping slows down the code.
If I knew the exact operations done by the block box, I would rewrite the function so that it takes vector iterators as the input
arguments. Unfortunately, this is not possible at the moment.
There's no well-defined way to achieve this functionality. With huge caveats and warnings, it can (for one GCC version at least) be hacked as below, or you could perhaps write something with better defined behaviour but based on your compiler's current std::vector implementation....
So... hacked. This will not work if insert/erase/resize/reserve/clear/push_back or any other operation affecting the overall vector is performed. It may not be portable / continue working / work with all optimisation levels / work on Tuesdays / use at own risk etc.. It depends on the empty base class optimisation.
You need a custom allocator but there's a catch: the allocator can't have any state or it'll change the binary layout of the vector object, so we end up with this:
#include <iostream>
#include <vector>
template <typename Container> // easy to get this working...
void f(Container& v)
{
std::cout << "f() v.data() " << v.data() << ", v.size() " << v.size() << '\n';
for (int& n : v) n += 10;
}
void g(std::vector<int>& v) // hard to get this working...
{
std::cout << "g() v.data() " << v.data() << ", v.size() " << v.size() << '\n';
for (int& n : v) n += 100;
}
int* p_; // ouch: can't be a member without changing vector<> memory layout
struct My_alloc : std::allocator<int>
{
// all no-ops except allocate() which returns the constructor argument...
My_alloc(int* p) { p_ = p; }
template <class U, class... Args>
void construct(U* p, Args&&... args) { std::cout << "My_alloc::construct(U* " << p << ")\n"; }
template <class U> void destroy(U* p) { std::cout << "My_alloc::destroy(U* " << p << ")\n"; }
pointer allocate(size_type n, std::allocator<void>::const_pointer hint = 0)
{
std::cout << "My_alloc::allocate() return " << p_ << "\n";
return p_;
}
void deallocate(pointer p, size_type n) { std::cout << "deallocate\n"; }
template <typename U>
struct rebind { typedef My_alloc other; };
};
int main()
{
std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
std::cout << "main() v.data() " << v.data() << '\n';
My_alloc my_alloc(&v[3]); // first element to "take over"
std::vector<int, My_alloc> w(3, my_alloc); // num elements to "take over"
f(w);
g(reinterpret_cast<std::vector<int>&>(w));
for (int n : v) std::cout << n << ' ';
std::cout << '\n';
std::cout << "sizeof v " << sizeof v << ", sizeof w " << sizeof w << '\n';
}
Output:
main() v.data() 0x9d76008
My_alloc::allocate() return 0x9d76014
My_alloc::construct(U* 0x9d76014)
My_alloc::construct(U* 0x9d76018)
My_alloc::construct(U* 0x9d7601c)
f() v.data() 0x9d76014, v.size() 3
g() v.data() 0x9d76014, v.size() 3
0 1 2 113 114 115 6 7 8 9
sizeof v 12, sizeof w 12
My_alloc::destroy(U* 0x9d76014)
My_alloc::destroy(U* 0x9d76018)
My_alloc::destroy(U* 0x9d7601c)
deallocate
See it run here