Please, I am new to C programming, please help me this.There is some error in the following code?
Basically, how do we work with pointers to 2-D arrays...
#include<stdio.h>
#include<stdlib.h>
int* max5(int** p,int r,int c)
{
/* have to design a function which takes as argument pointer to a 2-D array and returns a pointer to an array which contains the 5 highest entries*/
}
int main()
{
int rows,coloumns;
printf("Enter the number of rows and coloumns separated by a space\n");
scanf("%d %d",&rows,&coloumns);
int **ptr;
ptr=(int**)malloc(sizeof(int*)*rows);
int i,j;
for(i=0;i<rows;i++)
{
for(j=0;j<coloumns;j++)
{
scanf("%d",*(ptr+i)+j);
printf("%d\n",*(*(ptr+i)+j));
}
}
free(ptr);
return 0;
}
In C, a 2D array is simply a regular array with different indexing. So, you have two options:
Allocate an array and index it as: array[I*n+j]
Allocate an array of arrays (which is what you seem to be trying to do)
The second you can achieve in the way shown in the related post: allocate matrix in C.
Dynamically allocating a 2D array can be done in several ways.
Assuming you have a C99 compiler or C2011 compiler that supports variable-length arrays, this is dead easy:
int rows, columns;
...
scanf("%d %d",&rows,&columns);
int array[rows][columns];
array is a variable-length array (VLA), meaning its dimensions are established at run time. To pass this to another function, the prototype would simply be
int *max5( int rows, int columns, int arr[rows][columns] ) { ... }
which can also be written as
int *max5( int rows, int columns, int (*arr)[columns] ) { ... }
I'll get into that second form below. Note that both rows and columns must be declared before you use them in the VLA declaraion.
You would call it as
int *maxarr = max5( rows, columns, array );
The advantage of this approach is that a) it's easy, and b) the memory for array is allocated contiguously (i.e., as a single, continuous block of memory) and released as soon as you exit it's enclosing scope; there's no need to manually free it. The disadvantage of this approach is that rows and columns can't be arbitrarily large, you can't initialize the array using regular initializer syntax (i.e., you can't write int array[rows][columns] = { ... };), and you can't define VLAs at file scope (i.e., as a global variable). There's also the problem that VLA support has always been a little spotty, and the 2011 standard now makes them optional, so it's not guaranteed they'll be supported everywhere.
If rows or columns is very large, or if for some other reason you want to allocate this memory from the heap, it's just slightly more complicated:
int rows, columns;
...
scanf("%d %d", rows, columns);
int (*array)[columns] = malloc( sizeof *array * rows );
In this version, array is a pointer to an N-element array of int (where N == columns), and we malloc space to hold M such arrays (where M == rows). Again, this takes advantage of VLA syntax, but instead of allocating the memory from the stack frame, we allocate it from the heap. Like in the first method, the memory is allocated contiguously. You would index into it as you would any other array:
array[i][j] = x;
When you're done with the array, you would free it as
free( array );
The prototype for max5 would be
int *max5( int rows, int columns, int (*arr)[columns] ) { ... }
Look familiar? That's the second version we used earlier when we were passing a 2D VLA.
Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted to an expression of type "pointer to T", and the value of the expression will be the address of the first element in the array.
In the first case where we declared array as a 2D VLA, when you call
int *maxarr = max5( rows, columns, array );
the expression array is converted from type "rows-element array of columns-element array of int" to "pointer to columns-element array of int", so what max5 receives is a pointer value, not an array. It just so happens that in the context of a function parameter declaration, T a[N] and T a[] are both interpreted as T *a; IOW, all three declare a as a pointer to T.
Thus, both
int *max5( int rows, int columns, int arr[rows][columns] ) { ... }
and
int *max5( int rows, int columns, int (*arr)[columns] ) { ... }
declare arr as a pointer to an array, not a 2D array. Similarly, both return a simple int *, as opposed to an array of int. The reason for this is simple; if you return a subarray using
return arr[i];
the expression arr[i] will have type "columns-element array of int"; by the rule above, that expression will be converted to type int *. C doesn't allow you to return array objects as arrays, nor can an array expression be the target of an assignment.
Now, what if you're working with a C89 compiler, or a C2011 compiler that doesn't support VLAs? In this case, you'd need to allocate the memory in a piecemeal fashion, like so:
int rows, columns;
int **array;
...
scanf("%d %d", rows, columns);
array = malloc( sizeof *array * rows );
if ( array )
{
int i;
for ( i = 0; i < rows; i++ )
{
array[i] = malloc( sizeof *array[i] * columns );
}
}
Your max5 prototype would now be
int *max5( int rows, int columns, int **arr );
You're not dealing with arrays or pointers to arrays anymore; you're dealing with a double pointer, which is not the same thing as a pointer to a 2D array. You can still subscript it as though it were a 2D array
arr[i][j] = x;
but it is not an array object in and of itself.
The most important thing to realize is that the memory is not guaranteed to be contiguous; rows may not be adjacent in memory, so you don't want to try to walk down rows using a pointer, like so:
int (*p)[columns] = arr;
while ( some_condition )
p++; // sets p to point to the next row
For an array that was allocated piecemeal, that sort of algorithm won't work.
Also, since the memory was allocated piecemeal, it needs to be freed piecemeal:
for ( i = 0; i < rows; i++ )
free(array[i]);
free( array );
Hope that helps.
this code fix your issue :
#include<stdio.h>
#include<stdlib.h>
int* max5(int** p,int r,int c)
{
/* have to design a function which takes as argument pointer to a 2-D array and returns a pointer to an array which contains the 5 highest entries*/
}
int main()
{
int rows,coloumns;
printf("Enter the number of rows and coloumns separated by a space\n");
scanf("%d %d",&rows,&coloumns);
int **ptr;
int i,j;
ptr=(int**)malloc(sizeof(int*)*rows);
for(j=0;j<coloumns;j++)
{
*(ptr+j) = (int *)malloc(sizeof(int)*coloumns);
}
for(i=0;i<rows;i++)
{
for(j=0;j<coloumns;j++)
{
scanf("%d",*(ptr+i)+j);
//*(*(ptr+j)+i) = i+j;
printf("%d\n",*(*(ptr+i)+j));
}
}
free(ptr);
return 0;
}
in fact you forget to allocate memory for columns !
You can allocate a 2D array in C by defining the type correctly:
int (*ptr)[coloumns] = calloc(sizeof(int) , rows * coloumns);
Then you can address it via ptr[i][j]. If your array does need to persist beyond the scope that allocated it and is fairly small then you can also allocate it on the stack
int ptr[rows][coloumns];
memset(ptr, 0, sizeof(ptr));
What you are trying to allocate is an array of pointers int (*)[coloumns] is not the same type as int ** although they can be both addressed through the use of the indexing operator [i][j]. The latter is a far more complicated data structure and is prone to more errors. I would advise against it unless you need variable row sizes.
Related
An example to illustrate:
#include <stdlib.h>
#include<stdio.h>
void simple_function(int s , int array[][s]);
int main(void){
int x;
/*Static 2D Array*/
int array[2][2];
/*Many Methods to Dynamically Allocate 2D Array....for example*/
/* Using Array of pointers*/
int *array1[2];
for(x=0;x<2;x++){array1[x] = calloc (2, sizeof(int));}
/*Using pointer to a pointer */
int **array2 = calloc (2, sizeof(int*));
for(x=0;x<2;x++){array2[x] = calloc (2, sizeof(int));}
/*Using a single pointer*/
int *array3 = calloc (4 , sizeof(int));
/* Codes To Fill The Arrays*/
/*Passing the Arrays to the function, some of them won't work*/
simple_function(2, array); /*Case 1*/
simple_function(2, array1); /*Case 2*/
simple_function(2, array2); /*Case 3*/
simple_function(2, array3); /*Case 4*/
return 0;
}
void simple_function (int s, int array[][s]){
int x,y;
for(x=0;x<s;x++){
for(y=0;y<s; y++){
printf ("Content is %d\n", array[x][y]);
}
}
}
My Question:
Is there a way to write the signature of the simple_function to let it accepts all cases regardless of the method that the user chose? If not, what is the most preferable for the function if I want to make a library?
You've actually declared two different types of objects, as shown below
Your array and array3 are both stored in memory as 4 contiguous ints. There's no additional information, you've simply reserved space for 4 ints, and the C specification requires that they are contiguous.
However, array1 and array2 are actually pointer arrays. Your code reserves memory for an array of two pointers, and each pointer points to an array of two ints. The ints will be arranged in groups of two, but the groups can be scattered anywhere in memory.
From this, it should be clear that the compiler cannot use the same code to access both types of array. For example, let's say that you're trying to access the item at array[x][y]. With a contiguous array, the compiler computes the address of that item like this
address = array + (x * s + y) * sizeof(int)
With a scattered array, the compiler computes the address like this
pointer = the value at {array + x * sizeof(int *)}
address = pointer + y * sizeof(int)
So you need two functions to handle those two cases. For the contiguous array, the function looks like this
void showContiguousArray( int s, int array[][s] )
{
for ( int x=0; x<s; x++ )
for ( int y=0; y<s; y++ )
printf( "array[%d][%d] = %d\n", x, y, array[x][y] );
}
For the scattered array, the function is
void showScatteredArray( int s, int **array )
{
for ( int x=0; x<s; x++ )
for ( int y=0; y<s; y++ )
printf( "array[%d][%d] = %d\n", x, y, array[x][y] );
}
Notice that those functions are identical, except for one thing, the type of the array argument. The compiler needs to know the type in order to generate the correct code.
If the array is declared in the same scope where it's used, then all of these details are hidden, and it seems that you're using the exact same code to access different types of arrays. But that only works because the compiler knows the type of the array from the earlier declaration. But if you want to pass the array to a function, then the type information must be explicitly specified in the function declaration.
Is there a way to write the signature of the simple_function to let it accepts all cases regardless of the method that the user chose?
"All the cases" apparently describes the different declarations array, array1, array2, and array3, the latter three of which are described in code comments as "Methods to Dynamically Allocate 2D Array." But none of the four types are the same as any of the others, and none of the latter three in fact declare 2D arrays nor pointers to such. None of them are even compatible with each other. Of the dynamic ones, only array3 can even usefully be converted to something comparable to array.
A dynamically-allocated 2D array would be referenced via a pointer of this type:
int (*array4)[2];
array4 = calloc(2 * sizeof(*array4));
So, no.
If not, what is the most preferable for the function if I want to make a library?
It depends on your objectives. If your function must be compatible with static 2D arrays, then something of the general form you presented, plus or minus the variable dimension, is the only alternative. If you want to support the pointer-to-pointer form, then that's fine, and usable with declarations like array1's and array2's, but not with array, array3, or array4.
Assuming that you want this for 2D arrays, perhaps this can get you started ...
void simple_function(int s, int t , int* array) {
int i, j;
for (i=0; i<s; i++) {
for (j=0; j<t; j++) {
// accessing your array elements
printf(" %d", *(array + i*t + j));
}
printf("\n");
}
}
int main(void)
{
int array[2][3];
array[0][0] = 1;
array[0][1] = 2;
array[0][2] = 3;
array[1][0] = 11;
array[1][1] = 12;
array[1][2] = 13;
array[2][0] = 21;
array[2][1] = 22;
array[2][2] = 23;
simple_function(3, 3 , array);
return 0;
}
The expression int **array2 is not a 2D array by the way, it is a variable that holds the address of a pointer variable.
I recently started programming C just for fun. I'm a very skilled programmer in C# .NET and Java within the desktop realm, but this is turning out to be a bit too much of a challenge for me.
I am trying to do something as "simple" as returning a two-dimensional array from a function. I've tried researching on the web for this, but it was hard for me to find something that worked.
Here's what I have so far. It doesn't quite return the array, it just populates one. But even that won't compile (I am sure the reasons must be obvious to you, if you're a skilled C programmer).
void new_array (int x[n][n]) {
int i,o;
for (i=0; i<n; i++) {
for (o=0; o<n; o++) {
x[i][o]=(rand() % n)-n/2;
}
}
return x;
}
And usage:
int x[n][n];
new_array(x);
What am I doing wrong? It should be mentioned that n is a constant that has the value 3.
Edit: Here's a compiler error when trying to define the constant: http://i.imgur.com/sa4JkXs.png
C does not treat arrays like most languages; you'll need to understand the following concepts if you want to work with arrays in C.
Except when it is the operand of the sizeof or unary & operator, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array. This result is not an lvalue; it cannot be the target of an assignment, nor can it be an operand to the ++ or -- operators.
This is why you can't define a function to return an array type; the array expression will be converted to a pointer type as part of the return statement, and besides, there's no way to assign the result to another array expression anyway.
Believe it or not, there's a solid technical reason for this; when he was initially developing C, Dennis Ritchie borrowed a lot of concepts from the B programming language. B was a "typeless" language; everything was stored as an unsigned word, or "cell". Memory was seen as a linear array of "cells". When you declared an array as
auto arr[N];
B would set aside N "cells" for the array contents, along with an additional cell bound to arr to store the offset to the first element (basically a pointer, but without any type semantics). Array accesses were defined as *(arr+i); you offset i cells from the address stored in a and dereferenced the result. This worked great for C, until Ritchie started adding struct types to the language. He wanted the contents of the struct to not only describe the data in abstract terms, but to physically represent the bits. The example he used was something like
struct {
int node;
char name[14];
};
He wanted to set aside 2 bytes for the node, immediately followed by 14 bytes for the name element. And he wanted an array of such structures to be laid out such that you had 2 bytes followed by 14 bytes followed by 2 bytes followed by 14 bytes, etc. He couldn't figure out a good way to deal with the array pointer, so he got rid of it entirely. Rather than setting aside storage for the pointer, C simply calculates it from the array expression itself. This is why you can't assign anything to an array expression; there's nothing to assign the value to.
So, how do you return a 2D array from a function?
You don't. You can return a pointer to a 2D array, such as:
T (*func1(int rows))[N]
{
T (*ap)[N] = malloc( sizeof *ap * rows );
return ap;
}
The downside to this approach is that N must be known at compile time.
If you're using a C99 compiler or a C2011 compiler that supports variable-length arrays, you could do something like the following:
void func2( size_t rows, size_t cols, int (**app)[cols] )
{
*app = malloc( sizeof **app * rows );
(*app)[i][j] = ...; // the parens are necessary
...
}
If you don't have variable-length arrays available, then at least the column dimension must be a compile-time constant:
#define COLS ...
...
void func3( size_t rows, int (**app)[COLS] )
{
*app = malloc( sizeof **app * rows );
(*app)[i][j] = ...;
}
You can allocate memory piecemeal into something that acts like a 2D array, but the rows won't necessarily be contiguous:
int **func4( size_t rows, size_t cols )
{
int **p = malloc( sizeof *p * rows );
if ( p )
{
for ( size_t i = 0; i < rows; i++ )
{
p[i] = malloc( sizeof *p[i] * cols );
}
}
return p;
}
p is not an array; it points to a series of pointers to int. For all practical purposes, you can use this as though it were a 2D array:
int **arr = foo( rows, cols );
...
arr[i][j] = ...;
printf( "value = %d\n", arr[k][l] );
Note that C doesn't have any garbage collection; you're responsible for cleaning up your own messes. In the first three cases, it's simple:
int (*arr1)[N] = func(rows);
// use arr[i][j];
...
free( arr1 );
int (*arr2)[cols];
func2( rows, cols, &arr2 );
...
free( arr2 );
int (*arr3)[N];
func3( rows, &arr3 );
...
free( arr3 );
In the last case, since you did a two-step allocation, you need to do a two-step deallocation:
int **arr4 = func4( rows, cols );
...
for (i = 0; i < rows; i++ )
free( arr4[i] )
free( arr4)
Your function return void, so the return x; line is superfluous. Aside from that, your code looks fine. That is, assuming you have #define n 3 someplace and not something like const int n = 3;.
You can't return an array in C, multidimensional or otherwise.
The main reason for this is that the language says you can't. Another reason would be that generally local arrays are allocated on the stack, and consequently deallocated when the function returns, so it wouldn't make sense to return them.
Passing a pointer to the array in and modifying it is generally the way to go.
To return (a pointer to) a newly-created array of dimensions known at compile time, you can do this:
#define n 10 // Or other size.
int (*new_array(void))[n]
{
int (*x)[n] = malloc(n * sizeof *x);
if (!result)
HandleErrorHere;
for (int i = 0; i < n; ++i)
for (int o = 0; i < n; ++o)
x[i][o] = InitialValues;
return x;
}
…
// In the calling function:
int (*x)[n] = new_array();
…
// When done with the array:
free(x);
If the size is not known at compile time, you cannot even return a pointer to an array. C does support variable-length arrays but not in the return types of functions. You could instead return a pointer to a variable-length array through a parameter. That requires using a parameter that is a pointer to a pointer to an array of variable length, so it gets somewhat messy.
Also, the preferred choices between allocating an array in the caller dynamically, allocating an array in the caller automatically, allocating an array in the called function dynamically and using variable-lengths arrays or fixed-length arrays or even one-dimensional arrays with manual indexing depend on context, including what how large the array might be, how long it will live, and what operations you intend to use it for. So you would need to provide additional guidance before a specific recommendation could be made.
In C there's only pass/return by value (no pass by reference). Thus the only way of passing the array (by value) is to pass its address to the function, so that it can manipulate it through a pointer.
However, returning by value an array's address isn't possible, since by the time control reaches the caller, the function goes out of scope and its automatic variables go down with it too. Hence if you really have to, you can dynamically allocate the array, populate and return it, but the preferred method is passing the array and leaving the onus of maintaining the array to the caller.
As for the error, the only warning I get in GCC for this is warning: 'return' with a value, in function returning void which is simply meaning that you shouldn't return anything from a void function.
void new_array (int x[n][n]); what you're really doing here is taking a pointer to an array of n integers; the decayed type is int (*x)[n]. This happens because arrays decay into pointers generally. If you know n at compile time, perhaps the best way to pass is:
#define n 3
void new_array (int (*x)[n][n]) {
int i,o;
for (i=0; i<n; i++) {
for (o=0; o<n; o++) {
x[i][o]=(rand() % n)-n/2;
}
}
}
And call it as
int arr[n][n];
new_array(&arr);
You can pass around arbitrarily dimensions arrays like any another variable if you wrap them up in a struct:
#include <stdio.h>
#define n 3
struct S {
int a[n][n];
};
static struct S make_s(void)
{
struct S s;
int i, j;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++)
s.a[i][j] = i + j;
}
return s;
}
static void print_s(struct S s)
{
int i, j;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++)
printf(" %d", s.a[i][j]);
printf("\n");
}
}
int main(void) {
struct S s;
s = make_s();
print_s(s);
return 0;
}
You are probably declaring n as a constant integer:
const int n = 3;
Instead, you should define n as a preprocessor definition:
#define n 3
So... I have a dynamically allocated array on my main:
int main()
{
int *array;
int len;
array = (int *) malloc(len * sizeof(int));
...
return EXIT_SUCCESS;
}
I also wanna build a function that does something with this dynamically allocated array.
So far my function is:
void myFunction(int array[], ...)
{
array[position] = value;
}
If I declare it as:
void myFunction(int *array, ...);
Will I still be able to do:
array[position] = value;
Or I will have to do:
*array[position] = value;
...?
Also, if I am working with a dynamically allocated matrix, which one is the correct way to declare the function prototype:
void myFunction(int matrix[][], ...);
Or
void myFunction(int **matrix, ...);
...?
If I declare it as:
void myFunction(int *array, ...);
Will I still be able to do:
array[position] = value;
Yes - this is legal syntax.
Also, if I am working with a dynamically allocated matrix, which one
is correct to declare the function prototype:
void myFunction(int matrix[][], ...);
Or
void myFunction(int **matrix, ...);
...?
If you're working with more than one dimension, you'll have to declare the size of all but the first dimension in the function declaration, like so:
void myFunction(int matrix[][100], ...);
This syntax won't do what you think it does:
void myFunction(int **matrix, ...);
matrix[i][j] = ...
This declares a parameter named matrix that is a pointer to a pointer to int; attempting to dereference using matrix[i][j] will likely cause a segmentation fault.
This is one of the many difficulties of working with a multi-dimensional array in C.
Here is a helpful SO question addressing this topic:
Define a matrix and pass it to a function in C
Yes, please use array[position], even if the parameter type is int *array. The alternative you gave (*array[position]) is actually invalid in this case since the [] operator takes precedence over the * operator, making it equivalent to *(array[position]) which is trying to dereference the value of a[position], not it's address.
It gets a little more complicated for multi-dimensional arrays but you can do it:
int m = 10, n = 5;
int matrixOnStack[m][n];
matrixOnStack[0][0] = 0; // OK
matrixOnStack[m-1][n-1] = 0; // OK
// matrixOnStack[10][5] = 0; // Not OK. Compiler may not complain
// but nearby data structures might.
int (*matrixInHeap)[n] = malloc(sizeof(int[m][n]));
matrixInHeap[0][0] = 0; // OK
matrixInHeap[m-1][n-1] = 0; // OK
// matrixInHeap[10][5] = 0; // Not OK. coloring outside the lines again.
The way the matrixInHeap declaration should be interpreted is that the 'thing' pointed to by matrixInHeap is an array of n int values, so sizeof(*matrixInHeap) == n * sizeof(int), or the size of an entire row in the matrix. matrixInHeap[2][4] works because matrixInHeap[2] is advancing the address matrixInHeap by 2 * sizeof(*matrixInHeap), which skips two full rows of n integers, resulting in the address of the 3rd row, and then the final [4] selects the fifth element from the third row. (remember that array indices start at 0 and not 1)
You can use the same type when pointing to normal multidimensional c-arrays, (assuming you already know the size):
int (*matrixPointer)[n] = matrixOnStack || matrixInHeap;
Now lets say you want to have a function that takes one of these variably sized matrices as a parameter. When the variables were declared earlier the type had some information about the size (both dimensions in the stack example, and the last dimension n in the heap example). So the parameter type in the function definition is going to need that n value, which we can actually do, as long as we include it as a separate parameter, defining the function like this:
void fillWithZeros(int m, int n, int (*matrix)[n]) {
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
matrix[i][j] = 0;
}
If we don't need the m value inside the function, we could leave it out entirely, just as long as we keep n:
bool isZeroAtLocation(int n, int (*matrix)[n], int i, int j) {
return matrix[i][j] == 0;
}
And then we just include the size when calling the functions:
fillWithZeros(m, n, matrixPointer);
assert(isZeroAtLocation(n, matrixPointer, 0, 0));
It may feel a little like we're doing the compilers work for it, especially in cases where we don't use n inside the function body at all (or only as a parameter to similar functions), but at least it works.
One last point regarding readability: using malloc(sizeof(int[len])) is equivalent to malloc(len * sizeof(int)) (and anybody who tells you otherwise doesn't understand structure padding in c) but the first way of writing it makes it obvious to the reader that we are talking about an array. The same goes for malloc(sizeof(int[m][n])) and malloc(m * n * sizeof(int)).
Will I still be able to do:
array[position] = value;
Yes, because the index operator p[i] is 100% identical to *(ptr + i). You can in fact write 5[array] instead of array[5] and it will still work. In C arrays are actually just pointers. The only thing that makes an array definition different from a pointer is, that if you take a sizeof of a "true" array identifier, it gives you the actual storage size allocates, while taking the sizeof of a pointer will just give you the size of the pointer, which is usually the system's integer size (can be different though).
Also, if I am working with a dynamically allocated matrix, which one is the correct way to declare the function prototype: (…)
Neither of them because those are arrays of pointers to arrays, which can be non-contigous. For performance reasons you want matrices to be contiguous. So you just write
void foo(int matrix[])
and internally calculate the right offset, like
matrix[width*j + i]
Note that writing this using the bracket syntax looks weird. Also take note that if you take the sizeof of an pointer or an "array of unspecified length" function parameter you'll get the size of a pointer.
No, you'd just keep using array[position] = value.
In the end, there's no real difference whether you're declaring a parameter as int *something or int something[]. Both will work, because an array definition is just some hidden pointer math.
However, there's is one difference regarding how code can be understood:
int array[] always denotes an array (it might be just one element long though).
int *pointer however could be a pointer to a single integer or a whole array of integers.
As far as addressing/representation goes: pointer == array == &array[0]
If you're working with multiple dimensions, things are a little bit different, because C forces you declare the last dimension, if you're defining multidimensional arrays explicitly:
int **myStuff1; // valid
int *myStuff2[]; // valid
int myStuff3[][]; // invalid
int myStuff4[][5]; // valid
Say I have an array: array[2][4], and inside the main method, I have a call to a function blackandwhite. How can I pass this method the length and width of the array as arguments?
This is a possible solution :
void blackandwhite(int* array, int height, int width)
{
// Array-processing done here.
// array is pointer to int,
// initially points to element myarray[0][0].
// variable height = 2;
// variable width = 4;
}
int main()
{
int myarray[2][4];
blackandwhite(&myarray[0][0], 2, 4);
}
One can find the size of an array i.e. the number of elements in it by the following construct :
int array[8];
int size = sizeof(array)/sizeof(array[0]);
Unfortunately, C arrays are native arrays and do NOT contain any metadata embedded in them. Rows and Columns are just a way of representing/accessing what is essentially linear storage space in memory. AFAIK, there is no way to automatically determine the number of rows/columns of a 2D-array, given a pointer to it (in C).
Hence one needs to pass the number of columns/rows as separate arguments along-with the pointer to the 2D-array as shown in the example above.
More info on a related question here.
UPDATE:
Common pitfall no.1 : Using int** array in param-list
Note that a pointer to a 2 dimensional array of integers is still a pointer to a int.
int** implies that the param refers to a pointer to a pointer to an int, which is NOT the case here.
Common pitfall no.2 : Using int[][] in param-list
Failing to pass the dimension(s) of the array. One need NOT pass the size of the array's 1st dimension (but you may but the compiler will ignore it). The trailing dimensions are compulsory though. So,
// is INVALID!
void blackandwhite(int array[][], int height, int width)
// is VALID, 2 is ignored.
void blackandwhite(int array[2][4], int height, int width)
// is VALID.
void blackandwhite(int array[][4], int height, int width)
If you are using a C99 compiler, or a C2011 compiler that supports variable-length arrays, you can do something like the following:
/**
* the cols parameter must be declared before it is used in
* the array parameter.
*/
void blackandwhite(size_t rows, size_t cols, int (*array)[cols])
{
size_t i, j;
for (i = 0; i < rows; i++)
for (j = 0; j < cols; j++)
array[i][j] = ...;
}
and you would call it as
int array[N][M];
...
blackandwhite(N, M, array);
In most cases1, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element in the array. The expression array has type "N-element array of M-element array of int"; when we pass it as a parameter to blackandwhite, it's converted to an expression of type "pointer to M-element array of int", or int (*)[M], and its value is the same as &array[0].
If you are using a C2011 compiler that doesn't support variable-length arrays (VLA support is now optional), or a C89 or earlier compiler (which never supported VLAs), then you have two choices: you can either hardcode the number of columns:
void blackandwhite(size_t rows, int (*array)[M])
{
size_t i,j;
for (i = 0; i < rows; i++)
for (j = 0; j < M; j++)
array[i][j] = ...;
}
...
blackandwhite(N, array);
in which case this function will only work with arrays that have a specific number of columns, or you can use the approach shown by theCodeArtist, where you explicitly pass a pointer to the first element of the array along with the rows and columns as parameters. However, this means that you'll be treating array as a 1D array, not a 2D array, meaning you'll have to map 2D indices onto a 1D structure:
void blackandwhite(int *array, size_t rows, size_t cols)
{
size_t i, j;
for (i = 0; i < rows; i++)
for (j = 0; j < cols; j++)
array[i * rows + j] = ...; // use a 1D index
}
...
blackandwhite(&array[0][0], N, M);
Note that this approach relies on all the rows and columns of array being contiguous in memory; if you had dynamically allocated array such that
int **array = malloc(N * sizeof *array);
if (array)
{
size_t i;
for (i = 0; i < N; i++)
array[i] = malloc(M * sizeof *array[i]);
}
then there's no guarantee that rows are laid out contiguously in memory. However, in this specific case, you could write the following:
void blackandwhite(int **array, size_t rows, size_t cols)
{
size_t i, j;
for (i = 0; i < rows; i++)
for (j = 0; j < cols; j++)
array[i][j] = ...;
}
...
blackandwhite(array, N, M);
Confused yet?
Remember that the expression a[i] is treated as *(a + i); that is, we find the address of the ith element after a and dereference the resulting pointer value. a[i][j] is interpreted as *(*(a+i)+j); *(a + i) gives us the i'th array following a; by the rule mentioned above, this expression is converted from array type to pointer type, and the value is the address of the first element. We then add j to the new pointer value and dereference the result again.
We can use array[i][j] where array is passed as either a pointer to an array (int (*array)[cols] or int (*array)[M]) or as a pointer to pointer (int **array) because the result of array[i] is either a pointer type or an array type that decays to a pointer type, which can have the subscript operator applied to it. We can't use array[i][j] where array is passed as a simple pointer to int, because in that case the result of array[i] is not a pointer type.
So why not do
void blackandwhite(int **array, size_t rows, size_t cols) {...}
...
int array[N][M];
blackandwhite((int **) array, N, M);
Pointers and arrays are different things, so a pointer to a pointer and a pointer to an array are different things. That would probably work, but you're lying to the compiler. Above everything else, it's bad form.
EDIT
In the context of a function parameter declaration, a parameter declaration of the form T a[] or T a[N] will be interpreted as T *a; IOW, a will be declared as a pointer, not an array. Similarly, parameter declarations of the form T a[N][M] or T a[][M] are treated as T (*a)[M]; again, a is declared as a pointer, not an array.
In our first example we could have declared blackandwhite as
void blackandwhite(size_t rows, size_t cols, int array[rows][cols])
or
void blackandwhite(size_t rows, size_t cols, int array[][cols])
but I prefer using pointer declarations explicitly, since it correctly reflects what's happening.
1. The exceptions to this rule are when the array expression is an operand of the sizeof, _Alignof, or unary & operators, or is a string literal being used to initialize another array in a declaration, such as
char str[]="This is a test";.
In C, it is known that there is a method to calculate length of a string whereas in the case of array, you have to explicitly specify the dimensions of the array for passing length and width to the function blackandwhite.
I am trying to pass a double-dimensional array to a function, but it throws this error all the time. My code is below:
void initialize_centroids(int *,int,int);(Initialization)
initialize_centroids(centroid[0],noofcentroids,newnoofvar);(inside my main)
void initialize_centroids(int *carray,int p,int q)
{
int i,j;
for(i=0;i<p;i++)
{
for(j=0;j<q;j++)
{
carray[i][j]=i;
}
}
return;
}
You're passing a pointer to an int (int*), but you're trying to use two subscripts. Is it a 2D array? Then the parameter needs to be a pointer to a pointer to an int: int**
void initialize_centroids(int**, int, int);
int centroid[][] = .... // wherever this comes from
initialize_centroids(centroid, noofcentroids, newnoofvar);(inside my main)
void initialize_centroids(int** carray, int p, int q)
{
int i,j;
for(i=0;i<p;i++)
{
for(j=0;j<q;j++)
{
carray[i][j]=i;
}
}
return;
}
Don't de-reference centroid when passing in. Doing that will only pass your first row. Do this:
initialize_centroids( centroid, noofcentroids, newnoofvar );
Then you need to use the correct type here:
void initialize_centroids( int **carray, int p, int q )
Your array is presumably an int**. That means it's a pointer to a location in memory that contains an array of int* pointers. Each of those pointers (I assume) references memory that has been allocated one row of your array.
Now, once you obtain one of these pointers by doing carray[i] you have selected row i. You now have an int* which means it points to an array of int.
Finally, carray[i][j] selects a specific integer from column j on row i.
[edit]
Theory about what might be going wrong... You may not be allocating a 2D array in a dynamic sense, or indeed it might be a 1D array and you are supposed to use stride-lengths etc. Here is a solution that you can use for allocating a 2D array dynamically, and indexing it as carray[i][j].
Note: Some people have gotten upset at me for this method in the past due to alignment concerns, but I have never had issues. I think they were just having a grumpy day. For those who are worried about alignment, it's easy to adjust for that, but is too complicated for this example.
int** allocate_array2d( int rows, int cols )
{
int i, **arr, *data;
arr = (int**)malloc( rows * sizeof(int*) + rows*cols*sizeof(int) );
data = (int*)(arr + rows);
for( i = 0; i < rows; i++ ) {
rows[i] = data;
data += cols;
}
return arr;
}
void free_array2d( int** arr ) {
free((void*)arr);
}
Now, in your main:
int** centroid = allocate_array2d( noofcentroids, newnoofvar );
initialize_centroids( centroid, noofcentroids, newnoofvar );
[more...]
Okay.Yes I allocated memory like this centroid[100][100] – Bobby 11
mins ago
In that case, you should declare:
void initialize_centroids( int carray[100][100], int p, int q )
{
//...
}
No need to use my dynamic array example