I wrote a function that returns a pointer to a 1D array. The function prototype is:
double* NameFunction (int,double*,double*)
The function seems to work fine with 1D array.
Here's the question: I'd like to use the function to fill one row of a 2D array.
How do I write in the calling function a pointer to the row of the 2D array to fill?
Can I use the same pointer structure to indicate a row of a 2D array as argument?
Yes just pass the name of the 2D array with the first index filled in:
NameFunction (12121212,&array[0],some pointer here) // for example if you want to pass the first row
A variable pointing to an array in C always contains just an address location i.e., the pointer to the starting of the array. So, whatever the array type, it just needs a pointer which is a *.So this function should work as it is now.
But you may need to change some programming logic.
You can take double foo[20] as 1×20 array, or you can take it as two-dimensional 4×5 array. Then you have the coordinates like this:
foo foo+1 foo+2 foo+3 foo+4
foo+5 foo+6 foo+7 foo+8 foo+9
foo+10 foo+11 foo+12 foo+13 foo+14
foo+15 foo+16 foo+17 foo+18 foo+19
So if you have a function that returns double *, you can pass it (or make it return) foo + 5*n to point at row n.
#define ROW_LEN 5
#define ROWS 4
void fillRow(double * row)
{
int i;
for (i = 0; i < ROW_LEN; i++)
{
row[i] = 12;
(row + i) = 12; // this is the same thing written differently
}
}
double arr[ROW_LEN * ROWS];
fillRow(arr + 2 * ROW_LEN); // fill third row
Note that C does not do any range checking at all, so if you are not careful and accidentally to something like arr[627] = 553 somewhere in your code, it's going to blindly overwrite everything that is at the computed address in the memory.
Related
I have the following code :
int allocationMatrix[2+ 1][1+ 1];
allocationMatrix[0][0] = 2;
allocationMatrix[0][1] = 1;
buildAllocationMatrix(flowNProcess, allocationMatrix);
The buildAllocationMatrix function is declared like:
void buildAllocationMatrix(FlowNProcess *flowNProcess, int *allocationMatrix)
inside it, if I check the value of *(allocationMatrix+1), it gives 0 but the assignment
before calling the function is allocationMatrix[0][1] = 1; Not sure why its giving 0
It should return 1. What could be the reason for this?
Pass int **allocationMatrixinside function since it is 2D array.
(becauseint *allocationMatrix is interpreted as passing address of 1D array) .
And access it by
*(*(allocationMatrix+0)+1).
(Reason: by using*(allocationMatrix+0) you will get address of 0th row and in order to get address of 0th row 1st column we can use (*(allocationMatrix+0)+1) thus to get element of that position dereference it as
*(*(allocationMatrix+0)+1) ).
While accessing you can skip zero.
This question already has answers here:
Why do we need to specify the column size when passing a 2D array as a parameter?
(8 answers)
Closed 12 months ago.
As part of a small C program I've written, I have an insertion sort function that inserts a given string into a given array in its sorted location. I've gotten the function to work, but I'm wondering why I need to specify the second dimension for the array in the function definition to keep from getting a compilation time error. In my function, if I leave out the LONGEST_WORD macro, I get an "array has incomplete element type 'char []'" error when compiling. Everything runs smoothly when I keep it in. Could someone please explain why? Thank you!
#include <string.h>
int insertInOrder(char array[][LONGEST_WORD], char* word, int wordCount) {
int i, j, location = wordCount;
/* finds index of location to insert word; */
for (i = 0; i < wordCount; i++) {
if (strcmp(word, array[i]) <= 0) {
location = i;
break;
}
}
/* makes space for new word to be inserted, shifting all words greater than word to the right by one */
for (j = wordCount; j > location; j--) strcpy(array[j], array[j-1]);
strcpy(array[location], word); /* copies new word to its location */
return 0;
}
C is concerned with the actual memory size of your array. char array[][LONGEST_WORD] is an array of arrays of char that are LONGEST_WORD in length.
Knowing this, the compiler knows that array[n+1] is LONGEST_WORD bytes past the address of array[n].
If you hadn't specified this, it doesn't know how to address elements in that array.
A "2D array" is nothing more than a 1D array of 1D arrays.
To find the address of an element in a 1D array the compiler needs to know the address of the array and the size of an element (e.g. for uint32_t myArray[10];, to find the address of myArray[i] C does something like address = (void *)myArray + i * sizeof(uint32_t)).
To find the address of an element in a 1D array (of 1D arrays) the compiler needs to know the address and the size of an element, and the size of the outer array's elements is the size of a whole inner 1D array which depends on its type and how many elements the inner array has.
E.g. for uint32_t myArray[20][10];, to find the address of myArray[i] C does something like address = (void *)myArray + i * sizeof(uint32_t innerArray[10])); and to find the address of myArray[i][j] C does something like outerAddress = (void *)myArray + i * sizeof(uint32_t innerArray[10]) and then does innerAddress = outerAddress + j * sizeof(uint32_t). Of course this can (and normally would) be simplified into innerAddress = (void *)myArray + (i * 10 + j) * sizeof(uint32_t).
if I leave out the LONGEST_WORD macro, I get an "array has incomplete element type"
Yes. Essentially, the outer 1D array is a 1D array of "incomplete element type" elements; and not a 1D array of "1D array of LONGEST_WORD chars" elements.
I am trying to de-reference the 2D array inside the function islandPerimeter.
But I cannot understand why I am getting segfault for this.
Can someone point out what exactly I am doing wrong?
update:
So this was a part of a problem from leetcode I was trying to solve.I now understand it is not 2D array but a pointer. I am still confused over the int**. can someone explain it?
#include <stdio.h>
int islandPerimeter(int** grid, int gridSize, int gridColSize)
{
int perimeter=0,points=4,i=0;
for(int row=0;row<gridSize;++row)
{
for(int col=0;col<gridColSize;++col)
{
printf("%d ",grid[row][col]);
}
}
return perimeter;
}
int main()
{
int arr[4][5] = {{8,1,0,0,0},
{1,1,1,0,0},
{0,1,0,0,0},
{1,1,0,0,0}};
islandPerimeter(arr,4,5);
return 0;
}
A Pointer to Array
An array is a distinct type in C. It is a sequential collections of elements of a given type. In C a 2D array is actually an array of 1D arrays. In your case, you have an array [4] of int [5] (e.g. 4 - 5-elements arrays of int commonly called a 2D array of int)
Where new programmers normally get confused is how an array is treated on access. When an array is accessed, it is converted to a pointer to the first element. C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3) (pay attention to the 4 exceptions)
In the case of a 1D array, that is simple, the array is converted to a pointer to the first element of the array (the pointer is simply int*). In the case of a 2D array, the same holds true, the array is converted to a pointer to the first element -- but that first element is a 1D array of 5-int. (the pointer is a pointer-to-array of int [5], formally int (*)[5])
You can pass the 2D array (in your case) as a parameter of either int grid[4][5], int grid[][5], or to reflect that the array is converted to a pointer to the first element, int (*grid)[5]. The key is you must always provide the number of elements in the final dimension for your array (with additional '*' allowed for circumstances not relevant here) The 5 (or number of elements) must be an integer constant which is known at compile-time unless using a Variable Length Array (VLA), which are the topic for a separate discussion.
The same rule that on access an array is converted to a pointer to its first element applies to each dimension in your array, be it a 2D array or a 6D array. C11 Standard - 6.5.2.1 Array subscripting(p3)
Additionally, know the difference between a pointer-to-array (e.g. int (*grid)[5]) and an array-of-pointers (e.g. int *grid[5]). The parenthesis are required due to C Operator Precedence, the [..] has higher precedence than '*' in this case, so to require that *grid (in int *grid[5]) be evaluated as a pointer (instead of as an array grid[5]) you enclose it is parenthesis (*grid).
Thus resulting in a pointer-to-array of int [5], (int (*grid)[5]) instead of an array-of-pointers to int (5 of them) with int *grid[5].
A Pointer to Pointer
Contrast that with a pointer-to-pointer (e.g. int **, commonly called a double-pointer). You have two-levels of indirection represented by the two **. The pointer itself is a single-pointer -- to what? (another pointer, not to an array). You will generally use a double-pointer by first allocating a block of memory to hold some number of pointers, such as when you are dynamically allocating for an unknown number of allocated objects. This can be an unknown number of rows of an unknown number of columns of int or it can be an unknown number of strings, or a unknown number of structs, etc.. The key is your first level of indirection points to memory containing pointers.
Then for each of the available pointers you can allocate a block (e.g. in your case to hold 5 int and then assign the starting address for that block of memory to your first available pointer). You continue allocating for your columns (or strings or structs) and assigning the beginning address to each of your available pointers in sequence. When done, you can access the individual elements in your allocated collection using the same indexing you would for a 2D array. The difference between such a collection and a 2D array of arrays -- is the memory pointed to by each pointer need not be sequential in memory.
Telling Them Apart
The key to knowing which to use is to ask "What does my pointer point to?" Does it point to a pointer? Or, does it point to an array? If it points to another pointer, then you have a pointer-to-pointer. If the thing pointed to is an array, then you have a pointer-to-array. With that, you know what you need as a parameter.
Why the SegFault with int**
Type controls pointer arithmetic. Recall above, int** is a pointer-to-pointer, so how big is a pointer? (sizeof (a_pointer) - usually 8-bytes on x86_64, or 4-bytes on x86). So grid[1][0] is only one-pointer (8-bytes) away from grid[0][0]. What about the pointer-to-array? Each increment in the first index is a sizeof (int[5]) apart from the first. So in the case of a 4x5 array grid[1][0] is 5 * sizeof(int) (20-bytes) apart from grid[0][0].
So when attempting to access your array of arrays, using int**, beginning with grid[1][3] (or grid[1][4] on a 32-bit box) you are reading one-past the end of the 1st row of values. (you have offset by 8-bytes (one-pointer 8-bytes - skipping 2-int), placing you just before the 3rd integer in the 1st row, then offset 3 more integers placing you at what would be grid[0][5] one past the last value in the 1st row grid[0][4]. (this compounds with each row increment) The result is undefined and anything can happen.
When you pass the appropriate pointer-to-array, each increment of the row-index offsets by 20-bytes, placing you at the beginning of the next 1D array of values so iterating over each column remains within the bounds of that 1D array.
Think through it, and if you have further questions, just let me know and I'm happy to help further.
int** grid is a pointer to pointer to int. It lacks information of the array width.
With C99 or C11 onwards with optional variable length arrays:
// int islandPerimeter(int** grid, int gridSize, int gridColSize)
int islandPerimeter(int gridSize, int gridColSize, int grid[gridSize][gridColSize]) {
int perimeter=0;
for(int row=0;row<gridSize;++row) {
for(int col=0;col<gridColSize;++col) {
printf("%d ",grid[row][col]);
}
}
return perimeter;
}
Call with
islandPerimeter(4, 5, arr);
Try this
int islandPerimeter(int* grid, int gridSize, int gridColSize) {
int perimeter = 0, points = 4, i = 0;
for(int row=0; row < gridSize; ++row) {
for(int col = 0; col < gridColSize; ++col) {
printf("%d ",grid[row*gridColSize + col]);
}
}
return perimeter;
}
You will have to change the call to
islandPerimeter((int *)grid, 4, 5);
Let's say you wanted to leave your function as-is and instead change how the 2D array was initialized in main(or any other calling function). This is also what you would have to do if the array data was entered by a user or loaded from a file at runtime, so it's useful to know:
int main(void) {
const int ROWS = 4; //these don't have to be const;
const int COLS = 5;
const int data[20] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
int** pointer_arr = malloc(ROWS * sizeof(int*)); //allocate space for each ptr
//error check
if (pointer_arr == NULL) {
printf("Unsuccessful ptr-ptrarray allocation attempt\n");
exit(0);
}
for (int i = 0; i < ROWS; ++i) {
pointer_arr[i] = malloc(COLS * sizeof(int)); //allocate space for each int
//error check with alternative indexing syntax (same as pointer_arr[i])
if (*(pointer_arr + i) == NULL) {
printf("Unsuccessful ptr-intarray allocation attempt\n");
exit(0);
}
}
//load each allocated int address space with an int from data:
for (int i = 0; i < ROWS ; ++i) {
for (int j = 0; j < COLS; ++j) {
pointer_arr[i][j] = data[ROWS * i + j];
}
}
//Now you can call your unaltered function and it will perform as expected:
islandperimeter(pointer_arr, ROWS, COLS);
return 0;
}
Under normal conditions (when the program doesn't terminate at once) note that you would then have to manually free all that allocated memory, or suffer a memory leak.
I am trying to learn about an implementation of some code that uses a typedef to a double array and also uses pointers and I am having some difficulty understanding the code and details on how the code works and what types the variables are and what points to what.
I have tried playing around with different implementations and trying to understand how it works but the results I have gotten have been not what I have expected.
Heres some code I tried to test:
typedef int array[2][6];
array *arr;
arr = (array*)malloc(sizeof(array));
*arr[0][0]=2;
*arr[0][1]=4;
*arr[1][0]=3;
*arr[1][1]=5;
printf("line 1: %d %d\nline 2: %d %d\n",*arr[0][0],*arr[1][0],*arr[0][1],*arr[1][1]);
int *in = (int*) ((*arr)[0]);
printf("in = %d\n",in[1]); // results are unexpected
The code that I am actually looking at is for a ping pong buffer and is (simplified) as follows:
int buffer_count = 2; // 2 ping pong buffers
int sample_size = 15;
typedef int PingPong_t[buffer_count][sample_size];
PingPong_t *inputstream;
// logic goes here to determine pingpong_idx
int pingpong_idx = 0; // I believe this is to choose the first or second buffer
int *pcmIn = (int*)((*inputstream)[pingpong_idx]);
// do processing
I expect that pcmIn is an integer array of the current ping or pong buffer, but I am having trouble proving that to myself or I am just unsure what the datatypes actually are and what it is actually doing.
A good question I might have is, what is the type of inputstream? Is it correct to say that inputstream is a pointer to a double array of integers? Or is inputstream a double array of integer pointers?
Then what would be the type of pcmIn?
Let's break it down.
typedef int PingPong_t[buffer_count][sample_size];
This will make PingPong_t represent a 2D array of integers.
So, you can have
PingPong_t p = {{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},{3,4,5,6,7,8,9,10,11,12,13,14,15,16,17}};
Then, we declare a pointer to this type as
PingPong_t *inputstream;
which means that inputstream is a pointer to whatever PingPong_t represents. It is not an array, not an array of pointers, just a pointer.
Since PingPong_t essentially means int x[2][15], inpustream will mean int (*y)[2][15].
Suppose we had something like
inputstream = &p;
Then, inputstream will point to the 2D array that is p. A custom pointer of type 2-dimensional (2 x 15) int array.
So, inputstream is int (*)[2][15] and p is int [2][15].
Now, playing it further, let us suppose we were to increment inputstream.
Incrementing a pointer will add the size of the type it points to.
So, in our case incrementing inpustream adds sizeof(int)*2*15 i.e. 120 (my machine has 4-byte int). Note that it does not add sizeof(int) here as its base type is not int but a 2D array of ints.
Finally, pcmIn is an int pointer (which can be thought of as a 1D array here), we are assigning like this
int *pcmIn = (int*)((*inputstream)[pingpong_idx]);
which fetches the first row (pingpong_idx) of the 2D array that inputstream points to, and assigns it to pcmIn.
Furthermore, you are getting unexpected results in your example above as you are assigning to the array in wrong manner. Since arr is a pointer to 2D array, you assign a value to an element like this
(*arr)[0][1]=4;
and not like this
*arr[0][1]=4;.
The latter will mean that arr is a 2D array of pointers and you are basically setting the value of [0][1]th pointer to 4, which is not what you had planned.
arr is not a 2D array and so arr[0][1] will mean the adding sizeof(int)*15 to the value of arr, and *arr[0][1] is basically getting/setting value at that address. In effect, the [0][1]th value is still uninitialized.
But, then how does *arr[0][0]=2; and printf("in = %d\n",in[0]); work?
Because, doing *arr[0][0]=2; will set the value of [0][0]th element to 2.
I have a weird problem and I don't know the reason, so I can't think of a solution to fix it.
My problem:
I have a removeEntry function with a array of structs as parameter, but somehow this function doesn't work.
In an earlier version of my code, I declared my array of structs outside the main function (outside every function), and it worked then. But since I now create my array of struct in my main function, I have to give it as parameter, and now my removeEntry function doesn't work.
Code that isn't working:
void removeEntry(struct entry entries[])
{
int entryNumber;
int nrBytes = sizeof(entries);
int arrayLength = nrBytes / sizeof(entries[0]);
printf("\nEnter entry number to delete: ");
scanf("%d",&entryNumber);
while (scanfOnlyNumber(entryNumber) == false || entryNumber == 0 || entries[entryNumber].entry_number == 0)
{
printf("\nEnter a legit entry number to delete: ");
scanf("%d", &entryNumber);
// Tell the user that the entry was invalid
}
int i = 0;
for(i = entryNumber; i < arrayLength - 1; i++)
{
entries[i] = entries[i + 1]; //removes element and moves every element after that one place back.
}
updateEntryNumber(entries);
printf("\nEntry %d removed succesfully, and entry numbers updated!\n", entryNumber);
}
My teacher told me that my arraylength calculation doesn't work when I create my array of structs inside my main function (what I do now),
but I can't tell why it doesn't work. If anybody can explain that, then I might be able to fix my removeEnty problem by myself.
If anyone wants the working code (where I don't give my array as parameter, because I create my array outside every function), then tell me and I will post it.
Problem
When you pass an array to a function, you don't pass the entire array, instead you pass a pointer to the array. Hence, when you do int nrBytes = sizeof(entries); you're actually getting the size of pointer variable rather than the size of array.
Solution
Pass your array length along with a pointer to the array to the function, something like this:
void removeEntry(struct entry entries[], int arrayLength){
// your code
}
int nrBytes = sizeof(entries); // basically size of struct node *
int arrayLength = nrBytes / sizeof(entries[0]);
In this sizeof(entries) will give size of struct pointer , and not the array , and also in second statement , it wont correctly .
This is because array decays into pointer after passing it to function and referring it .
What you need to do is calculate number of elements in struct array in main and then pass it to the function .
void removeEntry(struct entry entries[],int arrayLength);
Calculate arrayLength in main as you do.
if you compile your example, you will probably see a warning like this one:
warning: sizeof on array function parameter will return size of 'entry *' instead of 'entry []' [-Wsizeof-array-argument]
As noted in the answer by V. Kravchenko, this suggests that nrBytes will contain merely the size of the pointer, while sizeof(entries[0]) returns the "true" size of the entire structure. Therefore, arrayLength will be most probably zero.
You should pass array's size to function, because int nrBytes = sizeof(entries); is always 4 or 8 (on x64 systems). It's just pointer's size.
int nrBytes = sizeof(entries);
Arrays of any type (even arrays of structs) decay into pointers when passed to a function. The lenght of this pointer is always fixed and gives no indication as to the lenght whatsoever. So pass the lenght with the function call.