我正在观看YouTube上来自Bucky的有关堆的教学视频。
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
int n, howMany;
int total;
float average = 0.0;
int * pointsArray;
printf("How many numbers do you want to average?\n");
scanf(" %d", &howMany);
pointsArray = (int *) malloc(howMany * sizeof(int));
printf("Enter numbers! \n");
for(n = 0; n<howMany; n++){
scanf(" %d", &pointsArray[n]);
total += pointsArray[n];
}
average = (float)total /(float)howMany;
printf("average is %f", average);
free(pointsArray);
return 0;
}
我不明白在上面的for循环中,pointer-pointsArray如何从指针变为指针数组。 我对变量的rvalue和Ivalue有了解。如果您可以在回答中包含这些概念,将不胜感激。
当编译器看到&pointsArray [n]时,为什么编译器知道如何在数组中输入值?
我能够确定我的问题应该出在我对数组和变成数组的指针的困惑中。我可以有一个清晰的定义,以便我可以将它们分开吗?
非常感谢。如果我的问题不清楚,请告诉我。我会尝试改善它。
答案 0 :(得分:1)
数组是连续的内存块,变量保存内存的开始。
int* pointsArray
pointsArray = malloc(sizeof(*pointsArray) * howMany);
现在pointsArray
拥有(指向)某些内存块,在这种情况下由malloc
返回,您通常可以使用数组批注来访问内存条目。
您的指针指向类型int
的内存,该类型的内存(我认为)分别在您的机器4 bytes
上。当您尝试访问内存pointsArray[n]
时,您正在访问内存位置:
pointsArray[0] = pointsArray + 4 * 0;
pointsArray[1] = pointsArray + 4 * 1;
pointsArray[n] = pointsArray + 4 * n;
如果我们采用非常通用的方式,则每个索引的偏移量为sizeof(type)
,在您的情况下,类型为int
,即4
个字节。
pointsArray[n] = pointsArray + sizeof(*pointsArray) * n
请不要因为强制放弃malloc的返回值不是一个好主意,并且在大小计算开始时添加sizeof(...)
部分会更好,更安全。
答案 1 :(得分:1)
我不明白pointer-pointsArray如何从指针更改为指针 上面的for循环中的指针数组。我对右值和 变量中的Ivalue。如果您可以包括这些,我将不胜感激 答案中的概念。
- pointsArray在哪一行变为指针数组? pointsArray =(int *)malloc(howMany * sizeof(int));应该只增加内存 pointsArray的大小。
首先,让我们从基础开始。指针不是数组,数组也不是指针,它们是C中的两个独立类型。如C11 Standard - 6.3.2.1 Lvalues, arrays, and function designators(p3)所述,数组类型在访问时被转换为指向数组第一个元素的指针。 >
int * pointsArray;
...
pointsArray = (int *) malloc(howMany * sizeof(int));
什么是pointsArray
? (键入int
是指针)。
那么,什么是指针?
指针只是一个普通变量,它以其他值作为地址。换句话说,指针指向可以找到其他内容的地址。在通常情况下,您会想到一个包含立即数的变量,例如int a = 5;
,指针将仅保存5
存储在内存中的地址,例如int *b = &a;
声明b
作为类型int
的指针,并将其值初始化为5
存储在内存中的地址(例如b
指向 a
-当前存储5
的位置)。无论指针指向哪种对象,其工作方式都相同。
无论类型如何,指针算术对所有指针都将起作用,因为指针的type
控制着指针算术,例如:使用char *
指针,pointer+1
指向pointer
之后的下一个字节。对于int *
指针(通常为4个字节的整数),pointer+1
将指向下一个 integer ,其偏移量为pointer
之后的4个字节。 (因此,一个指针只是一个指针。...type
自动处理算术的地方)
- pointsArray在哪一行变为指针数组? pointsArray =(int *)malloc(howMany * sizeof(int));应该只增加内存 pointsArray的大小。
回想一下,“指针不是数组,而数组不是指针。”数组可以通过以下方式声明:
type name[CONST]; /* declares an array of type with CONST elements */
type name[var]; /* declares a VLA C99+ with var no. of elements */
type name[] = { 1, 2, 3 }; /* declares & initializes an array of 3 elements */
对比:
type *name; /* declares a pointer to type */
对于具有恒定数量的元素或带有初始化列表的数组,将为该数组提供自动存储。可变长度数组(VLA)的存储类似,但在另一个段中(通常在.bss [用符号表示的块]中)。访问时(“除外,它是sizeof
运算符,_Alignof
运算符或一元&
运算符的操作数,或者是用于初始化数组的字符串文字“ ),数组将转换为 pointer类型的指针,该类型指向数组对象的初始元素,而不是左值。 6.3.2.1(p3)(您经常会看到一个“数组衰减到指针” 的短语-指的是这种转换)
(注意:它不能反向工作,指针永远不会转换为数组)
声明type *name;
声明name
作为指向type
的指针。它尚未初始化,并且具有不确定的地址。任何尝试访问或以其他方式使用未初始化的指针的操作都会调用未定义行为(通常会导致分段错误)。指针必须具有有效地址(例如,它必须指向有效存储),然后才能访问内存。为指针提供有效存储的最简单方法是动态分配内存块,并将该块的起始地址分配给指针。这是通过以下代码在您的代码中完成的:
pointsArray = (int *) malloc(howMany * sizeof(int));
(注意:无需强制返回malloc
,这是不必要的。请参阅:Do I cast the result of malloc?)
更好的方法是:
pointsArray = malloc (howMany * sizeof *pointsArray);
({pointsArray
的类型为int*
,因此*pointsArray
的类型为int
。如果将取消引用的指针用作类型大小,则将消除潜在的类型-大小错误)
以上,malloc
保留有效的内存块,保证其大小至少为howMany * sizeof *pointsArray
个字节。内存块的起始地址已分配给pointsArray
(例如pointsArray
现在指向该块)内存具有已分配的存储持续时间,并一直保持有效,直到您{{1 }},否则程序结束。您有责任保留一个指针,该指针包含内存块的起始地址,以便在不再需要时可以释放该指针。
您现在可以在free()
指向的内存块中最多存储howMany
个整数。要访问整数的第一个地址,您可以使用指针符号使用pointsArray
(仅仅是*(pointArray + 0)
),也可以使用使用*pointsArray
数组索引符号。 (关键是要理解pointsArray[0]
的行为与[]
中的'*'
一样)。因此,要访问第二个元素,可以使用*(name + offset)
或*(pointsArray + 1)
等。虽然您可以将 array index 标记与指针一起使用-但这不会使指针成为数组或不会将其转换为数组。
- 当编译器看到
pointsArray[1]
时,为什么编译器知道如何在数组中输入值?
与&pointsArray[n]
的要求相比,与scanf
的要求有关的更多。想一想。使用pointsArray
格式转换说明符%d
时从man 3 scanf():
scanf
因此,如果您想将整数读入用Matches an optionally signed decimal integer; the next pointer
must be a pointer to int.
声明的变量中,则使用int a;
将是:
scanf
对于要由if (scanf ("%d", &a) == 1) {
/* you have a good integer */
}
else {
/* a matching or input failure occurred */
}
放置在scanf
中的整数值,必须提供指向a
的指针。要将值放在整数a
中也没什么不同。您如何提供指向 (pointsArray[n]
的地址)的指针? (答案:您以运算符的一元pointsArray[n]
的一元地址('&'
开头)
希望这也会照顾到您的三号位。仔细研究一下,如果您还有其他问题,请告诉我。
答案 2 :(得分:0)
数组只是一个连续的内存位置,因此,如果您具有第一个元素的地址,则可以通过添加到第一个地址来访问其他元素。
因此,如果您有一个指向数组第一个元素的指针,例如
pointsArray = (int *) malloc(howMany * sizeof(int));
pointsArray
指向第一个元素。
现在可以访问其他元素了,您可以使用pointsArray[n]
转换为pointsArray + sizeof(*pointsArray) * n
(第n个元素的地址)来访问其他元素。
答案 3 :(得分:0)
pointsArray
在哪一行成为指针数组?
pointsArray = (int *) malloc(howMany * sizeof(int));
仅应增加pointsArray
的内存大小。
您是正确的。它不会成为指针数组。
当编译器看到&pointsArray [n]时,为什么编译器知道如何在数组中输入值?
编译器与此无关。该代码仅将sizeof(int)
(4字节)范围内的输入值读入pointsArray
所指向的内存中。在for
循环的每次迭代中,它都会移动四个字节并将输入的值写入该内存位置。
我能够确定我的问题应该在于我对数组和变成数组的指针的困惑。我可以有一个清晰的定义,以便我可以将它们分开吗?
由于指针包含地址位置的值,因此包括数组在内的任何类型的地址都是指针。因此可以在其上应用指针算法。
答案 4 :(得分:0)
指针只是内存中特定单元的地址(索引),它知道其类型的基本长度,例如int32_t *p = 0x12345678
是字节0x12345678处的地址,长度为4个字节。因此,当我们执行p+1
时,结果为0x1234567c
。注意Pointer
本身是一种长度为4或8个字节的类型,具体取决于您的盒子。
对于您的问题,
pointsArray
始终是int
的指针。它不知道有关数组的任何信息,例如数组的长度。在pointsArray = (int *) malloc(howMany * sizeof(int))
行之后,pointsArray
具有有效值(地址),该值是分配给int数组的内存的地址。 (pointsArray
不知道数组的长度。)&pointsArray[n]
,编译器只需将其写入地址pointsArray + n
的内存中即可。sizeof
时,可以获取数组的大小,而对于const指针您将获得指针的大小)。在C语言中,我不想使用array
的概念,而只是将其视为某种类型的const指针。