When passing a double pointer to a function, I used the notation *ptr[j++] in my function which lead the program to crash. I guessed it happened due to operator precedence, so I rectified it by writing (*ptr)[j++] but I didn't like this notation. It feels long and confusing.
I also know of the notation ptr[0][j++] but I also don't like it.Is there any better notation or approach around all of this?
My code:
#include <iostream>
using namespace std;
void mset(int **ptr, size_t size);
void main(void)
{
const size_t size = 10;
int *ptr = new int[size];
mset(&ptr, size);
for (size_t n = 0; n < size; n++) {
std::cout << ptr[n] << std::endl;
}
}
void mset(int **ptr, size_t size)
{
size_t j = 0;
while(j < size)
(*ptr)[j++] = 3;
}
P.S I know that I can write void mset(int *ptr, size_t size) and invoke mset(ptr, size), but I am asking about that particular case.
Simple use an extra level of indirection, something like:
auto p = *ptr;
for (size_t j = 0; j < size; ++j)
p[j] = 3;
For this particular case, inside mset(int **ptr, size_t size), you are never using ptr as it is. but using with dereference (i.e. *ptr). Hence, I would recommend to pass the pointer reference.
mset(ptr, size);
// ^^^ pass simply
void mset(int* const &ptr, const size_t size)
{ // ^^^ pointer reference which cannot change (prevents memory leak)
size_t j = 0;
while(j < size)
ptr[j++] = 3; // No dereferencing required
}
You may also remove the reference as well, because you don't intend to change the value of ptr ever in the mset(). But passing as above is also fine to be able to use the same ptr from main().
Here is the demo.
BTW, a standard compliant main() always returns int.
Declare the level one pointer (int**, and not int*) as const, and compiler would raise an error:
void mset(int const** ptr, size_t size)
{
size_t j = 0;
while (j < size)
*ptr[j++] = 3; // ERROR
}
You are sure that base pointer isn't going to change, and you don't want function to change its contents, so make is const. However, multiple level of pointers do cause problems, it is better to use references,
vectors, templates and other better/newer alternatives.
Related
void f1(int* count, char* str) {
for (int i = 0; i < *count; ++i) str[i] = 0;
}
void f2(int* count, char8_t* str) {
for (int i = 0; i < *count; ++i) str[i] = 0;
}
void f3(int* count, char* str) {
int n = *count;
for (int i = 0; i < n; ++i) str[i] = 0;
}
void f4(int* __restrict__ count, char* str) { // GCC extension; clang also supports it
for (int i = 0; i < *count; ++i) str[i] = 0;
}
According to this article,
the compiler (almost) replaces f2() with a call to memset(),
however, the compiler generates machine code from f1() that is almost identical to the above code.
Because the compiler can assume that count and str do not point to the same int object (strict aliasing rule) in f2(),
but cannot make such an assumption in f1() (C++ allow aliasing any pointer type with a char*).
Such aliasing problems can be avoided by dereferencing count in advance, as in f3(), or by using __restrict__, as in f4().
https://godbolt.org/z/fKTjcnW5f
The following functions are std::string/std::u8string version of the above functions:
void f5(int* count, std::string& str) {
for (int i = 0; i < *count; ++i) str[i] = 0;
}
void f6(int* count, std::u8string& str) {
for (int i = 0; i < *count; ++i) str[i] = 0;
}
void f7(int* count, std::string& str) {
int n = *count;
for (int i = 0; i < n; ++i) str[i] = 0;
}
void f8(int* __restrict__ count, std::string& str) {
for (int i = 0; i < *count; ++i) str[i] = 0;
}
void f9(int* __restrict__ count, std::string& __restrict__ str) {
for (int i = 0; i < *count; ++i) str[i] = 0;
}
https://godbolt.org/z/nsPdfhzoj
My questions are:
f5() and f6() are the same result as f1() and f2(), respectively.
However, f7() and f8() do not have the same result as f3() and f4() (memset() was not used). Why?
The compiler replaces f9() with a call to memset() (that does not happen with f8()). Why?
Tested with GCC 12.1 on x86_64, -std=c++20 -O3.
I created a simplified demo for the string case:
class String {
char* data_;
public:
char& operator[](size_t i) { return data_[i]; }
};
void f(int n, String& s) {
for (int i = 0; i < n; i++) s[i] = 0;
}
The problem here is that the compiler cannot know whether writing to data_[i] does not change the value of data_. With the restricted s parameter, you tell the compiler that this cannot happen.
Live demo: https://godbolt.org/z/jjn9d3Mxe
This is not necessary for passing a pointer, since it is passed in the register, so it cannot be aliased with the pointed-to data. However, if this pointer is a global variable, the same problem occurs.
Live demo: https://godbolt.org/z/Y3nWvn6rW
My guess would be that your std::string implementation internally has a char *, since that is the default type for the string template. That char * is accessed in str[i] and being a char * can alias with any other pointer or reference, specifically with your str. So every time str[i] = 0 is evaluated the str object might change.
Not so when you restrict str.
When the first C STandard was written, character types in C and C++ were widely used for three purposes:
Storing actual text characters.
Holding small numbers.
Accessing the raw bits in the storage underlying other types.
There is no particular reason why objects and pointers that are used for the first two purposes should have any kind of special aliasing rules, but the authors of the Standard wanted to make sure that aliasing rules wouldn't interfere with the use of character types for the third purpose.
A better way of accommodating the latter construct would have been to say that in contexts where a compiler can see that a T* is converted to U*, accesses performed via the U* will be recognized as possibly being performed "by" lvalues of type T. Applying such principles would eliminate the need for the "character type exception". Nonetheless, the fact that character types are sometimes used for the third purpose above has resulted in them having special rules which would not make sense if applied to other constructs that are only used for the first two purposes.
I want to pass a 2D array of characters to another function using a parameter of type "void*" and then have that function have access to the elements inside that array.
This code spits out a "Segmentation Fault:11" at the point where voidPointer tries to std::cout elements inside array2.
#include <iostream>
void voidPointer(void* userdata) {
char** array2 = static_cast<char**>(userdata);
for (int i=0; i<3; ++i) {
for (int j=0; j<3; ++j) {
std::cout << array2[i][j] << ' ';
}
std::cout << std::endl;
}
return;
}
int main() {
char array[3][3];
for (int i=0; i<3; ++i) {
for (int j=0; j<3; ++j) {
array[i][j] = 'a';
}
}
voidPointer(array);
return 0;
}
I've tried lots of different things and can't figure this out at all. I was able to get the above code to work when it's dealing with a 1D array. For example, this code
#include <iostream>
void voidPointer(void* userdata) {
char* array2 = static_cast<char*>(userdata);
for (int i=0; i<3; ++i) {
std::cout << array2[i] << ' ';
}
std::cout << std::endl;
return;
}
int main() {
char array[3];
for (int i=0; i<3; ++i) {
array[i] = 'a';
}
voidPointer(array);
return 0;
}
works as expected with the output "a a a".
Backstory: I'm working on my first project in which I'm trying to use a Mouse Callback function that accepts a parameter in the form "void* userdata". I am attempting to pass a 2D character array to this Callback function so I can then pass it on to other functions that will require access to the elements inside this array. I don't really know if this is good coding practice or not so feel free to let me know some alternatives.
To anyone that responds, thank you!
Firstly, I'll explain why your second example succeeds but your first example fails. Then I'll suggest some options for consideration to make your code work.
In short - your first example has undefined behaviour because the notional equivalence of pointers and arrays only works in one dimension.
The second example relies on the facts that;
The name of a one-dimensional array can be implicitly converted to a pointer to that array's first element. So, in main() of your second example, voidPointer(array) is equivalent to voidPointer(&array[0]). &array[0] has type char *.
A pointer can survive a round trip via a void * conversion - where "round trip" means retrieving the pointer of the original type. i.e. a char * can be converted to a void * AND that void * can be converted back to a char *. So the explicit conversion char* array2 = static_cast<char*>(userdata) done in voidPointer() successfully retrieves the pointer - so array2 in voidPointer() is equal to &array[0] passed by main();
Since the pointer passed by main() is the address of the first element of the array passed, voidPointer() can safely treat that pointer AS IF it is an array (as long as code doesn't try to access elements out of range of the original array).
The logic above is only applicable for pointers and one-dimensional arrays, so breaks down in the first example;
The name of a one-dimensional array can be implicitly converted to a pointer to that array's first element. So, in main() of your second example, voidPointer(array) is equivalent to voidPointer(&array[0]). However, the difference is that - the expression &array[0] has type char (*)[3] (a pointer to an array of three char) and that is NOT equivalent to a char **.
in voidPointer() your code converts the received pointer to a char ** via char** array2 = static_cast<char**>(userdata). This means that the pointer array2 has a different type that the pointer (&array[0]) passed by main();
Since array2 has a different type than the pointer passed by main() the code in voidPointer() which dereferences array2 (treats it as if it is an array of arrays) has undefined behaviour.
Generally speaking, there are two ways you can make the code work. The first is to do the right type of conversion.
void voidPointer(void* userdata)
{
char (*array2)[3] = static_cast<(char (*)[3]>(userdata);
// rest of your function can be used as is
}
As in your code, the array dimensions (which are both 3 in your example) must be known and fixed at compile time. There is no way that userPointer() can obtain any array dimensions from userdata, because a void * does not carry any of that sort of information from the caller.
A second option is to wrap the array in a data structure, for example
#include <iostream>
struct Carrier {char data[3][3];};
void voidPointer(void* userdata)
{
Carrier *package2 = static_cast<Carrier *>(userdata);
for (int i=0; i<3; ++i)
{
for (int j=0; j<3; ++j)
{
std::cout << package2->data[i][j] << ' ';
}
std::cout << std::endl;
}
}
int main()
{
Carrier package;
for (int i=0; i<3; ++i)
{
for (int j=0; j<3; ++j)
{
package.data[i][j] = 'a';
}
}
voidPointer(&package);
return 0;
}
This works because a Carrier * can survive a round trip via a void pointer (i.e. the value of package2 in voidPointer() has the the same type AND the same value as &package in main()) .
A second option is to use the std::array class. Although this is syntactically different, it is actually a modified version of the first option (since std::array is technically a templated data structure that contains an array of fixed dimension).
#include <iostream>
#include <array>
void voidPointer(void* userdata)
{
std::array<std::array<char, 3>, 3> *package2 = static_cast<std::array<std::array<char, 3>, 3> *>(userdata);
for (int i=0; i<3; ++i)
{
for (int j=0; j<3; ++j)
{
std::cout << (*package2)[i][j] << ' ';
}
std::cout << std::endl;
}
}
int main()
{
std::array<std::array<char, 3>, 3> package;
for (int i=0; i<3; ++i)
{
for (int j=0; j<3; ++j)
{
package[i][j] = 'a';
}
}
voidPointer(&package);
return 0;
}
Since your examples both had array dimensions fixed at compile time (3 in each dimension), my examples do the same. I'll leave extending the above to have dimensions fixed at run time (e.g. as user inputs) as a learning example.
It looks like the question is how to make this scenario work instead of why the attempt failed. For those interested in why the attempt failed, see casting void** to 2D array of int.
Note: It would be better to avoid using void*, but sometimes one has to interface with someone else's C-style API where void* is the traditional way to pass data to a callback.
There is a reasonably common trick for simulating a multi-dimensional array with a one-dimensional array. Using the trick reduces your scenario to the case that works. The trick involves how you access the elements. Instead of declaring char array[DIM1][DIM2] and accessing elements via array[i][j], declare the array to be char array[DIM1 * DIM2] and access elements via array[i*DIM2 + j].
However, remembering this formula is intellectual overhead, consuming brainpower that would be better used elsewhere. Not to mention that I get the dimensions reversed half the time. You could relieve the coder of this overhead by wrapping this array in a class, and hiding the formula in a method for accessing elements. This might look like the following.
class Array2D {
static constexpr unsigned DIM1 = 3;
static constexpr unsigned DIM2 = 3;
char data[DIM1 * DIM2];
public:
char& at(unsigned i, unsigned j) { return data[i*DIM2 + j]; }
// And perhaps other methods
};
You could then create an object of this class, then pass the address of that object to your C-style mouse handler. I.e. if your variable is Array2D array then call voidPointer(&array). This version can be adapted to the situation where the dimensions are not known at compile time.
Then again, if you are going to create a class anyway, why not try to preserve the syntax you are used to (using operator[] twice)? This does assume that the dimensions are compile-time constants.
class Array2D {
static constexpr unsigned DIM1 = 3;
static constexpr unsigned DIM2 = 3;
char data [DIM1][DIM2];
public:
auto& operator[] (unsigned i) { return data[i]; }
// And perhaps other methods
};
Of course, this approach locks you into a single size. It would probably be a good idea to make this a template. It would be even better if someone else did all that work for me.
#include <array>
static constexpr unsigned DIM1 = 3;
static constexpr unsigned DIM2 = 3;
using Array2D = std::array< std::array<char,DIM2>, DIM1 >;
// Note the order: ^^^^ ^^^^
Remember: if your variable is Array2D array then call voidPointer(&array).
Why not to use a structure instead of array. You can define everything in a structure, than pass its address to your function.
I'm trying to loop through a constant array of integers using pointers. My code is as follows:
void printArrayPointer(const int arr[], int n){
for (int *i = arr; i < arr + n; i++) {
cout << *i << ' ';
}
}
This gives me an error telling me that there is an invalid conversion from const int* to int*. I know how to do this using traversal by index (as in using the index of the elements in the array) but I'm trying to use pointers for this code.
As the compiler already told you, there is an error in the line:
for (int *i = arr;...
arr is const int[], i is int *. If the compiler allows i to be initialized with a const int * then the subsequent code can change the const values stored in a using i, because i is a pointer to non-const data. That's the reason the code doesn't compile and the compiler tells you in the error message.
You have to declare i as const int *i and it will work.
void printArrayPointer(const int arr[], int n){
for (const int *i = arr; i < arr + n; i++) {
cout << *i << ' ';
}
}
As axiac said, you must write for (const int* i ... because arr is a const int*.
The reason that you can still increment i is that const int* means that what is at i cannot be changed, by you can still change what i points to. So you cannot change the values in the array, but you can change the const pointer that iterates through them.
To make it so that you can't change what a pointer points to, you would have to declare it like this: int* const i. Then you could change what is at i but not what i points to. You could also make it so that neither could be changed, which could be written as const int* const i.
I have function that receives an array of pointers like so:
void foo(int *ptrs[], int num, int size)
{
/* The body is an example only */
for (int i = 0; i < size; ++i) {
for (int j = 0; j < num-1; ++j)
ptrs[num-1][i] += ptrs[j][i];
}
}
What I want to convey to the compiler is that the pointers ptrs[i] are not aliases of each other and that the arrays ptrs[i] do not overlap. How shall I do this ? My ulterior motive is to encourage automatic vectorization.
Also, is there a way to get the same effect as __restrict__ on an iterator of a std::vector ?
restrict, unlike the more common const, is a property of the pointer rather than the data pointed to. It therefore belongs on the right side of the '*' declarator-modifier. [] in a parameter declaration is another way to write *. Putting these things together, you should be able to get the effect you want with this function prototype:
void foo(int *restrict *restrict ptrs, int num, int size)
{
/* body */
}
and no need for new names. (Not tested. Your mileage may vary. restrict is a pure optimization hint and may not actually do anything constructive with your compiler.)
Something like:
void foo(int *ptrs[], int num, int size)
{
/* The body is an example only */
for (int i = 0; i < size; ++i) {
for (int j = 0; j < num-1; ++j) {
int * restrict a = ptrs[num-1];
int * restrict b = ptrs[j];
a[i] += b[i];
}
}
... should do it, I think, in C99. I don't think there's any way in C++, but many C++ compilers also support restrict.
In C++, pointer arguments are assumed not to alias if they point to fundamentally different types ("strict aliasing" rules).
In C99, the "restrict" keyword specifies that a pointer argument does not alias any other pointer argument.
Call std::memcpy. Memcpy's definition will have restrict set up if your language/version and compiler support it, and most compilers will lower it into vector instructions if the size of the copied region is small.
I have quite peculiar problem. I want initialize an array pointed by a void pointer to which memory is allocated using new as shown below.
const int ARRAY_SIZE = 10;
void InitArray()
{
int *ptrInt = new int[ARRAY_SIZE];
for(int i=0; i<ARRAY_SIZE;i++)
{
ptrInt[i] = 1; //OK
}
void *ptrVoid = new int[ARRAY_SIZE];
for(int i=0; i<ARRAY_SIZE;i++)
{
*(int*)ptrVoid[i] = 1; //Culprit : I get a compiler error here
//(error C2036: 'void *' : unknown size)
}
}
Now, I want to initialize the elements of this array which is pointed by ptrVoid with say 1. How do I go about it? With this code I get a compiler error as shown in the code(I am using VS 2010). Any suggestions?
You have an order of operations problem (and an extra *). Try this inside your second loop:
((int *)ptrVoid)[i] = 1;
*(int*)ptrVoid[i] is *((int*)(ptrVoid[i])), and you're dereferencing too many times (the [] does a dereference).
Write ((int*)ptrVoid)[i] (or, better, static_cast<int*>(ptrVoid)[i]) then re-consider your use of void* at all.
You just need to parenthesize correctly and cast the void* to an int*, so that the compiler knows how many bytes to offset when you index it with [i].
for(int i=0; i<ARRAY_SIZE;i++)
{
((int*)ptrVoid)[i] = 1;
}
How about:
int* ptrVoidAsInt = new int[ARRAY_SIZE];
for(int i=0; i<ARRAY_SIZE;i++)
{
ptrVoidAsInt[i] = 1;
}
void* ptrVoid = ptrVoidAsInt;
But, one has to wonder what the meaning of either a void array or 1 initialised data is. Is this really an array of int or some other type that is going to be passed as a void* and then recast back to a typed array?