Related
I don't quite understand what does it mean to assign one pointer to another pointer? Here **p is an array of pointers/2D array, then p[0] is assigned to p[1], do both of them point to same address?
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i,j;
int **p = (int **)malloc(2 * sizeof(int *));
p[0] = (int *)malloc(2 * sizeof(int));
p[1] = p[0];
for(i = 0; i < 2; i++)
for(j = 0; j < 2; j++)
p[i][j] = i + j;
printf("%d",p[0][0]);
return 0;
}
I expected output to be 0 but it's actually 1, why?
The both pointers p[0] and p[1] have the same value after the expression statement
p[1] = p[0];
So where the expression p[1] is used it can be substituted for the expression p[0] because the both expressions have the same value and vice versa.
In this loop
for(i = 0; i < 2; i++)
for(j = 0; j < 2; j++)
p[i][j] = i + j;
when i is equal to 1 and j is equal to 0 (given that p[1] is the same as p[0].) So p[1][0] is equivalent to p[0][0] and equal to i + j that is 1.
In fact this loop
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
p[i][j] = i + j;
is equivalent to the loop
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
p[0][j] = i + j;
Thus the second iteration of the outer loop rewrites the values stored in p[0][0] and p[0][1] after the first iteration.
In the second iteration we have i is equal 1. So for j in the range [0, 1] we have
p[0][0] = i + j (when i == 1 and j == 0 ) = 1
and
p[0][1] = i + j (when i == 1 and j == 1 ) = 2
I don't quite understand what does it mean to assign one pointer to
another pointer? Here **p is an array of pointers/2D array, then p[0]
is assigned to p[1], do both of them point to same address?
Yes. But it's probably more useful conceptually to ignore the significance of the values of p[0] and p[1] (that the values are pointers, and what those values point to) to focus instead on the fact that after the assignment, their values are the same. That the values then point to the same thing follows from that.
Note well that I have intentionally chosen wording that distinguishes between designators for storage locations (e.g. "p[1]") and the contents of those locations -- their values. We often use language that blurs the distinction because it's cumbersome to speak precisely in that way, but it is essential that you maintain the distinction in your head. Formally, it is not the expression p[1] or even the storage location it designates that is a pointer, but rather the value stored there. The assignment operator copies that value to another storage location, so naturally, if the contents of that other storage location are interpreted according to the same data type, then they have the same meaning as the original.
I expected output to be 0 but it's actually 1, why?
Because after having set p[1] = p[0], the expression p[1][0] designates the same object that p[0][0] does. After you assign p[1][0] = 1 + 0, you can therefore read back the resulting value (1) from either p[1][0] or p[0][0].
do both of them point to same address?
Yes.
Here's a modification of your program which should make this more clear:
int main(void) {
int i,j;
int **p = (int **)malloc(2 * sizeof(int *));
p[0] = (int *)malloc(2 * sizeof(int));
p[1] = p[0];
for(i = 0; i < 2; i++)
for(j = 0; j < 2; j++)
p[i][j] = i + j;
for(i = 0; i < 2; i++) {
printf("%d (%p)\t", i, p[i]);
for(j = 0; j < 2; j++)
printf("%d ", p[i][j]);
printf("\n");
}
return 0;
}
Here we print out the pointer values p[0] and p[1], and all four cells of the "two-dimensional" array -- except that, yes, there are really only two cells, doing double duty for each of the two rows. On my system, this prints out
0 (0x7f92ca402710) 1 2
1 (0x7f92ca402710) 1 2
and you can clearly see that the two pointers are the same.
We can think of this, in memory, as looking something like this:
+-------+
p: | * |
+---|---+
|
v
+-------+ +---+---+
| *----------+--->| 1 | 2 |
+-------+ / +---+---+
| *--------'
+-------+
If, on the other hand, you called malloc three times, instead of two, like this:
int **p = (int **)malloc(2 * sizeof(int *));
p[0] = (int *)malloc(2 * sizeof(int));
p[1] = (int *)malloc(2 * sizeof(int));
you would get a printout more like this:
0 (0x7fb747402710) 0 1
1 (0x7fb747402720) 1 2
and you would get a picture in memory something like this:
+-------+
p: | * |
+---|---+
|
v
+-------+ +---+---+
| *-------------->| 0 | 1 |
+-------+ +---+---+
| *--------.
+-------+ \ +---+---+
'--->| 1 | 2 |
+---+---+
This question has been answered here but for me some things are still left open and the discussion is too old for anyone to reply to comments, besides I wanted to ask what was wrong in my implementation.
Anyway, the problem setting. I want to allocate space for a 3D array of dimensions 3, h, w. What I am doing:
int ***a = (int***)malloc(3 * sizeof(int*));
for (int i = 0; i < 3; i++)
{
a[i] = (int **) malloc(height * sizeof(int*));
}
I understand this as creating first space for three 3 int** and then allocating height int**;
Then :
for (int i = 0; i < 3; i++)
{
a[i][0] = (int *) malloc(height * width * sizeof(int *));
for (int j = 1; j < height; j++)
{
a[i][j] = a[i][j-1] + width;
}
}
So now I think that each a[i][0] is basically space for a 2D array of sizes height and width; furthermore the rest of a[i][j] for j != 0 is initialized to the j th row of a[i][0];
then I initialize values by:
for (int c = 0; c < 3; c++){
for (int i = 0; i < height; i++){
for (int j = 0; j < width; j++){
a[c][i][j] = i+j+c;
This however is incorrect. I wanted to do this as an extension exercise to allocating a 2D array in the following way:
int *a[height];
a[0] = (int*)malloc(height * width * sizeof(int));
for (int i = 1; i < height; i++){
a[i] = a[i-1] + widht;
}
This way a is an array of pointers of length height, a[0] is allocated for the whole 2D array and the consequent pointers point to their respective rows and I can write to them simply by calling a[i][j] = some number;
What I want to do is extend this 2D idea to the 3D case, but I am sadly failing.
int ***a = (int***)malloc(3 * sizeof(int*));
This doesn't allocate anything meaningful. In particular, it does not allocate a 3D array, or even a part of a 3D array. It allocates space for 3 integer pointers, which doesn't make any sense. Neither does a int*** make any sense.
Instead, allocate a 3D array:
int (*a)[y][z] = malloc( sizeof(int[x][y][z]) );
for(int i=0; i<x; i++)
for(int j=0; j<y; j++)
for(int k=0; k<z; k++)
a[i][j][k] = something;
free(a);
EDIT (any sarcastic tone in the below text is fully intentional)
For the benefit of the three-star programmers who refuse to accept any solution with less than three levels of indirection, here is an example of how to properly do three-star programming.
Unfortunately it does not yet contain non-conditional branching upwards, memory leaks or complex pointer arithmetic. But for a three-star programmer, such joyful things are of course trivial to add later. It does however guarantee data cache misses, so that the program will run with equal performance as a system without any data cache.
One may also consider to pass the allocated pointer-to-pointer-to-pointer as a parameter, thus achieving 4 stars!!!
[As in, the pointer-to-pointer method is very bad practice, but if you for reasons unknown insist on using it, you might as well do it proper.]
#include <stdlib.h>
#include <stdio.h>
void free_int_3d (int*** ppp, size_t X, size_t Y, size_t Z)
{
(void)Z;
if(ppp == NULL)
{
return ;
}
for(size_t x=0; x < X; x++)
{
if(ppp[x] != NULL)
{
for(size_t y=0; y < Y; y++)
{
if(ppp[x][y] != NULL)
{
free(ppp[x][y]);
}
}
free(ppp[x]);
}
}
free(ppp);
}
#define malloc_with_cleanup(ptr, size) \
ptr = malloc(size); \
if(ptr == NULL) \
{ \
free_int_3d(result, X, Y, Z); \
return NULL; \
}
int*** int_alloc_3d (size_t X, size_t Y, size_t Z)
{
int*** result = NULL;
malloc_with_cleanup(result, sizeof(int**[X]));
for(size_t x=0; x<X; x++)
{
malloc_with_cleanup(result[x], sizeof(int*[Y]));
for(size_t y=0; y<Y; y++)
{
malloc_with_cleanup(result[x][y], sizeof(int[Z]));
}
}
return result;
}
int main (void)
{
const size_t X = 5;
const size_t Y = 3;
const size_t Z = 4;
int*** ppp = int_alloc_3d(X, Y, Z);
for(size_t i=0; i<X; i++)
{
for(size_t j=0; j<Y; j++)
{
for(size_t k=0; k<Z; k++)
{
ppp[i][j][k] = (int)k;
printf("%d ", ppp[i][j][k]);
}
printf("\n");
}
printf("\n");
}
free_int_3d(ppp, X, Y, Z);
return 0;
}
Output:
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
Let's visualize what you're trying to do first, and then I'll show the code to get there:
int *** int ** int * int
+---+ +---+ +---+ +---+
a | | ---> a[0] | | ------> a[0][0] | | ----> a[0][0][0] | |
+---+ +---+ +---+ +---+
a[1] | | ----+ a[0][1] | | -+ a[0][0][1] | |
+---+ | +---+ | +---+
a[2] | | | ... | ...
+---+ | +---+ | +---+
| a[0][h-1] | | | a[0][0][w-1] | |
| +---+ | +---+
| |
| +---+ | +---+
+-> a[1][0] | | +--> a[0][1][0] | |
+---+ +---+
a[1][1] | | a[0][1][1] | |
+---+ +---+
... ...
+---+ +---+
a[1][h-1] | | a[0][1][w-1] | |
+---+ +---+
Types:
Each a[i][j][k] has type int;
Each a[i][j] points to the first element of a sequence of int objects, so it must have type int *;
Each a[i] points to the first element of a sequence of int * objects, so it must have type int **;
a points to the first element of a sequence of int ** objects, so it must have type int ***.
Since you're doing a piecemeal nested allocation, you need to check the result of each malloc call, and if there's an error, you will want to clean up any previously allocated memory before bailing out, otherwise you risk memory leaks. Unfortunately, there's no really clean or elegant way to do that - you either carry a flag around and do a bunch of extra tests, or you throw in a couple of gotos. I'm going to show two different approaches, neither of which is all that pretty.
First method - as we allocate each a[i], we also allocate each a[i][j] (a "depth-first" approach) and initialize each a[i][j][k]. If an allocation on a[i][j] fails, we must deallocate all of a[i][0] through a[i][j-1], then deallocate a[i], then repeat the process for each of a[0] through a[i-1]:
/**
* Depth-first approach: allocate each a[i][j] with each a[i]
*/
int ***alloc1( size_t pages, size_t height, size_t width )
{
size_t i, j, k;
int ***a = malloc( sizeof *a * pages ); // allocate space for N int **
// objects, where N == pages
if ( !a )
return NULL;
for ( i = 0; i < pages; i++ )
{
a[i] = malloc( sizeof *a[i] * height ); // for each a[i], allocate space for
if ( !a[i] ) // N int * objects, where N == height
goto cleanup_1;
for ( j = 0; j < height; j++ )
{
a[i][j] = malloc( sizeof *a[i][j] * width ); // for each a[i][j], allocate
if ( !a[i][j] ) // space for N int objects,
goto cleanup_2; // where N == w
for ( k = 0; k < width; k++ )
a[i][j][k] = initial_value( i, j, k );
}
}
goto done;
/**
* Free all of a[i][0] through a[i][j-1], then free a[i]
*/
cleanup_2:
while ( j-- )
free( a[i][j] );
free( a[i] );
/**
* Free all of a[0] through a[i-1], then free a
*/
cleanup_1:
while ( i-- )
{
j = height;
goto cleanup_2;
}
free( a );
a = NULL;
done:
return a; // at this point, a is either a valid pointer or NULL.
}
Yes, this code contains gotos, and it violates one of my pet rules (never branch backwards). But, there's a fairly clean separation between the allocation code and the cleanup code, and we don't repeat ourselves in the cleanup sections. cleanup_2 deallocates all the rows within a page, along with the page itself; cleanup_1 deallocates all of the pages. cleanup_2 "falls through" into cleanup_1.
Here's a second method - first we allocate all a[i] before allocating any a[i][j], then we make sure all a[i][j] were successfully allocated before initializing the array contents.
/**
* Breadth-first approach; allocate all a[i], then all a[i][j], then initialize
*/
int ***alloc2( size_t pages, size_t height, size_t width )
{
size_t i, j, k;
/**
* Allocate space for N int ** objects, where N == pages
*/
int ***a = malloc( sizeof *a * pages );
if ( !a )
return NULL; // allocation failed for initial sequence, return NULL
for ( i = 0; i < pages; i++ ) // For each a[i], allocate N objects of type
{ // int *, where N == height
a[i] = malloc( sizeof *a[i] * height );
if ( !a[i] )
break;
}
if ( i < pages )
{
while ( i-- ) // allocation of a[i] failed, free up a[0] through a[i-1]
free( a[i] );
free( a ); // free a
return NULL;
}
for ( i = 0; i < pages; i++ )
{
for ( j = 0; j < height; j++ )
{
a[i][j] = malloc( sizeof *a[i][j] * width ); // for each a[i][j], allocate
if ( !a[i][j] ) // space for N int objects,
break; // where N == width
}
}
if ( j < h )
{
do
{
while ( j-- ) // allocation of a[i][j] failed, free up a[i][0] through a[i][j-1]
free( a[i][j] ); // repeat for all of a[0] through a[i-1].
free( a[i] );
j = h;
} while ( i-- );
free( a ); // free a
return NULL;
}
/**
* All allocations were successful, initialize array contents
*/
for ( i = 0; i < pages; i++ )
for ( j = 0; j < height; j++ )
for ( k = 0; k < width; k++ )
a[i][j][k] = initial_value( i, j, k );
return a;
}
No gotos! But the code doesn't "flow" as well IMO. There's not as clean a separation between allocation and cleanup code, and there's a bit of repetitiveness between the cleanup sections.
Note that for both methods, the memory is not contiguous - the element immediately following a[0][0][h-1] is not going to be a[0][1][0]. If you need all your array elements to be adjacent in memory, you will need to use the method shown by the others:
int (*a)[h][w] = malloc( sizeof *a * 3 );
with the caveat that if h and w are large, you may not have enough contiguously allocated memory to satsify the request.
To solve OP's higher goal, I suspect a pointer to a 2D array would suffice #mch
int (*a)[height][width] = malloc(sizeof *a * 3);
a[c][i][j] = i + j + c;
But per OP's request....
... to allocate space for a 3D array ...
How would code typically create a 3D array?
The below code does that using a variable length array available in C99.
(VLA is optionally support in C11)
int a1[3][height][width];
// and assign it accordingly
for (int c = 0; c < 3; c++) {
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
a1[c][i][j] = i + j + c;
Yet OP wants to allocate space for such an array.
malloc() returns a pointer. A pointer to allocated memory. So we need to create a pointer to such an array. Once we have done that, allocation and element assignment is easy.
int (*a2)[3][height][width] = malloc(sizeof *a2);
// 3 nested for loop as above
(*a2)[c][i][j] = i + j + c;
Example code
void VT_3_dimensional_array(int height, int width) {
assert(height > 0 && width > 0);
int (*a2)[3][height][width] = malloc(sizeof *a2);
printf("%p %zu\n", (void*) a2, sizeof *a2);
if (a2 == NULL) {
perror("Out of memory");
exit(EXIT_FAILURE);
}
for (int c = 0; c < 3; c++) {
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
(*a2)[c][i][j] = i + j + c;
}
}
}
// use a;
for (int c = 0; c < 3; c++) {
for (int i = 0; i < height; i++) {
putchar('(');
for (int j = 0; j < width; j++) {
printf(" %X", (*a2)[c][i][j]);
}
putchar(')');
}
puts("");
}
free(a2);
}
int main() {
VT_3_dimensional_array(5, 7);
return 0;
}
output
0x80071980 420
( 0 1 2 3 4 5 6)( 1 2 3 4 5 6 7)( 2 3 4 5 6 7 8)( 3 4 5 6 7 8 9)( 4 5 6 7 8 9 A)
( 1 2 3 4 5 6 7)( 2 3 4 5 6 7 8)( 3 4 5 6 7 8 9)( 4 5 6 7 8 9 A)( 5 6 7 8 9 A B)
( 2 3 4 5 6 7 8)( 3 4 5 6 7 8 9)( 4 5 6 7 8 9 A)( 5 6 7 8 9 A B)( 6 7 8 9 A B C)
Note, technically OP's code is using the wrong size. Usually sizeof(int*) will be the same as sizeof(int**), so no great harm. Yet it does indicate confusion about the allocation.
// wrong type -----------------------v should be int**
int ***a = (int***)malloc(3 * sizeof(int*));
I was searching for non-recursive odd-even-merge sort algorithm and found 2 sources:
a book from Sedgewick R.
this SO question
Both algorithms are identical but false. The resulting sorting network is not an odd-even-merge sort network.
Here is an image of the resulting network with 32 inputs. A vertical line between 2 horizontal lines means compare value a[x] with a[y], if greater then swap the values in the array.
(source: flylib.com)
(clickable)
I copied the code from Java to C and replaced the exch function by a printf to print the exchange candidates.
When one draws a diagram of the pairs, it can be seen that too many pairs are generated.
Has anyone an idea how to fix this algorithm?
Why do I need non-recursive version?
I want to transform this sorting network into hardware. It's easy to insert pipeline stages into a non-recursive algorithms.
I also investigated the recursive version, but it's too complex to transform the algorithm into pipelined hardware.
My C code:
#include <stdlib.h>
#include <stdio.h>
void sort(int l, int r)
{ int n = r-l+1;
for (int p=1; p<n; p+=p)
for (int k=p; k>0; k/=2)
for (int j=k%p; j+k<n; j+=(k+k))
for (int i=0; i<n-j-k; i++)
if ((j+i)/(p+p) == (j+i+k)/(p+p))
printf("%2i cmp %2i\n", l+j+i, l+j+i+k);
}
int main(char* argv, int args)
{ const int COUNT = 8;
sort(0, COUNT);
}
The result:
0 -o--------o-------------------------o---------------o-------------------------
| | | |
1 -o--------|-o------o----------------|-o-------------o-o-----------------------
| | | | | |
2 -o-o------o-|------o-o--------------|-|-o----o--------o-o---------------------
| | | | | | | | |
3 -o-o--------o--------o--------------|-|-|-o--|-o--------o-o-------o-----------
| | | | | | | |
4 -o-o-o----o---o----o-----o----------o-|-|-|--o-|-o--------o-o-----o-o---------
| | | | | | | | | | | | | |
5 -o-o-o----|-o-|-o--o-o---o-o---o------o-|-|----o-|-o--------o-o-----o-o---o---
| | | | | | | | | | | | | |
6 -o-o-o-o--o-|-o-|----o-o---o-o-o-o------o-|------o-|----------o-o-----o-o-o-o-
| | | | | | | | | | | | | |
7 -o-o-o-o----o---o------o-----o---o--------o--------o------------o-------o---o-
When I know the correct exchange pairs and the algorithm is equal to the image, I'll translate it into VHDL for tests on my hardware platform.
Other open source hardware sorting network implementations:
PoC.sort.sortnet.oddevensort
PoC.sort.sortnet.bitonicsort
Appendix:
Odd-even mergesort (a.k.a Batcher's sort) is like bitonic sort (not to confuse with Batcher's bitonic sort). But in hardware this algorithm has a better size complexity than bitonic sort, while latency is the same.
These algorithm can be implemented with good resource usage compared to fast software algorithms like quicksort.
Wikipedia: odd-even mergesort
Note:
Because sorting networks are static and independent of the input values, no compare and swap is needed to generate the network. That's one reason why it can be transformed into hardware. My code generates the indices for the compare operations. In hardware, these vertical connections will be replaced by compare and swap circuits. So unsorted data will travel throught the network and on the output side it will be sorted.
The following code works for arrays of any size and isn't recursive. It is a straight port from the implementation of the corresponding function in Perl's Algorithm::Networksort module. The implementation happens to correspond to the algorithm as described by Knuth in The Art of Computer Programming, vol 3 (algorithm 5.2.2M). It doesn't help to actually fix your algorithm, but it at least gives you a working non-recursive implementation of Batcher's odd-even mergesort with only three nested loops :)
#include <math.h>
#include <stdio.h>
void oddeven_merge_sort(int length)
{
int t = ceil(log2(length));
int p = pow(2, t - 1);
while (p > 0) {
int q = pow(2, t - 1);
int r = 0;
int d = p;
while (d > 0) {
for (int i = 0 ; i < length - d ; ++i) {
if ((i & p) == r) {
printf("%2i cmp %2i\n", i, i + d);
}
}
d = q - p;
q /= 2;
r = p;
}
p /= 2;
}
}
If you can get your hands on a copy of The Art of Computer Programming, vol 3, you will have a nice explanation of how and why the algorithm works, as well as a few additional details.
I think I found a solution. I checked it for length = 2, 4, 8, 16.
Here is my code:
void sort(int length)
{ int G = log2ceil(length); // number of groups
for (int g = 0; g < G; g++) // iterate groups
{ int B = 1 << (G - g - 1); // number of blocks
for (int b = 0; b < B; b++) // iterate blocks in a group
{ for (int s = 0; s <= g; s++) // iterate stages in a block
{ int d = 1 << (g - s); // compare distance
int J = (s == 0) ? 0 : d; // starting point
for (int j = J; j+d < (2<<g); j += 2*d) // iterate startpoints
{ for (int i = 0; i < d; i++) // shift startpoints
{ int x = (b * (length / B)) + j + i; // index 1
int y = x + d; // index 2
printf("%2i cmp %2i\n", x, y);
}
}
}
}
}
This solution introduces a fifth for-loop to handle subblocks in a group.
The j loop has a changed start and abort value to handle odd counts of post merge steps without generating doubled compare steps.
This is a fixed non-recursive subroutine.
void sort(int n)
{
for (int p = 1; p < n; p += p)
for (int k = p; k > 0; k /= 2)
for (int j = k % p; j + k < n; j += k + k)
//for (int i = 0; i < n - (j + k); i++) // wrong
for (int i = 0; i < k; i++) // correct
if ((i + j)/(p + p) == (i + j + k)/(p + p))
printf("%2i cmp %2i\n", i + j, i + j + k);
}
or
void sort(int n)
{
for (int p = 1; p < n; p += p)
for (int k = p; k > 0; k /= 2)
for (int j = 0; j < k; j++)
for (int i = k % p; i + k < n; i += k + k)
if ((i + j)/(p + p) == (i + j + k)/(p + p))
printf("%2i cmp %2i\n", i + j, i + j + k);
}
I am trying to make this program, but it is not working, its failing in attributing values for the second line of the aux matrix, and i cant see why, can anyone help me? Thanks!
Oh, and I have already putted some debugging lines, and, apparently, everything is fine, it's just not attributing the values.
#include <stdio.h>
#include <math.h>
void zerar(int n,int m[][n]) {
int i, j;
for (i=0; i<n; i++) {
for (j=0; j<n; j++) {
m[i][j] = 0;
}
}
}
void printm(int n, int matriz[][n]) {
int i,j;
for (i=0; i<n; i++) {
for (j=0; j<n; j++) {
printf("\t%d", matriz[i][j]);
}
printf("\n");
}
}
int det(int n, int m[][n]) {
int i, j, k, x, y, soma=0, aux[n][n];
zerar(n, aux);
if (n < 1) {}
else if (n == 1) {
return m[0][0];
}
else if (n == 2) {
soma = (m[0][0] * m[1][1]) - (m[0][1] * m[1][0]);
return soma;
}
else {
for (i=0; i<n; i++) {
for (j=1, x=0; j<n; j++) {
for (k=0, y=0; k<n; k++) {
if (k == i) {
continue;
}
else {
printf("\n\n");
printf("\nx=%d, y=%d, j=%d, k=%d, i=%d\n", x, y, j, k, i);
aux[x][y] = m[j][k];
printm(n-1, aux);
y++;
}
}
x++;
}
soma += m[0][i]*pow(-1, i+2)*det(n-1, aux);
}
return soma;
}
}
int main()
{
int m[3][3] = {{4, 3, 2}, {1, 4, 5}, {2, 1, 2}};
det(3, m);
printf("%d", det(3, m));
printf("\n\n");
printm(3, m);
printf("\n\n");
}
When the input matrix is of size n x n, the size of the auxiliary matrix, aux, needs to be n-1 x n-1.
Change
int i, j, k, x, y, soma=0, aux[n][n];
to
int i, j, k, x, y, soma=0, aux[n-1][n-1];
You said in a comment:
Can someone explain me please why it have to be that way?
If you use aux[n][n] and n is 3, the memory layout of the object is:
+---+---+---+---+---+---+---+---+---+
| | | | | | | | | |
+---+---+---+---+---+---+---+---+---+
and you fill up the data as though it is a 2 x 2 matrix.
0 1 2
+---+---+---+---+---+---+---+---+---+
| x | x | | x | x | | | | |
+---+---+---+---+---+---+---+---+---+
In the next recursive call, you treat that memory as though it is a 2 x 2 array.
0 1
+---+---+---+---+---+---+---+---+---+
| x | x | | x | x | | | | |
+---+---+---+---+---+---+---+---+---+
^ ^
| Ignored
Using uninitialized array element
In theory, the program is subject to undefined behavior if you use aux[n][n] instead of aux[n-1][n-1].
I am trying to recreate the game 2048 in C, but I can't get the algorithms to move or merge tiles together to function properly.
In the original 2048 game you would move tiles together like this:
2 | 2 | 4 | 4 4 | 8 | |
---+---+---+--- *swipes to the left* -> ---+---+---+---
8 | | 8 | 16| | |
So two tiles that are the same can merge into one tile that is twice the size. My version is almost the same, but instead of using numbers I use characters that increment by one when they merge, so[A|A] would merge to [B], etc. I did that only to not have to deal with varying size tiles.
So my board is stored as a 4*4 char array inside a struct I called grid (I know probably a bit redundant)
typedef struct grid {
char tiles[4][4];
} Grid;
I have tried to make algorithms to move and merge up, down, left and right, but they don't work properly.
void pushLeft(Grid * grid)
{
int i, j, k;
for(i = 0; i < 4; i++) //Row number i
{
for(j = 1; j < 4; j++) //Column number j
{
if(grid->tiles[i][j] != ' ') //tile is not empty
{
int flag = 1; //flag to prevent merging more than one level at a time
//Starting on column k, push tile as far to the left as possible
for(k = j; k > 0; k--)
{
if(grid->tiles[i][k-1] == ' ') //neighbor tile is empty
{
grid->tiles[i][k-1] = grid->tiles[i][k];
grid->tiles[i][k] = ' ';
}
else if(grid->tiles[i][k-1] == grid->tiles[i][k] && flag) //neighbor equals
{
grid->tiles[i][k-1]++;
grid->tiles[i][k] = ' ';
flag = 0;
}
else //Can't push or merge
{
flag = 1;
break;
}
}
}
} // Done with row
}
}
void pushRight(Grid * grid)
{
int i, j, k;
for(i = 0; i < 4; i++) //Row number i
{
for(j = 2; j >= 0; j--) //Column number j
{
if(grid->tiles[i][j] != ' ') //tile is not empty
{
int flag = 1; //flag to prevent merging more than one level at a time
//Starting on column k, push tile as far to the right as possible
for(k = j; k < 3; k++)
{
if(grid->tiles[i][k+1] == ' ') //neighbor tile is empty
{
grid->tiles[i][k+1] = grid->tiles[i][k];
grid->tiles[i][k] = ' ';
}
else if(grid->tiles[i][k+1] == grid->tiles[i][k] && flag) //neighbor equals
{
grid->tiles[i][k+1]++;
grid->tiles[i][k] = ' ';
flag = 0;
}
else //Can't push or merge
{
flag = 1;
break;
}
}
}
} // Done with row
}
}
void pushUp(Grid * grid)
{
int i, j, k;
for(i = 0; i < 4; i++) //Column number i
{
for(j = 1; j < 4; j++) //Row number j
{
if(grid->tiles[j][i] != ' ') //tile is not empty
{
int flag = 1; //flag to prevent merging more than one level at a time
//Starting on row k, push tile as far upwards as possible
for(k = j; k > 0; k--)
{
if(grid->tiles[k-1][i] == ' ') //neighbor tile is empty
{
grid->tiles[k-1][i] = grid->tiles[i][k];
grid->tiles[k][i] = ' ';
}
else if(grid->tiles[k-1][i] == grid->tiles[i][k] && flag) //neighbor equals
{
grid->tiles[k-1][i]++;
grid->tiles[k][i] = ' ';
flag = 0;
}
else //Can't push or merge
{
flag = 1;
break;
}
}
}
} // Done with column
}
}
void pushDown(Grid * grid)
{
int i, j, k;
for(i = 0; i < 4; i++) //Column number i
{
for(j = 2; j >= 0; j--) //Row number j
{
if(grid->tiles[j][i] != ' ') //tile is not empty
{
int flag = 1; //flag to prevent merging more than one level at a time
//Starting on row k, push tile as far down as possible
for(k = j; k < 3; k++)
{
if(grid->tiles[k+1][i] == ' ') //neighbor tile is empty
{
grid->tiles[k+1][i] = grid->tiles[i][k];
grid->tiles[k][i] = ' ';
}
else if(grid->tiles[k+1][i] == grid->tiles[i][k] && flag) //neighbor equals
{
grid->tiles[k+1][i]++;
grid->tiles[k][i] = ' ';
flag = 0;
}
else //Can't push or merge
{
flag = 1;
break;
}
}
}
} // Done with column
}
}
I tested these algorithms with some hardcoded testdata. The algorithm to push the tiles to the left seems to be working correctly. pushRight almost works, but it merges two levels at the same time, so [B|A|A] merges into [C] but should merge into [B|B].
pushUp seems to be almost always just wiping the entire board with empty tiles (spaces).
pushDows seems to be removing some tiles.
Does anyone see the problem or know a way to do this? I have thought about using recursive algorithms, but I just can't wrap my head around it.
I would personally break the swipe into two steps as the swipe left and swipe right are actually functionally the same regarding tile combination. The only difference is that the remaining tiles are bunched to either the left or the right depending on direction.
Below is a quick algorithm to replace two tiles with the a new one. I scan left->right and replace the left tile with the new tile, zero the right tile and then make sure I exclude this new tile from comparison:
typedef struct grid {
char tiles[4][4];
} Grid;
void eliminateHoriz (Grid* g)
{
int row, col, col2;
for (row=0; row<4; row++)
{
for (col=0; col<4; col++)
{
if (g->tiles[row][col])
{
for (col2=col+1; col2<4; col2++)
{
if (g->tiles[row][col2])
{
if (g->tiles[row][col] == g->tiles[row][col2])
{
g->tiles[row][col++] *= 2;
g->tiles[row][col2] = 0;
}
break;
}
}
}
}
}
}
void showGrid (Grid* g)
{
int row, col;
for (row=0; row<4; row++)
for (col=0; col<4; col++)
printf ("%4d%c",
g->tiles[row][col],
col == 3 ? '\n' : ' ');
printf ("\n");
}
int main()
{
Grid g = {{2,2,4,4,
8,0,8,0,
8,8,8,4,
2,2,2,2}};
showGrid (&g);
eliminateHoriz (&g);
showGrid (&g);
system ("pause");
return 0;
}
Output of this:
2 2 4 4
8 0 8 0
8 8 8 4
2 2 2 2
4 0 8 0
16 0 0 0
16 0 8 4
4 0 4 0
After this a simple compaction step could be made, or output realtime to a second buffer, or which ever. Less duplication.
I only have done the case of pushing the lines to the left, but it the same method for every direction. I took the code of the answer and modify it; take a look:
typedef struct grid {
int tiles[4][4];
} Grid;
/* Functions prototypes */
void pushLeft(Grid* grid);
void showGrid (Grid* g);
void find_great_tile(Grid* grid);
/* Main function */
int main()
{
Grid g = {{4,2,2,8,
2,8,2,2,
16,2,0,2,
128,128,64,64}};
/*
The sequence is:
--> Show the grid
--> PushLeft
--> Find great tile
--> PushLeft
--> Show the grid
*/
printf("\n\n\n\n");
showGrid (&g);
printf("\n\n\n\n");
pushLeft(&g);
showGrid (&g);
printf("\n\n\n\n");
find_great_tile(&g);
showGrid(&g);
printf("\n\n\n\n");
pushLeft(&g);
showGrid(&g);
printf("\n\n\n\n");
return 0;
}
/* Functions definitions */
void pushLeft(Grid* grid){
int row, col, col2;
for (row = 0; row < 4; row++)
{
for (col = 0; col < 4; col++)
{
if (!grid->tiles[row][col])
{
for (col2 = col+1; col2 < 4; col2++)
{
if (grid->tiles[row][col2])
{
/*
if (grid->tiles[row][col] == grid->tiles[row][col2])
{
grid->tiles[row][col++] *= 2;
grid->tiles[row][col2] = 0;
}
break;
*/
grid->tiles[row][col] = grid->tiles[row][col2];
grid->tiles[row][col2] = 0;
break;
}
}
}
}
}
}
void showGrid (Grid* grid){
int row, col;
for(row = 0; row < 4; row++){
fprintf(stdout, "\t\t |");
for(col = 0; col < 4; col++)
{
/*
In case there's any number in the matrix, it will print those numbers, otherwise, it'll print a space (it is the alternative of putting a 0)
*/
if(grid->tiles[row][col])
{
printf("%4d |", grid->tiles[row][col]);
}else
printf("%4c |", ' ');
}
fprintf(stdout, "\n\n");
}
}
void find_great_tile(Grid* grid){
int row, col, col2;
for(row = 0; row < 4; row++)
{
for(col = 0; col < 4; col++)
{
if(grid->tiles[row][col])
{
col2 = col+1;
if(grid->tiles[row][col2])
{
if(grid->tiles[row][col] == grid->tiles[row][col2])
{
grid->tiles[row][col++] *= 2;
grid->tiles[row][col2] = 0;
}
}
}
}
}
}
Output of this:
| 4 | 2 | 2 | 8 |
| 2 | 8 | 2 | 2 |
| 16 | 2 | | 2 |
| 128 | 128 | 64 | 64 |
| 4 | 2 | 2 | 8 |
| 2 | 8 | 2 | 2 |
| 16 | 2 | 2 | |
| 128 | 128 | 64 | 64 |
| 4 | 4 | | 8 |
| 2 | 8 | 4 | |
| 16 | 4 | | |
| 256 | | 128 | |
| 4 | 4 | 8 | |
| 2 | 8 | 4 | |
| 16 | 4 | | |
| 256 | 128 | | |
Of course, you can compress the steps doing:
--> PushLeft
--> FindGreatTile
--> PushLeft