程序的指针访问表示

The pointer access representation of the program

代码运行和我预测的不一样,我觉得*(score+i*n+j)有问题,也可能是其他地方的问题,我不是很确定,但我不知道怎么办修改一下。

#include  <stdio.h>

#define STUD 30            // Maximum number of students possible
#define COURSE 5           // The maximum number of possible exam subjects

void  Total(int *score, int sum[], float aver[], int m, int n);
void  Print(int *score, int sum[], float aver[], int m, int n);

int main(void)
{
    int i, j, m, n, score[STUD][COURSE], sum[STUD];
    float aver[STUD];
    printf("Enter the total number of students and courses:\n");
    scanf("%d %d",&m,&n);
    printf("Enter score:\n");

    for (i = 0; i < m; i++)
    {
        for (j = 0; j < n; j++)
        {
            scanf("%d", &score[i][j]);
        }
    }

    Total(*score, sum, aver, m, n);
    Print(*score, sum, aver, m, n);
    return 0;
}

void  Total(int *score, int sum[], float aver[], int m, int n)
{
    int  i, j;

    for (i = 0; i < m; i++)
    {
        sum[i] = 0;

        for (j = 0; j < n; j++)
        {
            sum[i] = sum[i] + *(score + i * n + j);
        }

        aver[i] = (float) sum[i] / n;
    }
}

void  Print(int *score, int sum[], float aver[], int m, int n)
{
    int  i, j;
    printf("Result:\n");

    for (i = 0; i < m; i++)
    {
        for (j = 0; j < n; j++)
        {
            printf("%4d\t", *(score + i * n + j));
        }

        printf("%5d\t%6.1f\n", sum[i], aver[i]);
    }
}

程序示例运行:

Enter the total number of students and courses:
2 3↙
Enter score:
90↙
95↙
97↙
82↙
73↙
69↙
Result:
  90  95  97  282  94.0
  82  73  69  224  74.7

OP 不清楚为什么代码使用 #defines 来定义数组 score 的行和列的值,然后继续使用 scanf() 输入可能与 #defines 冲突或不冲突的新值,甚至溢出数组内存。任何一种方法都有效,但同时使用这两种方法会使事情变得混乱。任选其一。

旁白:如果需要动态大小的数组,则可以将其创建为指针,或指向已分配内存的指针,或使用 VLA

例如:使用用户输入和动态内存分配来创建大小适合当前需要的数组的简短示例:

注意:以下方法允许您使用普通数组表示法来赋值:

score[i][j] = someValue://easy to use and readable
//as opposed to 
*(score + i*n + j) = someValue;// cumbersome to use and read  

示例:

int student, course;//using descriptive variables
printf("Enter the total number of students and courses:\n");
scanf("%d %d",&student,&course);
int (*score)[course] = create_arr_2d (student, course);
if(score)
{   //use score
    ...
    free(score);
}

定义create_arr_2d(...)的地方:

void * create_arr_2d (size_t x, size_t y)
{
    int (*ptr_2)[x] = malloc( sizeof (int *[y]) ); //allocate a true 2D array    
    if(ptr_2)
    {
        memset(ptr_2, 0, sizeof **ptr_2);
    }
    return ptr_2;
}

()

按原样处理您的代码。首先,下面创建变量,但不初始化它们中的任何一个:

int i, j, m, n, score[STUD][COURSE], sum[STUD];
float aver[STUD];

要消除您可能遇到的一些问题,请初始化:

int i=0, j=0, m=0, n=0, score[STUD][COURSE]={{0}}, sum[STUD]={0};
float aver[STUD]={0};

在您给定代码的函数原型中:

Total(int *score, int sum[], float aver[], int m, int n)

int *score

建议传递一个指向一维数组的指针,但如果它被用来表示 score[STUD][COURSE],那么它应该作为 `score[m][n] 传递,并更改原型如:

Total(int m, int n, int score[m][n], int sum[m], float aver[m]);  

然后称为:

Total(STUD, COURSE, score[STUD][COURSE], sum[STUD], aver[STUD]){...} 

注意,这种安排利用了VLA type function arguemnts

另请注意,一个数组,例如:(从您的值中缩短以便于查看)

int m = 5;
int n = 4
int array[m][n] = {{0}}   

创建一个连续的内存块,可以想象在内存中看起来像这样:

`|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|...`
 0         5        10         15       20 

所有元素都可以在这样的 for 循环中访问:

for(int i=0; i<m; i++
   for(int j=0;j<n;j++)
       *(array + i*n + j);
   ...
...

编译您的程序不会产生任何警告或错误。 运行 它与您提供的样本输入产生:

Enter the total number of students and courses:
2 3
Enter score:
90
95
97
82
73
69
Result:
  90      95      97      282     94.0
404780     0      82    404862  134954.0

第一组分数是正确的,但第二组不是。正如您的直觉,这意味着您通过指针数学访问数组的数学可能是错误的。

考虑一下您的数组在内存中的实际情况。您已经在堆栈上分配了一个如下所示的数组:

+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+

您的示例输入是这样填写的:

+---+---+---+---+---+
| 90| 95| 97|   |   |
+---+---+---+---+---+
| 82| 73| 69|   |   |
+---+---+---+---+---+
...

如果你想访问第二行的第一个元素,你需要你的偏移量是i * 5而不是i * 3,当你使用i * n时会发生这种情况。这个5我们可以从你的常量COURSE.

得到
*(score + i * COURSE + j)

当您使用不同的偏移量时,您会得到尚未初始化的数据,这就是您看到垃圾值的原因。如果将数组中的所有值初始化为 0,但保持代码不变,您可以看到实际效果。

int i, j, m, n, score[STUD][COURSE] = {0}, sum[STUD];
Enter the total number of students and courses:
2 3
Enter score:
90
95
97
82
73
69
Result:
  90      95      97      282     94.0
   0       0      82       82     27.3

正如您所注意到的,问题在于您的数组访问——您在 main 中使用 score[i][j] 来填充数组,然后在 TotalPrint 中使用 *(score + i * n + j)尝试访问它的函数,这些是不同且不兼容的。最简单的修复可能只是修复 TotalPrint 的声明以匹配您正在使用的 score

void  Total(int score[][COURSE], int sum[], float aver[], int m, int n);
void  Print(int score[][COURSE], int sum[], float aver[], int m, int n);

然后你可以在其中使用 score[i][j],一切都应该有效。您还可以将 score 作为 score 而不是 *score.

或者,将 score 的声明更改为 score[STUD*COURSE] 并在 main 中使用
*(score + i * n + j)(或 score[i*n + j])像您一样访问它在 TotalPrint.