C语言 程序的指针访问表示法



代码运行与我预测的不同,我认为*(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("%4dt", *(score + i * n + j));
}
printf("%5dt%6.1fn", 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

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

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

正如您所注意到的,问题是您的数组访问—您使用score[i][j]在main中填充数组,然后在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一样访问它。

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

(credit for method)

按原样寻址代码。首先,下面的代码创建了变量,但没有初始化任何变量:

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类型的函数参数

还要注意,数组如:(从您的值中缩短,以便于查看)

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);
...
...