【问题标题】:CS50 week 4 filter and "void vs pointers"CS50 第 4 周过滤器和“void vs pointers”
【发布时间】:2021-10-26 13:11:18
【问题描述】:

我正在尝试用 CS50 制作灰度滤镜。

首先 - 为什么我的代码无法运行,而我在 GitHub 上找到的与我相似的代码运行良好。

这是我的代码:

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;
}

这是我在 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;
}

作者:https://gist.github.com/ndiecodes

第二个问题是 - 为什么不需要指针。我的意思是在讲座中有信息,我们需要在想要更改元素值的情况下使用指针。这个函数没有返回任何值——它只是 VOID——所以我很好奇为什么第二个代码可以正常工作

【问题讨论】:

  • 您需要在第一个循环中每次将j初始化为0。否则j 将在i 为0 时到达width,之后内部循环将永远不会执行
  • 天哪。谢谢你!那是菜鸟的错误
  • 不需要在函数开头声明变量。在您需要的地方声明它们可以避免这个问题。
  • RGBTRIPLE的定义在哪里? rgbtBluergbtGreenrgbtRed 有哪些数据类型?在这部分你可能会面临整数溢出的风险:image[i][j].rgbtBlue + image[i][j].rgbtGreen + image[i][j].rgbtRed

标签: c cs50


【解决方案1】:

使用 while 循环的代码不会在循环后将 j 重置为零。结果,第一行之后的数组的每一行都没有做任何事情。

其次,这里没有 显式 指针,但 C 数组被有效传递,就好像(松散地说)它们是指向数据的指针,所以您对 image 的访问会影响数据调用者看到的。

【讨论】:

  • 推论:使用for 循环,以便将循环控件捆绑到一行中。
【解决方案2】:

第二个问题是 - 为什么不需要指针。我的意思是在讲座中有信息,我们需要在想要更改元素值的情况下使用指针。

数组很奇怪。

除非它是sizeof 或一元&amp; 运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则表达式 类型为“N- “T”的元素数组将被转换(或“衰减”)为类型为“指向T”的表达式,表达式的值将是数组第一个元素的地址。

当你调用一个以数组表达式作为参数的函数时,函数实际接收的是指向第一个元素的指针:

int arr[10];
foo( arr );  // equivalent to foo( &arr[0] );
...
void foo( int *a ) { ... }

在您的情况下,当您像这样调用grayscale 时:

RGBTRIPLE image[rows][cols];
...
grayscale( rows, cols, image );

grayscale 实际接收的是一个指针,指向cols-元素数组RGBTRIPLE

void grayscale( int height, int width, RGBTRIPLE (*image)[width] )
{
  ...
}

不过……

在函数参数声明的上下文中,任何类型为 T []T [N] 的参数都将“调整”为类型 T *,因此我可以声明 foo

void foo( int a[10] )

void foo( int a[] )

void foo( int *a )

所有的解释方式都相同 - a 是一个 指针int,而不是 int 的数组。这就是为什么您仍然可以将grayscale 声明为

void grayscale( int height, int width, RGBTRIPLE image[height][width] )

数组下标是根据指针操作定义的。表达式a[i] 定义为*(a + i) - 给定起始地址a,从该地址偏移i 对象(不是字节!)并取消引用结果:

a[0] == *(a + 0) == *a
a[1] == *(a + 1)
a[2] == *(a + 2)

等等

数组访问是为什么衰减规则首先存在的原因。 C 是从一种更早的语言 B 派生出来的(它是从 BCPL 派生的,它又是从 CPL 派生的,受 Algol 等的影响)。在 B 语言中,有一个指向数组第一个元素的显式指针。鉴于声明

auto vec[10];

你会在记忆中得到这个:

     +---+
vec: |   | ----------+
     +---+           |
      ...            |
     +---+           |
     |   | vec[0] <--+
     +---+
     |   | vec[1] 
     +---+
      ...

所以在 B 中,a[i] == *(a + i) 的等价性是有道理的 - 你总是有一个指向第一个元素的显式指针。

在设计 C 时,Ritchie 想保留 B 的数组行为,但他不想为行为所需的显式指针留出空间——他只想为数组元素本身留出空间。给定

int vec[10];

你得到

     +---+
vec: |   | vec[0]
     +---+
     |   | vec[1]
     +---+
      ...

所以他提出了衰减规则——只要编译器看到一个数组表达式不是&amp;sizeof(或_Alignof)的操作数,它就会将该表达式替换为指向数组的第一个元素。

这就是为什么您不需要在将数组参数传递给scanf 之类的函数时需要使用&amp;,以及为什么您不能从函数返回数组——所有这些都会返回是指向数组第一个元素的指针,当函数返回时,不再存在

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-26
    • 1970-01-01
    相关资源
    最近更新 更多