I have this loop which gives seg. fault.
s->c = malloc(width * height * sizeof(double));
if (s->c == NULL) { puts("malloc failed"); exit(1); }
for (int n = 0; n < width; n++) {
for (int m = 0; m < height; m++) {
d = (&s->c)[m][n];
printf("d %f\n", d);
printf("m %i\n", m);
printf("n %i\n", n);
}
}
Inside s->c is:
double* c;
When executed it just outputs:
d 27.000000
m 0
n 0
and then seg. fault.
It worked when I treated the s->c as a 1D array, but I would really like to treat it as a 2D array.
Is that possible, when the c pointer is in a struct?
If so, is (&s->c)[m][n] then the correct way to access the elements?
Sandra
The problem is that the compiler doesn't know the dimensions of your matrix.
When you have: double tab[m][n] you can access the element tab[row][col] as *(tab + (row * n) + col)
In your case you only have double *tab; that can be considered as the pointer to the element tab[0][0] with no information on the matrix dimensions and the compiler can't compute the right address.
You could compute the address yourself (for example using a macro) but would lose the nice tab[x][y] syntax.
I`m surprised it compiles. You should have received at least a warning about implicitly casting a double to a pointer.
I'm very surprised it even compiles. Apparently c is a double*, so (&s->c)[m] is the m'th double. Now, double doesn't have an operator[], so I don't see how the [n] part in (&s->c)[m][n] can be legal.
Presumably, you have declared c differently. There are different solutions: a pointer to a pointer, an pointer to an array of doubles, an array of pointers to doubles, etcetera. All might work, if the allocations match the declaration. In your case, the allocation will not match the declaration.
The correct way to access the array elements is
d = s->c[m * width + n];
Is that what you mean by treating it as a 1D array?
Access the elements using
double d = s->c[m*width+n];
Perhaps through an inline function, to avoid unexpected behaviour.
The compiler does not know about the width of your intended 2D array. It might possibly interpret (&s->c)[m][n] as s->c[m+n], or as something quite different.
Short answer: you can't treat it as a 2D array, at least not in the way you expect.
The reason writing
(&s->c)[m][n]
doesn't work can be shown as follows. Assume the address of s->c is 0x00080004, and the address of the dynamically allocated memory pointed to by s->c is 0x00001000.
The expression (&s->c)[m][n] is evaluated as *(*(&s->c + m) + n);
The expression &s->c evaluates to 0x00080004;
The expression (&s->c + m) evaluates to 0x00080004+m;
The expression *(&s->c + m) evaluates to the value of whatever is pointed to by 0x00080004+m. If m is 0, then 0x00080004+m points to 0x00001000, which is the address of your dynamically allocated memory (*(&x) == x). If m is any other value, then 0x00080004+m points somewhere random;
The expression (*(&s->c + m) + n) evaluates to whatever 0x00080004+m points to offset by n. If m is 0, then the value is 0x00001000+n, or an offset into your dynamically allocated memory. If m is not 0, then the value is something random;
The expression *(*(&s->c) + m) + n) attempts to dereference the above value. If m is 0, then the result is the value of an element in the dynamically allocated array. If m is not 0, then the result is ... something else. In your case, a segfault.
If you want to dynamically allocate a 2D array, you have to use a pointer to a pointer and allocate it in steps, like so:
struct {
...
double **c;
...
} *s;
...
/**
* Notes: type of s->c is double **
* type of *(s->c) and s->c[i] is double *
* type of *(s->c[i]) and s->c[i][j] is double
*/
s->c = malloc(sizeof *(s->c) * rows);
if (s->c)
{
for (i = 0; i < rows; i++)
{
s->c[i] = malloc(sizeof *(s->c[i]) * columns);
if (s->c[i])
{
// initialize s->c[i][0] through s->c[i][columns-1]
}
}
}
If you want to allocate s->c as a 1D array, then you can define a macro that does the job for you (but you need to know the second dimension):
#define AR(M, X, Y) ((M)[(Y) + dimy * (X)])
I'm surprised nobody mentioned boost::multi_array_ref:
#include <iostream>
#include <boost/multi_array.hpp>
int main()
{
int rows = 4, cols = 3;
// Allocate one big block of contiguous data for entire multi-array
double* arrayData = new double[rows*cols];
if (arrayData)
{
boost::multi_array_ref<double, 2>
arrayRef(arrayData, boost::extents[rows][cols]);
for (int row = 0; row < rows; ++row)
{
for (int col = 0; col < cols; ++col)
{
arrayRef[row][col] = row*cols + col;
std::cout << arrayRef[row][col] << " ";
}
std::cout << std::endl;
}
}
delete [] arrayData;
}
You can also just use boost::multi_array and resize it dynamically:
boost::multi_array_ref<double, 2> marray;
marray.resize(boost::extents[rows][cols]);
Related
I am a Fortran user and do not know C++ well enough. I need to make some additions into an existing C++ code. I need to create a 2d matrix (say A) of type double whose size (say m x n) is known only during the run. With Fortran this can be done as follows
real*8, allocatable :: A(:,:)
integer :: m, n
read(*,*) m
read(*,*) n
allocate(a(m,n))
A(:,:) = 0.0d0
How do I create a matrix A(m,n), in C++, when m and n are not known at the time of compilation? I believe the operator new in C++ can be useful but not not sure how to implement it with doubles. Also, when I use following in C++
int * x;
x = new int [10];
and check the size of x using sizeof(x)/sizeof(x[0]), I do not have 10, any comments why?
To allocate dynamically a construction similar to 2D array use the following template.
#include <iostream>
int main()
{
int m, n;
std::cout << "Enter the number of rows: ";
std::cin >> m;
std::cout << "Enter the number of columns: ";
std::cin >> n;
double **a = new double * [m];
for ( int i = 0; i < m; i++ ) a[i] = new double[n]();
//...
for ( int i = 0; i < m; i++ ) delete []a[i];
delete []a;
}
Also you can use class std::vector instead of the manually allocated pointers.
#include <iostream>
#include <vector>
int main()
{
int m, n;
std::cout << "Enter the number of rows: ";
std::cin >> m;
std::cout << "Enter the number of columns: ";
std::cin >> n;
std::vector<std::vector<double>> v( m, std::vector<double>( n ) );
//...
}
As for this code snippet
int * x;
x = new int [10];
then x has type int * and x[0] has type int. So if the size of the pointer is equal to 4 and the size of an object of type int is equal also to 4 then sizeof( x ) / sizeof( x[0] ) will yields 1. Pointers do not keep the information whether they point to only a single object or the first object pf some sequence of objects.
I would recommend using std::vector and avoid all the headache of manually allocating and deallocating memory.
Here's an example program:
#include <iostream>
#include <vector>
typedef std::vector<double> Row;
typedef std::vector<Row> Matrix;
void testMatrix(int M, int N)
{
// Create a row with all elements set to 0.0
Row row(N, 0.0);
// Create a matrix with all elements set to 0.0
Matrix matrix(M, row);
// Test accessing the matrix.
for ( int i = 0; i < M; ++i )
{
for ( int j = 0; j < N; ++j )
{
matrix[i][j] = i+j;
std::cout << matrix[i][j] << " ";
}
std::cout << std::endl;
}
}
int main()
{
testMatrix(10, 20);
}
The formal C++ way of doing it would be this:
std::vector<std::vector<int>> a;
This creates container which contains a zero size set of sub-containers. C++11/C++13 provide std::array for fixed-sized containers, but you specified runtime sizing.
We now have to impart our dimensions on this and, unfortunately. Lets assign the top-level:
a.resize(10);
(you can also push or insert elements)
What we now have is a vector of 10 vectors. Unfortunately, they are all independent, so you would need to:
for (size_t i = 0; i < a.size(); ++i) {
a[i].resize(10);
}
We now have a 10x10. We can also use vectors constructor:
std::vector<std::vector<int>> a(xSize, std::vector<int>(ySize)); // assuming you want a[x][y]
Note that vectors are fully dynamic, so we can resize elements as we need:
a[1].push_back(10); // push value '10' onto a[1], creating an 11th element in a[1]
a[2].erase(2); // remove element 2 from a[2], reducing a[2]s size to 9
To get the size of a particular slot:
a.size(); // returns 10
a[1].size(); // returns 11 after the above
a[2].size(); // returns 9 after teh above.
Unfortunately C++ doesn't provide a strong, first-class way to allocate an array that retains size information. But you can always create a simple C-style array on the stack:
int a[10][10];
std::cout << "sizeof a is " << sizeof(a) <<'\n';
But using an allocator, that is placing the data onto the heap, requires /you/ to track size.
int* pointer = new int[10];
At this point, "pointer" is a numeric value, zero to indicate not enough memory was available or the location in memory where the first of your 10 consecutive integer storage spaces are located.
The use of the pointer decorator syntax tells the compiler that this integer value will be used as a pointer to store addresses and so allow pointer operations via the variable.
The important thing here is that all we have is an address, and the original C standard didn't specify how the memory allocator would track size information, and so there is no way to retrieve the size information. (OK, technically there is, but it requires using compiler/os/implementation specific information that is subject to frequent change)
These integers must be treated as a single object when interfacing with the memory allocation system -- you can't, for example:
delete pointer + 5;
to delete the 5th integer. They are a single allocation unit; this notion allows the system to track blocks rather than individual elements.
To delete an array, the C++ syntax is
delete[] pointer;
To allocate a 2-dimensional array, you will need to either:
Flatten the array and handle sizing/offsets yourself:
static const size_t x = 10, y = 10;
int* pointer = new int[x * y];
pointer[0] = 0; // position 0, the 1st element.
pointer[x * 1] = 0; // pointer[1][0]
or you could use
int access_2d_array_element(int* pointer, const size_t xSize, const size_t ySize, size_t x, size_t y)
{
assert(x < xSize && y < ySize);
return pointer[y * xSize + x];
}
That's kind of a pain, so you would probably be steered towards encapsulation:
class Array2D
{
int* m_pointer;
const size_t m_xSize, m_ySize;
public:
Array2D(size_t xSize, size_t ySize)
: m_pointer(new int[xSize * ySize])
, m_xSize(xSize)
, m_ySize(ySize)
{}
int& at(size_t x, size_t y)
{
assert(x < m_xSize && y < m_ySize);
return m_pointer[y * m_xSize + x];
}
// total number of elements.
size_t arrsizeof() const
{
return m_xSize * m_ySize;
}
// total size of all data elements.
size_t sizeof() const
{
// this sizeof syntax makes the code more generic.
return arrsizeof() * sizeof(*m_pointer);
}
~Array2D()
{
delete[] m_pointer;
}
};
Array2D a(10, 10);
a.at(1, 3) = 13;
int x = a.at(1, 3);
Or,
For each Nth dimension (N < dimensions) allocate an array of pointers-to-pointers, only allocating actual ints for the final dimension.
const size_t xSize = 10, ySize = 10;
int* pointer = new int*(x); // the first level of indirection.
for (size_t i = 0; i < x; ++i) {
pointer[i] = new int(y);
}
pointer[0][0] = 0;
for (size_t i = 0; i < x; ++i) {
delete[] pointer[i];
}
delete[] pointer;
This last is more-or-less doing the same work, it just creates more memory fragmentation than the former.
-----------EDIT-----------
To answer the question "why do I not have 10" you're probably compiling in 64-bit mode, which means that "x" is an array of 10 pointers-to-int, and because you're in 64-bit mode, pointers are 64-bits long, while ints are 32 bits.
The C++ equivalent of your Fortran code is:
int cols, rows;
if ( !(std::cin >> cols >> rows) )
// error handling...
std::vector<double> A(cols * rows);
To access an element of this array you would need to write A[r * rows + c] (or you could do it in a column-major fashion, that's up to you).
The element access is a bit clunky, so you could write a class that wraps up holding this vector and provides a 2-D accessor method.
In fact your best bet is to find a free library that already does this, instead of reinventing the wheel. There isn't a standard Matrix class in C++, because somebody would always want a different option (e.g. some would want row-major storage, some column-major, particular operations provided, etc. etc.)
Someone suggested boost::multi_array; that stores all its data contiguously in row-major order and is probably suitable. If you want standard matrix operations consider something like Eigen, again there are a lot of alternatives out there.
If you want to roll your own then it could look like:
struct FortranArray2D // actually easily extensible to any number of dimensions
{
FortranArray2D(size_t n_cols, size_t n_rows)
: n_cols(n_cols), n_rows(n_rows), content(n_cols * n_rows) { }
double &operator()(size_t col, size_t row)
{ return content.at(row * n_rows + col); }
void resize(size_t new_cols, size_t new_rows)
{
FortranArray2D temp(new_cols, new_rows);
// insert some logic to move values from old to new...
*this = std::move(temp);
}
private:
size_t n_rows, n_cols;
std::vector<double> content;
};
Note in particular that by avoiding new you avoid the thousand and one headaches that come with manual memory management. Your class is copyable and movable by default. You could add further methods to replicate any functionality that the Fortran array has which you need.
int ** x;
x = new int* [10];
for(int i = 0; i < 10; i++)
x[i] = new int[5];
Unfortunately you'll have to store the size of matrix somewhere else.
C/C++ won't do it for you. sizeof() works only when compiler knows the size, which is not true in dynamic arrays.
And if you wan to achieve it with something more safe than dynamic arrays:
#include <vector>
// ...
std::vector<std::vector<int>> vect(10, std::vector<int>(5));
vect[3][2] = 1;
How to make a 2d pointer like **check point a 2d array like
mycheck[][]?
How to convert a 1d like check[16], to 2d array like mycheck[4][4]?
My attempt
float (*mycheck)[4] = (float (*)[4]) check;
But if second time I want to use mycheck again for some other 1d array, how can I do? My attempt:
float (*mycheck)[4] = (float (*)[4]) other1darray;
this will definitely give a re-declaration error.
The answer to the first question is that you cannot do that. All you can do is allocate some memory and copy the data over.
The answer to the second question is very simple
mycheck = (float (*)[4]) other1darray;
You only have to declare variables once, after that just use the variable name.
Array a[] decays to a pointer to the first element when you drop the []. This does not happen recursively, in other words, it doesn't work for a[][].
Secondly, you can't assign arrays in C. You can ONLY initialize them. You will have to set each member yourself.
You can create a 2D array in C like this.
Use a typedef to make it easier.
typedef int **matrix;
matrix create2Darray(int row, int col)
{
int idx;
matrix m = malloc(row * sizeof(int*));
for (idx = 0; idx < row; ++idx)
{
m[idx] = malloc(col * sizeof(int));
}
return m;
}
And then call this in another function;
matrix check = create2Darray(2, 2);
To assign a 1D array to a 2D array you can assign the pointers to the right position in the array. An example below. It also show how to create a 2D array dynamically, but I commented it out, since it is not needed for the example.
#include <stdio.h>
#include <stdlib.h>
int main()
{
float **matrix;
float *array;
array = (float *) malloc(16 * sizeof(float));
for (size_t idx = 0; idx != 16; ++idx)
{
array[idx] = idx;
}
matrix = (float **) malloc(4 * sizeof(float *));
for (size_t idx = 0; idx != 4; ++idx)
{
// matrix[idx] = malloc(4 * sizeof(int));
matrix[idx] = &array[idx * 4];
}
for (size_t row = 0; row != 4; ++row)
{
for (size_t col = 0; col != 4; ++col)
{
printf("%.1f ", matrix[row][col]);
}
printf("\n");
}
}
Note: this makes the 1D array and 2D array point to the same memory. If you change something in the 1D it also changes in the 2D and vice-versa. If you don't want this, first copy the array.
Suppose I have an arbitrary size array of integer values that specify the number of elements for each dimension (level) of the array to be allocated, how do I allocate the array without resorting to recursion? It's preferable to do it without recursion to avoid stack overflow.
So, for example, how to complete a function like this:
template <typename Type>
void* allocMulti (int numDim, int* numElementsPerDim)
{
// 'Type' if one-dimensional, should be 'void*' otherwise
void* multiArray = new Type[numElementsPerDim[0]];
// ...
return multiArray;
}
I'm looking for a general algorithm that would cover languages without direct memory access.
If the array is actually a matrix (e.g. length AxB and not a list of arrays of different lengths), then you could allocate a single array of length A*B instead of an array of length A where each position is a pointer to an array of length B.
This could also improve performance, as the memory is continuous (less paging).
You would have to access each cells using a[y * B + x] instead of a[y][x] though (assuming dim(a,0) = A and dim(a,1) = B.
My C++ my be a bit rusty, however, I believe this sort of approach may work:
T* AllocateMatrix(int dims, int[] dimLengths)
{
// Assert dims >= 1
int length = dims[0];
for (int d = 1; d < dims; d++)
length *= dims[d];
return new T[length];
}
*T AccessMatrix(T* matrix, int dims, int[] dimLengths, int[] pos)
{
// Assert dims >= 1
int p = pos[0];
for (int d = 1; d < dims; d++)
{
p = p * dimLengths[d] + pos[d];
}
return &matrix[p];
}
Here's an approach: Allocate the data values as a block, then all the rows of (int *) as a block, then the rows of (int **) as a block, etc.
a) Allocate all the data values as a block. If you have nDim dimensions in the array elementsPerDim, there are prod = product(elementsPerDim, nDim) data values (which you can easily calculate), so you need to allocate:
int prod = product(elementsPerDim, nDim);
int * intblock = calloc(prod, sizeof(int));
b) Allocate all the (int*). Their number is equal to the product of all the dimensions except the last one, so you can simply call your product() function with length nDim-1. So there are product(elementsPerDim, nDim-1) such values, each of size sizeof (int*). Let's allocate them:
int npointers = product(elementsPerDim, nDim-1);
int ** ptrblock = calloc(npointers, sizeof (int *));
Now you must initialize them to point into your block from the previous step. Each pointer gets a non-overlapping block of elementsPerDim[nDim-2] ints, like this:
int rowlength = elementsPerDim[nDim-2];
for (int i=0; i < npointers; i++)
ptrblock[i] = & intblock[i * rowlength]; /* a.k.a. intblock + i*rowlength */
c) Iterate step b backwards until you run out of dimensions. I.e., follow up step (b) with this loop:
void ** prev_block = (void **) ptrblock;
void ** curblock;
for (int d = nDim-2; d > 0; d--) {
int npointers = product(elementsPerDim, d);
curblock = calloc(npointers, sizeof (void **));
int rowlength = elementsPerDim[d-1];
for (int i=0; i < npointers; i++)
curblock[i] = & prev_block[i * rowlength];
prev_block = curblock; /* get ready for the next round */
}
When you're done, curblock will be an array of pointers pointing into the block of second-level pointers, and so on down to the block of ints. You can use normal array notation to dereference them:
ptrblock[3][2][15], etc.
I may have gotten an index off by one somewhere, but this should be the algorithm. You'll notice this is in C, and uses void ** instead of stacking the number of dereferences. You did say you were interested in the algorithm, not in type golf... (It should work as long as all pointers have the same size on your machine.)
How should an array of constant size:
const int m = 5, n = 3;
int arr[m][n];
be passed to a function in a way which is both C89 and C++-compatible?
void func(const int m, const int n, int arr[][n]) { }
isn't valid C++ (giving errors such as "A parameter is not allowed" and "Variable 'n' was not declared in this scope"), even though the size of arr is determinate at compile-time. (It is valid C, however.) #defineing m and n works but is not preferred due to scope issues. Passing a pointer to the first element of the array leads to ugly code in the function body.
Feel free to take a look at this FAQ for context.
In C++, you can pass an array to a function with full type information intact by utilizing a template and an array reference function argument:
template <unsigned M, unsigned N>
void func (int (&arr)[M][N]) {
//...
}
The function prototype you are using is using a C99 feature called VLA to provide a dynamic binding of the array dimension. This is not a C++ feature, although some C++ compilers will allow it as an extension to the C++ language.
The C-FAQ was written before C99 was ratified, so the variable length array feature was not yet a standard feature of C. With a modern C compiler with VLA support, the function prototype you provided works just fine.
There is another alternative to use if you have an older compiler for which VLA support is not available. That is to treat the 2-D array as a flattened 1-D array, and use manual calculations to index the correct integer:
void func(const int m, const int n, void *p) {
int *a = p;
int i, j;
for (i = 0; i < m; ++i) {
for (j = 0; j < n; ++j) {
printf(" %d", a[i*n + j]);
}
puts("");
}
}
Then you call func(m, n, arr). In side the function, the expression
a[i*n + j]
steps over n ints i times, then steps over j ints. Since each row is n ints long, the calculation returns the ith row and the jth column, which corresponds precisely to arr[i][j].
I have tried this code:
void func(const int m, const int n, int arr[][n])
{
printf("%d\n", arr[4][2]);
}
int main()
{
const int m = 5, n = 3;
int arr[m][n];
arr[4][2] = 10;
func(m, n, arr);
}
and this work with no warnings
Your array arr[m][n] is not constant. However you have constant variables M and N. You should also define the arr[m][n] as a constant and not just an int array.
You may want to consider dynamicaly allocating your array so that you can just pass the pointer address down.
const int m = 5, n = 3;
int i = 0;
int* *arr; //Pointer to an integer pointer (Note can also be int **arr or int** arr)
arr = malloc(sizeof(int*)*(m+1)); //I add one because I am assuming that 'm' does not account for the terminating null character. But if you do not need a terminating null then you can remove this and the perantheses around the 'm'.
for(i = 0; i < m; i++)
{
arr[i] = malloc(sizeof(int*)*(n+1)); //Same as before
}
The inital malloc() call allocates memory for an array of integer arrays or said in another way, it allocates a pointer to a series of other pointers. The for loop will allocate an integer array of 'm' size for each element of the original array or said another way it will allocate space for every pointer address pointed to by the original pointer address. I left out error checking in order to simplfy my example but here is the same example with error checking.
const int m = 5, n = 3;
int i = 0;
int* *arr = NULL;
if((arr = malloc(sizeof(int*)*(m+1))) == NULL)
{
perror("ERROR(1): Failed to allocate memory for the initial pointer address ");
return 1;
}
for(i = 0; i < m; i++)
{
if((arr = malloc(sizeof(int*)*(m+1))) == NULL)
{
perror("ERROR(2): Failed to allocate memory for a subsequent pointer address ");
return 2;
}
}
Now that you have dynamicaly allocated your array you can just pass the pointer address.
int* *arr in the following the way.
void fun(const int n, const int m, int* *arr) {}
Also you don't necessarily have to keep track of the size of your arrays if the sizes are constant and if you use null terminated arrays. You just have to malloc the array using the constant integer variable's actual value and then check for the terminating null byte when iterating threw the array.
int* *arr = NULL;
if((arr = malloc(sizeof(int*)*6)) == NULL)'m'+1 = 6;
{
perror("ERROR(1): Failed to allocate memory for the initial pointer address ");
return 1;
}
for(i = 0; i < m; i++)
{
if((arr = malloc(sizeof(int*)*4) == NULL)//'n'+1 = 4
{
perror("ERROR(2): Failed to allocate memory for a subsequent pointer address ");
return 2;
}
}
You can then display the entire two dimensional array in the following way. Note that '\000' is the octagonal value for a null byte(00000000).
int i, j;
for(i = 0; arr[i] != '\000'; i++)
{
for(j = 0; arr[i][j] != '\000'; j++)
{
printf("%i ", arr[i][j]); //Prints the current element of the current array
}
printf("\n"); //This just ends the line so that each of the arrays is printed on it's own line.
}
Of course the above mentioned loops would have the same result as the following.
int i, j;
int m = 5;
int n = 3;
for(i = 0; i < m; i++)
{
for(j = 0; i < n; j++)
{
printf("%i ", arr[i][j]); //Prints the current element of the current array
}
printf("\n"); //This just ends the line so that each of the arrays is printed on it's own line.
}
Which means, in most situations there is no need for keeping track of an array's size but there are situations in which it is necessary. For example if one your arrays could possible contain a null byte other than the terminating null byte. The new null byte would shorten the array's size to the index of the new null byte. If you have any questions or comments feel free to comment below or message me.
The problem here is the "missing" support for dynamic arrays in C++.
const int m = 5, n = 3;
int arr[m][n];
Works since m and n are compile time constant and accessible directly at the declaration of the array.
void func(const int m, const int n, int arr[][n]) { }
The compiler handles your function regardless of where it is called in first place.
Therefore n is unknown/variable and thus prohibited as a array dimensionality.
The following example won't work too because of the very same reason:
void foo (const int n)
{
int arr[n]; // error, n is const but not compile time constant
}
int main (void)
{
foo(4);
}
jxh answered what to do about it.
I must implement a bunch of methods that allocates, modify and free a 2D array of c-style string. I cannot use string nor vectors nor any stl container.
getNewMat :
char*** getNewMat(int w, int h){
char*** newMat = new char**[h];
for(int i = 0 ; i < h ; i++){
newMat[i] = new char*[w];
for(int j = 0 ; j < w ; j++)
newMat[i][j] = NULL;
}
return newMat;
}
fillMat
void fillMat(char***mat, int x, int y, char* newEl){
mat[y][x] = newEl; //this will produce a segfault (even with good index)
}
showMat :
void showMat(char*** mat, int w, int h){
for(int i = 0 ; i < h ; i++){
for(int j = 0 ; j < w ; j++)
cout << mat[i][j];
}
cout << endl;
}
so, can anyone please tell me what's wrong with this?
In your fillMat method you do this:
mat[y][x] = newEl;
Where x and y are the dimensions of the two ranks of the array. That line will cause a segmentation fault because you're going outside the bounds of the array. mat is indexed from 0 to length - 1 and setting by x and y is going 1 outside the bounds of the array.
Maybe you meant to loop through and set them:
for (int i = 0; i < y; ++i)
{
for (int k = 0; k < x; ++k)
mat[i][k] = newEl;
}
Moreover, inside your showMat function you have this:
cout << showMat[i][j];
I think you meant for that to be mat:
cout << mat[i][j];
newMat[i][j] = NULL - it's a bad idea. In showMat you will try to dereference a NULL pointer - this is UB and may cause a segfault.
char* - it's not a string - it's just a pointer to char, that may points to memory location, where can be beginning of string. If you want to work with it like with a string, you should allocate memory for it too.
mat[y][x] = newEl - it's a bad idea too. As I already said, char* is not a string, so, you can't just use assignment operator to copy data from one C-string into another. You should use std::copy or std::strncpy.
Do not forget to free allocated memory after using.
You should implement your own string class - it's the better solution, I can see there. At least, because it simpler and easier to understand.
I must implement a bunch of methods that allocates, modify and free a
2D array of c-style string. ...snip... so, can anyone please tell me
what's wrong with this?
A "c-style string" isn't a type. It's a representation of data within a type. '\0'-terminated strings are typically stored within char arrays, but you could store one just as easily in a unsigned int array. For example:
unsigned int message[32] = { 0 };
strcpy((char *) message, "Hello, world!");
printf("%s\n", (char *) message);
I discourage programming like this, however micro-optimistic the benefits may seem. It's also possible to store a string in something that isn't an array. Consider that a char might be suitable for storing an empty string:
char x = '\0';
printf("%s\n", &x);
It's reasonable to assume that you meant "an array of array of array of char", when you said "2D array of c-style string". Let us carry on in that direction.
I don't know a lot about C++, but there's a list of property of arrays which you probably haven't thought about in your quest to mimic actual arrays. I'll summarise these using assertions:
#define x 7
#define y 13
#define z 1
char foo[x][y][z] = { 0 };
assert((char *) foo == foo[0]);
assert(sizeof foo == (char *) &foo[1] - (char *) &foo[0]);
assert(sizeof foo == x * y * z);
I'm not sure if you'll be able to solve your problem with any of these assertions passing in C++, but I'm open for any input from others as to hints as to how one might...
Arrays are contiguous. This means that newMat[x] + w and newMat[x+1], for values of x in 0 .. h-1. In your code, this isn't a reality, because you allocate newMat[x] and newMat[x+1] separately. Similarly, it is expected that newMay[0][y] == newMat[0][y+1] + n, where n is the maximum length of your strings. This can be a problem when using generic array sorting algorithms, because they might rely upon your arrays being contiguous.
The closest you might come to solving this problem seems to involve allocating only once per dimension, rather than h times for the first dimension and w for the second. This would look something like this:
char ***getNewMat(size_t w, size_t h, size_t n){
char ***newMat = new char **[h];
newMat[0] = new char *[h*w];
newMat[0][0] = new char[h*w*n];
for(size_t i = 0; i < h; i++){
newMat[i] = newMat[0] + i * w;
for (size_t j = 0; j < w; j++) {
newMat[i][j] = newMat[0][0] + i * w * n + j * n;
}
}
return newMat;
}
One side-effect of arrays being contiguous is that you can't assign C-style strings by merely changing the pointer within the array to point to a different location. The pointer is the result of a conversion from an array expression to a pointer expression which isn't an lvalue. As I said earlier, I don't know much about C++, but in C that means the following code can't compile:
char foo[x][y][z] = { 0 };
foo[a][b] = "hello";
However, the following code can compile:
char *foo[x][y] = { 0 };
foo[a][b] = "hello";
The former might constitute an array of C-style strings, but the latter can't because of the contiguity rule we've covered, and the fact that it starts off with most if it's elements pointing to NULL, a pointer which can't point to anything let alone strings. There might be some operator overloading magic you can perform to permit the former to compile. I'm also open for any hints in the right direction to provide an example for the OP, here.