In the Chapter 7 of Deitel's C how to program the author used bubble sort example to show that information hiding does not allow individual array elements to be known to the function, pointers can be used to pass the address around and used the same way.
Here is the swap function using pointer:
void swap( int *element1Ptr, int *element2Ptr ) {
int hold = *element1Ptr;
*element1Ptr = *element2Ptr;
*element2Ptr = hold;
}
Here is what I did:
void swap(int array[],int j) {
int hold = array[j];
array[j] = array[j + 1];
array[j + 1] = hold;
}
The main():
int main(void) {
int b[] = {2,6,4,8,10,12,89,68,45,37};
for (int i = 0; i < 9; i++) {
for (int n = 0; n < 9; n++) {
if (b[n] > b[n + 1]) {
swap(&b[n], &b[n+1]); // or swap(b,n);
}
}
}
for (int i = 0; i < 9; i++) {
printf("%d ", b[i]);
}
puts("");
return 0;
}
I ran the code and both sort the array correctly. So I thought my function actually gained the access to individual element. Which part am I understanding wrong? I need to make sure I understand every point before moving on as skipping one really makes the coming content difficult to grasp.
In C, pointers and arrays are interchangeable. When you access array elements by index, as in myArray[x], it means "the location of myArray + (x * the size of the elements in the array)". So you can get the same result by doing myArray + (x * sizeof(<type of myArray>));.
You may also want to check out this answer which goes into more depth on the differences between arrays and pointers.
Related
This question already has answers here:
Why can't we use double pointer to represent two dimensional arrays?
(6 answers)
Closed 13 days ago.
I wrote this program that is supposed to sort NxN array. It gets compiled but doesn't work because the pointer type is incompatible.
I just need help with the pointers as argument. I get incompatible pointer type warning for both functions swap and dblArraySort. Any idea why is that ?
thanks in advance !
#include <stdio.h>
#include <stdlib.h>
void
swap(int **a, int **b)
{
int temp;
temp = **a;
**a = **b;
**b = temp;
}
void
dblArraySort(int **dblArray, int arrLen)
{
int chkIndex;
int i, j, k;
for (i = 0; i < arrLen; i++) {
if ((i + 1) % 2 == 0) {
for (j = 0; j < arrLen; j++) {
chkIndex = dblArray[i][j];
for (k = 1; k + j < arrLen; k++)
if (chkIndex < dblArray[i][k + j])
swap(&dblArray[i][j], &dblArray[i][k + j]);
else
continue;
}
} else {
for (j = 0; j < arrLen; j++) {
chkIndex = dblArray[i][j];
for (k = 1; k + j < arrLen; k++)
if (chkIndex >= dblArray[i][k + j])
swap(&dblArray[i][j], &dblArray[i][k + j]);
else
continue;
}
}
}
}
int
main()
{
unsigned int arrayLength;
printf("Provide array size: \n");
scanf("%d", &arrayLength);
int doubleArray[arrayLength][arrayLength];
for (int i = 0; i < arrayLength; i++) {
for (int j = 0; j < arrayLength; j++) {
scanf("%d", &doubleArray[i][j]);
}
}
dblArraySort(doubleArray, arrayLength);
for (int i = 0; i < arrayLength; i++) {
for (int j = 0; j < arrayLength; j++) {
printf("%d ", doubleArray[i][j]);
}
printf("\n");
}
return 0;
}
I tried the code mentioned above
Arrays in C can be confusing. The thing you need to worry about is element type.
The element type of int ** dblArray is int *. In other words, dblArray is an array of int *s.
However, the element type of int doubleArray[arrayLength][arrayLength] is int row_type[arrayLength]. That is not an int *, that is an array, which is a totally different thing.
Moreover, when you use an array⟶pointer conversion, as happens when you say:
dblArraySort(doubleArray, arrayLength); // doubleArray is converted to a pointer
You get a pointer to the array, which in this case is a pointer to the innermost element type, an int — which is also not an int *.
tl;dr: You are trying to pass an array of array of int to a function taking an array of pointer to int. That won’t work.
I would like to comment on your variable naming as well. When you say “double” or “dbl”, as in doubleArray and dblArray the very first thing people will think is that you are handling a linear array of type double, which is also not what the array is.
You have there a two-dimensional array. Not a “double” array. Common naming for such thing would be array2D or matrix.
To make it work you need either C11, which allows you to pass a VLA as:
void sort_array2D( size_t rows, size_t columns, int array[rows][columns] )
{
...
int value = array[i][j];
...
}
int main(void)
{
int array2D[Array_Length][Array_Length];
...
sort_array2D( Array_Length, Array_Length, array2D );
Or you need to simply assume you must compute the index manually. A little function will help:
size_t index2D( size_t rows, size_t columns, size_t r, size_t c )
{
(void)rows; // (quiet the compiler about not using this argument)
return r * columns + c;
}
Then you can write your function as:
void sort_array2D( int * array, size_t rows, size_t columns )
{
...
int value = array[index2D( rows, columns, i, j )];
...
}
int main(void)
{
int array2D[Array_Length][Array_Length];
...
sort_array2D( (int *)array2D, Array_Length, Array_Length );
I haven’t bothered to analyze your sort function. It doesn’t look right to me, but honestly, I’ve barely glanced at it. Calling a value from the array chkIndex looks fishy, since the values of the array are not indices per se, at least not in the context of sorting them.
Remember, when messing with arrays in C you need to keep strict care to not mix up the type of the elements. (Or the types of things in general, whether syntactic or conceptual.)
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4 - i; j++) {
if (array[j] > array[j + 1]) {
swap(array[j], array[j + 1]);
}
}
}
printf("Descending : ");
for (int k = 4; 0 <= k; k--) {
printf("%d ", array[k]);
}
I made code for descending five typed integer numbers.
And there was two ways for activating this.
The first one is using temp swap in loop(like, tmp=a, a=b, b=tmp), and second one is declaring swap function(void swap(int x, int y){int tmp=x, x=y, y=tmp}) and using this func in loop.
First way was successful, but the second way was failed.
I want to know why swap func in loop can't be actuated.
I made code for descending five typed real numbers.
Taking into account the presented code (the used conversion specifier %d in the call of prinf) it seems you mean integer numbers not real numbers.
You need to pass the swapped objects to the function by reference indirectly through pointers to them. Otherwise the function will deal with copies of values of the original objects..
The function can be declared and defined for example the following way
void swap( int *a, int *b )
{
int tmp = *a;
*a = *b;
*b = tmp;
}
and can be called like
swap( array + j, array + j + 1 );
or
swap( &array[j], &array[j + 1] );
I've followed all the algorithm steps very carefully , but still this always outputs me the wrong answer. I don't understand why. I think something's wrong in the merge algorithm that's causing this but cannot pinpoint what. Please help. Also if there is anything that can be done to improve the code please suggest.
Thank you
INPUT - {5,6,1,8,9,7}
OUTPUT - {1,0,7,0,9,7}
#include<stdio.h>
#include<malloc.h>
void mergeSort(int array[],int length);
void merge(int *leftArray,int *rightArray,int *array);
void main()
{
int array[] = {5,6,1,8,9,7};
int length_of_array;
length_of_array = sizeof(array) / sizeof(array[0]);
mergeSort(array,length_of_array);
int i;
for(i=0;i<length_of_array;i++)
{
printf("%d->",array[i]);
}
}
void mergeSort(int array[],int length)
{
if(length < 2)
return;
int mid;
int i;
mid = length/2;
int *leftArray, *rightArray;
leftArray = (int*)malloc(mid*sizeof(int));
rightArray = (int*)malloc((length-mid)*sizeof(int));
for(i=0;i<mid;i++)
leftArray[i] = array[i];
for(i=mid;i<length;i++)
rightArray[i-mid] = array[i];
mergeSort(leftArray, mid);
mergeSort(rightArray, length-mid);
merge(leftArray,rightArray,array);
}
void merge(int *leftArray,int *rightArray,int *array)
{
int i,j,k;
i = j = k = 0;
int leftSize = sizeof(leftArray)/sizeof(leftArray[0]);
int rightSize = sizeof(rightArray)/sizeof(rightArray[0]);
while(i < leftSize && j < rightSize)
{
if(leftArray[i]<rightArray[j])
{
array[k] = leftArray[i];
k = k + 1;
i = i + 1;
}
else
{
array[k] = rightArray[j];
k = k + 1;
j = j + 1;
}
}
while(i<leftSize)
{
array[k] = leftArray[i];
k = k + 1;
i = i + 1;
}
while(j<rightSize)
{
array[k] = rightArray[j];
k = k + 1;
j = j + 1;
}
}
As commented by #molbdnilo, you can't get the size of an array from a pointer parameter. So merge needs to take the length of the left and right arrays as well as the pointers to them.
The issue is that arrays in C are not a 'complete' data type, but rather just a convenient syntax. In your merge function, the parameter int *leftArray is exactly what it says - a pointer to an integer. So sizeof will tell you the size of a pointer. In your main function, array is known to be an array, and its length is known (from the initial value given), so sizeof can give the actual size of memory allocated to that variable. But that size is not stored anywhere with the variable, so it is not passed into merge - the only thing passed in is the pointer to the block of memory.
In addition, while it won't be causing you problems in this case, you should be freeing the leftArray and rightArray pointers that you malloc. That way you can use your sorting function in an actual application without leaking memory.
I made a structure who has two members (int and int**), and I return the pointer to this structure from one function to main(). It is fine to access the int value in the structure. However, in main() I got Segmentation fault : 11 when I tried to access the element of the 2D array.
#include<stdio.h>
#include<stdlib.h>
typedef struct Square {
int value;
int **array;
} Square;
Square * generate();
int main(int argc, char *argv[]){
Square *sqrptr = generate();
printf("%d\n", sqrptr -> value);
/* It prints 1 */
/* Print out the 2D array */
for (int i = 0; i < 3; i++){
for (int j = 0; j < 3 ; j++){
printf("%d ", *(*((sqrptr -> array) + i) + j));
}
printf("\n");
}
/* It gives segmentation fault */
return 0;
}
Square * generate(){
Square mySquare;
mySquare.value = 1;
mySquare.array = malloc(sizeof(int*) * 3);
/* Initialize the 2D array */
for (int i = 0; i < 3; i++){
*(mySquare.array + i) = malloc(sizeof(int) * 3);
for (int j = 0; j < 3; j++){
*(*(mySquare.array + i) + j) = 0;
}
}
/* Print out the 2D array */
for (int i = 0; i < 3; i++){
for (int j = 0; j < 3l ; j++){
printf("%d ", *(*(mySquare.array + i) + j));
}
printf("\n");
}
/* I can see the complete 2D array here */
Square *sqrptr = &mySquare;
return sqrptr;
}
I have tried to generate the Square in main(), and use one pointer of the structure to access my 2D array. It works fine, so I guess I have missed something when I use a pointer returned from other functions. On the other hand, I can access the int value successfully, so I have no clues now.
Could someone please explain the underlying reason for this segmentation fault? Thanks!
You're returning a pointer to a local variable (&mySquare). Stack memory (where local variables reside) is when the function returns, so the resulting pointer is pointing to invalid memory. Allocate the struct, and return the pointer to heap memory:
Square *my_square = malloc(sizeof *my_square);
//do stuff
return my_square;
Or pass a pointer to a stack variable as argument:
Square * generate(Square *my_square)
{
//in case pointer wasn't provided, allocate
if (my_square == NULL) {
my_square = malloc(sizeof *my_square);
if (!my_square)
return NULL; // or exit or whatever
}
//initialize members. To initialize array to 3x3 zero matrix, you can use:
for (int i=0;i<3;++i)
my_square.array[i] = calloc(3, sizeof *my_square->array[i]);
//or even, if you change array member to type int*:
my_square.array = calloc(3*3, sizeof *my_square->array);
//at the end:
return my_square;
}
The latter is arguably the most flexible solution: if you want to work on stack, you call the function like so:
Square my_stack_square;
generate(&my_stack_square);
If you need to use heap memory, you can use:
Square *my_heap_square = generate(NULL);
As Jonathan Leffler pointed out, for a small struct such as this, returning by value isn't too much of a cost. Getting a struct on heap can be achieved in the same way as returning any other type:
Square generate( void )
{
Square my_square;
//initialize
return my_square;
}
//call like so:
Square sq = generate();
The idea here is that you'll use a local variable in the generate function to create a new square, initialize the fields, and then return it. Because in C everything is passed by value, this essentially means the function will assign the value of the local variable from the generate function to the caller's scoped sq variable. For small structs such as this, that's perfectly fine.
What's more, a common thing for compilers to do is to optimise these kinds of functions to the equivalent of the second example I posted: Essentially your function will be creating a new Sqaure object on the stack memory of the caller. This can happen, that's not to say it will. It depends on the optimization levels used when compiling, and on the size of the struct you're returning.
Basically, if you want to keep the code as close to what you have now, it's probably easiest to stick to the first version (returning a heap pointer).
The more flexible approach is the second one (as it allows you to use stack and heap, depending on how you call the function).
For now, using the third approach is perfectly fine: the compiler will most likely optimize the code to whatever makes most sense anyway.
Try this:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct Square {
int value;
int **array;
} Square;
Square * generate();
int main(int argc, char *argv[]){
Square *sqrptr = generate();
printf("%d\n", sqrptr -> value);
/* It prints 1 */
/* Print out the 2D array */
int i,j;
for (i = 0; i < 3; i++){
for (j = 0; j < 3 ; j++){
printf("%d ", *(*((sqrptr -> array) + i) + j));
}
printf("\n");
}
/* It gives segmentation fault */
return 0;
}
Square * generate(){
Square* mySquare = (Square*) malloc(sizeof(Square)); //c++ compiler
//Square* mySquare = (void*) malloc(sizeof(Square)); //c compiler
mySquare->value = 1;
mySquare->array = malloc(sizeof(int*) * 3);
/* Initialize the 2D array */
int i,j;
for (i = 0; i < 3; i++){
*(mySquare->array + i) = malloc(sizeof(int) * 3);
for (j = 0; j < 3; j++){
*(*(mySquare->array + i) + j) = 0;
}
}
/* Print out the 2D array */
for (i = 0; i < 3; i++){
for (j = 0; j < 3l ; j++){
printf("%d ", *(*(mySquare->array + i) + j));
}
printf("\n");
}
/* I can see the complete 2D array here */
return mySquare;
}
I was writing a code the other day and I found it rather strange, that int** and int[][] does not behave the same way. Can anyone point out the differences between them? Below is my sample code, which fails with a segmentation fault, if I pass constant size 2d array, while it does work fine when I pass a dinamically allocated 2d array.
I am confused mainly because ant int[] array works the same as int*.
#include<stdio.h>
#include<stdlib.h>
void sort_by_first_row(int **t, int n, int m)
{
int i, j;
for(i = m-1 ; i > 0 ; --i)
{
for(j = 0 ; j < i; ++j)
{
if(t[0][j] < t[0][j+1])
{
int k;
for(k = 0 ; k < n ;++k)
{
int swap;
swap = t[k][j];
t[k][j] = t[k][j+1];
t[k][j+1] = swap;
}
}
}
}
}
int main(void) {
int i, j;
/* Working version */
/*int **t;
t =(int**) malloc(3*sizeof(int*));
for(i = 0; i < 3; ++i)
{
t[i] = (int*) malloc(6*sizeof(int));
}*/
/*WRONG*/
int t[3][6];
t[0][0] = 121;
t[0][1] = 85;
t[0][2] = 54;
t[0][3] = 89;
t[0][4] = 879;
t[0][5] = 11;
for( i = 0; i < 6; ++i )
t[1][i] = i+1;
t[2][0] = 2;
t[2][1] = 4;
t[2][2] = 5;
t[2][3] = 3;
t[2][4] = 1;
t[2][5] = 6;
sort_by_first_row(t, 3, 6);
for(i = 0; i < 3; ++i)
{
for(j = 0; j < 6; ++j)
printf("%d ", t[i][j]);
printf("\n");
}
return 0;
}
So based on the below answers I realize, that a multidimensional array is stored continuously in a row major order. After some modification, the below code works:
#include<stdio.h>
#include<stdlib.h>
void sort_by_first_row(int *t, int n, int m)
{
int i, j;
for(i = m-1 ; i > 0 ; --i)
{
for(j = 0 ; j < i; ++j)
{
if(t[j] < t[j+1])
{
int k;
for(k = 0 ; k < n ;++k)
{
int swap;
swap = t[k*m + j];
t[k*m + j] = t[k*m + j+1];
t[k*m + j+1] = swap;
}
}
}
}
}
int main(void) {
int i, j;
/* Working version */
/*int **t;
t =(int**) malloc(3*sizeof(int*));
for(i = 0; i < 3; ++i)
{
t[i] = (int*) malloc(6*sizeof(int));
}*/
/*WRONG*/
int t[3][6];
t[0][0] = 121;
t[0][1] = 85;
t[0][2] = 54;
t[0][3] = 89;
t[0][4] = 879;
t[0][5] = 11;
for( i = 0; i < 6; ++i )
t[1][i] = i+1;
t[2][0] = 2;
t[2][1] = 4;
t[2][2] = 5;
t[2][3] = 3;
t[2][4] = 1;
t[2][5] = 6;
sort_by_first_row(t, 3, 6);
for(i = 0; i < 3; ++i)
{
for(j = 0; j < 6; ++j)
printf("%d ", t[i][j]);
printf("\n");
}
return 0;
}
My new question is this: How to modify the code, so that the procedure works with int[][] and int** also?
Realize that int **t makes t a pointer to a pointer, while int t[3][6] makes t an array of an array. In most cases, when an array is used in an expression, it will become the value of the address of its first member. So, for int t[3][6], when t is passed to a function, the function will actually be getting the value of &t[0], which has type pointer to an array (in this case, int (*)[6]).
The type of what is being pointed at is important for how the pointer behaves when indexed. When a pointer to an object is incremented by 5, it points to the 5th object following the current object. Thus, for int **t, t + 5 will point to the 5th pointer, while for int (*t)[M], t + 5 will point to the 5th array. That is, the result of t + 5 is the same as the result of &t[5].
In your case, you have implemented void sort_by_first_row(int **t, int n, int m), but you are passing it an incompatible pointer. That is, the type of &t[0] (which is what t will become in main) is not the same as what the function wants, a int **t. Thus, when the sorting function starts to use that address, it will think its indexing into pointers, when the underlying structure is an array of arrays.
int** is quite different from int[][]. int** is simply a pointer to a pointer and would appear like the following:
in reality, you can access the entire multidimensional array with simply int* pointing to the first element, and doing simple math from that.
Here is the result of the separate allocations (in your commented code):
However when you allocate a multidimensional array, all of the memory is contiguous, and therefore easy to do simple math to reach the desired element.
int t[3][6];
int *t = (int*) malloc((3 * 6) * sizeof(int)); // <-- similarly
This will result in a contiguous chunk of memory for all elements.
You certainly can use the separate allocations, however you will need to walk the memory differently.
Hope this helps.
int t[3][6] is very nearly the same thing as int t[18]. A single contiguous block of 18 integers is allocated in both cases. The variable t provides the address of the start of this block of integers, just like the one-dimensional case.
Contrast this with the case you have marked as "working", where t gives you the address of a block of 3 pointers, each of which points to a block of memory with 6 integers. It's a totally different animal.
The difference between t[3][6] and t[18] is that the compiler remembers the size of each dimension of the array, and automatically converts 2D indices into 1D offsets. For example, the compiler automatically converts t[1][2] into *(t + 1*6 + 2) (equivalent to t[8] if it were declared as a one-dimensional array).
When you pass a multi-dimensional array to a function, there are two ways to handle it. The first is to declare the function argument as an array with known dimension sizes. The second is to treat your array like a 1D array.
If you are going to declare the size of your array, you would declare your function like this:
void sort_by_first_row(int t[][6], int n)
or this
void sort_by_first_row(int t[3][6])
You either have to declare all array dimension sizes, or you can leave out the first size. In both cases, you access elements of t using t[i][j]; you've given the compiler enough information to do the offset math that converts from 2D notation to a 1D index offset.
If you treat it as a 1D array, you have to pass the array dimensions and then do the offset math yourself.
Here's a full working example, where f and f2 both generate the same output:
void f(int* t, int m, int n)
{
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
std::cout << t[i * n + j] << " ";
std::cout << std::endl;
}
void f2(int t[][6], int m)
{
for (int i = 0; i < m; i++)
for (int j = 0; j < 6; j++)
std::cout << t[i][j] << " ";
std::cout << std::endl;
}
int main()
{
int t[3][6];
int val = 1;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 6; j++)
{
t[i][j] = val;
val++;
}
}
f(&(t[0][0]), 3, 6);
f2(t, 3);
return 0;
}
One thing to note is the hack-ish way I had to pass t to f. It's been a while since I wrote in C/C++, but I remember being able to pass t directly. Maybe somebody can fill me in on why my current compiler won't let me.
A int ** is a pointer to a pointer to an int, and can be a pointer to an array of pointers to arrays of ints. A int [][] is a 2-dimensional array of ints. A two-dimensional array is exactly the same as a one-dimensional array in C in one respect: It is fundamentally a pointer to the first object. The only difference is the accessing, a two-dimensional array is accessed with two different strides simultaneously.
Long story short, a int[][] is closer to an int* than an int**.