I'm trying to make a grayscale filter with CS50.
First - Why my code isn't working where code that I found on GitHub which is similar to mine is working very well.
That's my code:
void grayscale(int height, int width, RGBTRIPLE image[height][width])
{
int i = 0, j = 0;
float rgbGray;
while (i < height)
{
while (j < width)
{
rgbGray = (image[i][j].rgbtBlue image[i][j].rgbtGreen image[i][j].rgbtRed) / 3.00;
rgbGray = round(rgbGray);
image[i][j].rgbtBlue = rgbGray;
image[i][j].rgbtGreen = rgbGray;
image[i][j].rgbtRed = rgbGray;
j ;
}
i ;
}
return;
}
And that is working code which I found on GitHub
void grayscale(int height, int width, RGBTRIPLE image[height][width])
{
float rgbGray;
for (int i = 0; i < height; i )
{
for (int j = 0; j < width; j ) {
rgbGray = round( (image[i][j].rgbtBlue image[i][j].rgbtGreen image[i][j].rgbtRed)/ 3.00);
image[i][j].rgbtBlue = rgbGray;
image[i][j].rgbtGreen = rgbGray;
image[i][j].rgbtRed = rgbGray;
}
}
return;
}
Author: https://gist.github.com/ndiecodes
Second question is - why there aren't pointers needed. I mean in lecture there is information, that we need to use pointers in cases where we want to change values of elements. This function isn't returning any values - it's just VOID - so I'm curious why this second code is working properly
CodePudding user response:
Your code, which uses while loops, doesn't reset j
back to zero after the loop. As a result, nothing is done for every row of the array after the first.
Second, there isn't an explicit pointer here, but C arrays are effectively passed as if (speaking loosely) they were pointers to the data, so your accesses to image
affect the data that the caller sees.
CodePudding user response:
Second question is - why there aren't pointers needed. I mean in lecture there is information, that we need to use pointers in cases where we want to change values of elements.
Arrays are weird.
Except when it is the operand of the sizeof
or unary &
operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T
" will be converted (or "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.
When you call a function with an array expression as an argument, what the function actually receives is a pointer to the first element:
int arr[10];
foo( arr ); // equivalent to foo( &arr[0] );
...
void foo( int *a ) { ... }
In your case, when you call grayscale
like so:
RGBTRIPLE image[rows][cols];
...
grayscale( rows, cols, image );
what grayscale
actually receives is a pointer to a cols
-element array of RGBTRIPLE
:
void grayscale( int height, int width, RGBTRIPLE (*image)[width] )
{
...
}
However...
In the context of a function parameter declaration, any parameters with type T []
or T [N]
will be "adjusted" to type T *
, so I can declare foo
as any of
void foo( int a[10] )
or
void foo( int a[] )
or
void foo( int *a )
and all are interpreted the same way - a
is a pointer to int
, not an array of int
. That's why you can still declare grayscale
as
void grayscale( int height, int width, RGBTRIPLE image[height][width] )
Array subscripting is defined in terms of pointer operations. The expression a[i]
is defined as *(a i)
- given a starting address a
, offset i
objects (not bytes!) from that address and dereference the result:
a[0] == *(a 0) == *a
a[1] == *(a 1)
a[2] == *(a 2)
etc.
Array access is why the decay rule exists in the first place. C was derived from an earlier language named B (which was derived from BCPL, which was derived from CPL, which was influenced by Algol, etc.). In the B language, there was an explicit pointer to the first element of the array. Given the declaration
auto vec[10];
you'd get this in memory:
---
vec: | | ----------
--- |
... |
--- |
| | vec[0] <--
---
| | vec[1]
---
...
So in B the equivalence of a[i]
== *(a i)
makes sense - you always had an explicit pointer to the first element.
When he was designing C, Ritchie wanted to keep B's array behavior, but he didn't want to set aside space for the explicit pointer that behavior required - he just wanted to set aside space for the array elements themselves. Given
int vec[10];
you get
---
vec: | | vec[0]
---
| | vec[1]
---
...
so he came up with the decay rule - any time the compiler sees an array expression that isn't the operand of &
or sizeof
(or _Alignof
), it replaces that expression with a pointer to the first element of the array.
This is why you don't need to use &
when passing an array argument to functions like scanf
, and why you can't return arrays from functions - all that gets returned is a pointer to the first element of the array, which ceases to exist when the function returns.