C++ sizeof different result for the same value - c++

I want to calculate the 'sizeof' array:
char* arr[] = { "abc", "def" };
When I call sizeof manually, immediately after the initialization of the array, it works fine. However if I pass the array to some function, It doesn't give the same result.
int test(char* b[]) {
return (int)sizeof(b);
}
int _tmain(int argc, _TCHAR* argv[])
{
char* arr[] = { "abc", "def" };
int p = test(arr); // gives 4
int k = sizeof(arr); // gives 8
...
}
So what's the problem? Sorry for the newbie question, but I really miss it.

When you use sizeof on arr in _tmain, you're taking the size of the array itself, which is 2 * sizeof(char*) = 2 * 4 = 8 on your architecture, because your char*s occupy 32 bits = 4 bytes.
When you use sizeof on b in test, you're taking the size of a pointer to the first element of the array, which is sizeof(char**) = 4 on your architecture. The reason you're taking the size of the pointer and not the array is that you can't pass arrays as such to functions - instead, you pass them via a pointer to their first element. The information about the actual size of the array is thus lost in the call. Put another way, the signature of your function test is equivalent to int test(char **b).

This is because when you call a function in C or C++, arrays decay to pointers.
In the first case, the compiler sees arr as an array; in the second case, all it sees is a pointer to a pointer.

At point where test is defined, compiler cannot see what b points to. So, sizeof gives the size of a pointer (actually, size of a pointer to pointers).
Inside main, it can see perfectly well that real size is two pointers.
There really is no such thing as passing array into function - only pointer to array is passed. The [] form is just syntactic sugar.

You lose the size information when calling test because you array decays to a pointer.
You have to pass the size of the array as additional parameter, or fix it in the parameter type.

Welcome to C++.
You cannot pass an array to a function at all. In C++, arrays decay to pointers most of the time. You are trying to pass an array but in fact passing a pointer, and your second sizeof evaluates to the size of that pointer.
You should not care about any of this. Arrays are specialized low-level data structures that should be only used to implement higher-level abstractions. Use std::vector instead.

Related

While passing an array through a function in C++/C, why address of array is used in actual parameter whereas the array in formal parameter?

#include <iostream>
using namespace std;
int func(int ar[5]){
//the code is written here with returning whatever the requirement is
}
int main(){
int ar[5];
func(ar);
return 0;
}
In this kind of situation where we are passing array through a function why the address of the array is used in actual parameter whereas the array in formal parameter?
This is because C handled arrays weirdly.
In C, arrays convert to a pointer to their first element at the drop of a hat, cannot be passed as arguments to a function, cannot be returned from functions, and cannot be copied by assignment.
C++, originally based off C, carries these screwed up design decisions as a legacy issue. Fixing them would break insane amounts of code.
If you want an array that behaves more reasonable, use std::array.
void foo(int[5]);
this is actually taking an int* argument.
int arr[7];
foo(arr);
this converts the 7 element arr to a pointer and passes it to foo. Which seems to take int[5] but that 5 does nothing.
Yes this is utterly crazy.
Now,
void bar(int(&)[5])
because C does not have references, the above is an actual reference to an array if 5 elements, so
bar(arr)
won't compile.
There is nothing weird, nor screwed up about how arrays are passed in C. C is expressed pretty simply, really. C /does/ have references; they are explicit, so, easy to see when they're in use. We call them pointers.
One simply needs to understand that there is not dedicated storage for array types, that arrays are not first-class types, so, won't behave like first-class types. Can't be assigned into (directly), nor passed into and out of functions directly.
In C/C++ we use pointers to refer to a chunk of memory where the contents of the array are stored.
So, all that's left is that we understand that when we declare an array, the name of the array is really a pointer to the beginning of the array.
int a[12] ;
/* a's type is this: */
int *a ;
So, when we "pass an array" to a function, we are copying the pointer into the function. The semantics are straightforward, and entirely consistent (unlike Java).
The array, itself, is never passed directly.
Finally, realise that there is no difference between a pointer to a single integer, and a pointer to a contiguous chunk of integers in memory.
Remember, a[i] is equivalent to *(a+i). So, in this example:
int i = 12 ;
int *p = &i ;
, *p is the same as *(p+0); the p can be thought of as an array of length 1.
With a slight change in perspective you should be able to see the simplicity, the elegance, that is C.

Why don't I have to give the function the address of the array

Hi I'm new to C and learning about pointers. I'm writing a simple recursive function to test it where it takes parameters int *a and int size. In my main method, I send print_array the address of the first character of my array using the & beforehand.
This doesn't seem to work, I get given an "incompatible pointer types" error at compilation. I understand that I can remove the & and the program works fine. I have a question:
Why can't I pass in the memory address of my_array from main with a &? Shouldn't I be able to just give the function the memory address of first element of array and it can deal with the rest?
Thanks, hope this question wasn't too noob.
#include <stdio.h>
#include <stdlib.h>
void print_array(int *a, int size){
if (size>0){
printf("%d\n", a[0]);
print_array(a+1, size-1);
}
}
int main(void){
int my_array[20];
int i;
for (i=0; i < 20; i++){
my_array[i] = rand() % 20;
}
/*the contents of the array*/
printf("The contents of the array\n");
for (i=0; i < 20; i++){
printf("%d\n", my_array[i]);
}
printf("The recursive method print\n");
print_array(&my_array, 20);
return EXIT_SUCCESS;
}
Yes, you can give the function the address of the first element, and let it deal with the rest. You could do that as either:
print_array(my_array, 20);
...or:
print_array(&my_array[0], 20);
Unfortunately, while &my_array is legal code, it produces a pointer to the entire array, rather than a pointer to the first element of the array. Those have the same address, but different types, which is what's causing the error.
The type of a pointer determines (among other things) how arithmetic on that pointer will work. In your case, print_array prints the first int in the array, then adds one to the pointer. Since it's a pointer to int, that addition actually adds the size of an int to the address in the pointer.
If you used a pointer to the entire array, then adding one would instead add the size of the entire array to the address. For example, let's assume 4-byte ints and that my_array has a base address of 1000. In this case, my_array+1 will yield 1004, so it holds the address of the second int in the array (just as you undoubtedly wanted). By contrast, &my_array will take the address of the entire array, with the type "pointer to array of 20 ints". When you add one to it, that will add 1 * the size of the pointed-to type to the address, so you'll get 1080 (i.e., the entire array is 20 * 4 = 80 bytes). This is obviously not what you wanted--instead of x+1 pointing to the second item in the array, it now points past the end of the array, and attempting to dereference the pointer will give undefined behavior.
So, just switch to one of the forms above (my_array or &my_array[0]). As a more general point, realize that the name of an array evaluates as a pointer to the first element of the array under most circumstances--the notable exceptions being when you use the name of the array as a the operand of either the sizeof operator or the address-of operator (as you did here). In these two cases, the name of the array still refers to the entire array instead of a pointer to the first element.
Your function needs a pointer to an int, specifically the address of the first (0th) element of the array.
By calling print_array(&my_array, 20);, you're trying to pass the address of the entire array, a value of type int(*)[20], which is different than int*. It points to the same memory location, but it's of a different type.
To pass the address of the first element, you can write:
print_array(&my_array[0], 20);
or, equivalently:
print_array(my_array, 20);
The latter works because, in most but not all contexts, the name of an array is implicitly converted to a pointer to its first element.
The relationship between arrays and pointers in C and C++ can be confusing. Recommended reading: Section 6 of the comp.lang.c FAQ
Because arrays automatically decay to pointers, the following was extracted from the n1570 draft
6.3.2 Other operands
6.3.2.1 Lvalues, arrays, and function designators
Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.

memset on static array

I am a little confused about what to pass as the first parameter to memset when you have to set the elements of a static array. I've been searching but I couldn't find the answers to some specific questions I have.
If I have an array declared as:
char arr[10];
I have seen that these two calls are valid and produce the same effect:
memset( arr, 0, 10);
memset( &arr, 0, 10);
My specific questions are:
1- Why do they have the same effect on arr?
2- What is the different between these calls?
3- Which one would be considered the correct one?
Thank you!
Storage duration has nothing to do with it; an array is an array. This expression:
&arr
produces a char (*)[10], i.e., a pointer to an array of char with 10 elements. However, when arr is passed to a function like so:
memset(arr, 0, 10);
it degrades to a pointer to the first element, i.e., a char*. These are not the same thing. The "correct" (idiomatic) call is:
memset(arr, 0, 10);
However, in this case they are both converted to a void* when passed to memset and interpreted in the function as an unsigned char*. Since they both point to the same place it produces the same result.
However, it is important to realize that, when dealing with the true respective types (i.e., not a void*), a pointer to an array is not the same as a pointer to the first element of an array.
For example, incrementing a char (*)[10] will increment the pointer sizeof(char[10]) bytes, while incrementing a char* will increment only one byte.
Why do they have the same effect on arr? What is the different between these calls?
Because the address of an array is the same as the address of its first element (arrays decay into pointers to their first element when passed to a function), it's just that they have a different type. arr has type char[10], that decays into char * when passed to a function. In contrast, &arr has the type char (*)[10] which doesn't change when passed as a function argument.
Which one would be considered the correct one?
As long as the function doesn't expect a specific type, i. e. it accepts void *, either one is good. If the called function, however, expects that either of the types be specified, then the other one should not be used, since then your program would be malformed and invoke undefined behavior.
1- Why do they have the same effect on arr?
They both contain the same value which is the address of the beginning of the array.
2- What is the different between these calls?
arr decays to a pointer to char, i.e. char* (this conversion happens when you pass the name of an array to a function) and &arr is a pointer to an array of char, i.e. char (*)[].
3- Which one would be considered the correct one?
I would use arr. memset accepts a void* which is the reason both work.
Also, note that char arr[10] = {}; can be used to zero initialize the array.

c++ pass array to function question

How is it possible to pass an array parameter to a function, and calculate the size of the array parameter? Because every time i try to calculate it, the return value is always 4.
Thanks the fast replies. I'm going to pass the size of the array as well then. Or i give a shot to vectors. Thanks
You can do it using templates as described here:
template<size_t Size>
void AcceptsArray( ParameterType( &Array )[Size] )
{
//use Size to find the number of elements
}
it is be called like this:
ParameterType array[100];
AcceptsArray( array ); //Size will be auto-deduced by compiler and become 100
The only drawback is that you now have a templated function and that increases code bloat. This can be addressed by redirecting the call to a non-templated function that accepts the address of the first element and the number of elements.
This is (one of) the difference between arrays and pointers. Taking the size of an array results in its size in bytes, whereas taking the size of a pointer yields the size of pointers on that system. However, whenever you pass an array to a function, it decays to a pointer, the size of which is always the same no matter what type of pointer it is (4 on a 32 bit machine).
So technically, it's impossible to pass an array into a function since whenever you try, it becomes a pointer.
You'll need to pass the size of the array in to the function as well, or better yet if you can, use std::vector or std::array, or std::string if you're using the array as a C-style string.
In C++ it is considered a bad pratice to use raw arrays. You should consider using std::vector or boost::array instead.
It is difficult to calculate the size of an array if you do not keep track of the size or supply some sort of an guardian value at the end. In C-strings (not std::strings), for example, the '\0' character marks the end of the string (a char array).
This works with g++:
template <typename T>
std::size_t size_of_array (T const & array)
{
return sizeof (array) / sizeof (*array);
}
int main ()
{
int x [4];
std::cout << "size: " << size_of_array (x) << '\n';
}
I guess it is because the function is inlined, but still it seems the array does not decay in this case. Can somebody explain why?
If you are using c arrays it's not possible because they're automatically casted to pointer when passing it to a function. So 4 is the size of the pointer.
Solution: use std::vector
#include <vector>
int carray[] = {1,2,3,4};
std::vector<int> v(carray, carray + sizeof(carray)/sizeof(int));
my_function(v);

Cannot convert from 'int *' to 'int []'?

I know this might be a common question but I have tried to search but still cannot find a clear answer.
I have the following code:
int* f() {
int a[] = {1,2,3};
return a;
}
int main() {
int a[] = f(); // Error here
getch();
return 0;
}
This code produces the error message: "Cannot convert from 'int *' to 'int []'"
I found this quite strange because I have read that pointer and array are similar. For example, we can use a[i] instead of *(a + i).
Can anyone give me a clear explanation, please?
There are actually two errors in this code.
Firstly, you are returning the address of a temporary (the int array within f), so its contents are undefined after the function returns. Any attempt to access the memory pointed to by the returned pointer will cause undefined behaviour.
Secondly, there is no implicit conversion from pointers to array types in C++. They are similar, but not identical. Arrays can decay to pointers, but it doesn't work the other way round as information is lost on the way - a pointer just represents a memory address, while an array represents the address of a continuous region, typically with a particular size. Also you can't assign to arrays.
For example, we can use a[i] instead of *(a + i)
This, however, has little to do with the differences between arrays and pointers, it's just a syntactic rule for pointer types. As arrays decay to pointers, it works for arrays as well.
The type int[] doesn't actually exist.
When you define and initialize an array like
int a[] = {1,2,3};
the compiler counts the elements in the initializer and creates an array of the right size; in that case, it magically becomes:
int a[3] = {1,2,3};
int[] used as a parameter to a function, instead, it's just plain int *, i.e. a pointer to the first element of the array. No other information is carried with it, in particular nothing about the size is preserved. The same holds when you return a pointer
Notice that an array is not a pointer: a pointer can be changed to point to other stuff, while an array refers always to the same memory; a pointer does not know anything about how big is the space of memory it points to, while the size of an array is always known at compile time. The confusion arises from the fact that an array decays to a pointer to its first element in many circumstances, and passing it to a function/returning it from a function are some of these circumstances.
So, why doesn't your code work? There are two big errors:
You are trying to initialize an array with a pointer. We said that an int * doesn't carry any information about the size of the array. It's just a pointer to the first element. So the compiler cannot know how big a should be made to accomodate the stuff returned by f().
In f you are returning a pointer to a variable that is local to that function. This is wrong, because a pointer does not actually store the data, it only points to where the data is stored, i.e. in your case to the a local to f. Because that array is local to the function, it ceases to exist when the function exits (i.e. at the return).
This means that the pointer you are returning points to stuff that does not exist anymore; consider the code:
int * a = f();
This initialization works, and you can try to use a later in the function, but a will be pointing to the no-longer existent array of f; in the best case your program will crash (and you'll notice immediately that you've done something wrong), in the worst it will seem to work for some time, and then start giving strange results.
int * and int [] are similar but different.
int * is a real pointer, meanwhile int[] is an array reference ( a sort of "constant pointer" to the begin of the data) wich cannot be modified. So, a int * can be threated like a int [] but not viceversa.
You can use a[b] and*(a+b) interchangeably because that is exactly how a[b] is defined when one of a or b is a pointer and the other is of integer or enumeration type.
Note: This also means that expressions like 42[a] are perfectly legal. Human readers might object strongly, but the compiler won't bat an eye at this.