为何指针变量分配动态空间后,其指向的变量可以直接当数组用??

2025-02-24 19:10:50
推荐回答(5个)
回答1:

简单的说,使用指针的时候,*(p+1)和p[1]都是合法的。它们语义上没有区别。因此不存在*(p+1)可以p[1]不可以的情况。
在使用指针的过程中,我们需要关注的除了指针的内容外,指针是否指向有效的内存空间也是十分需要关注的。
大大的问题主要需要关注的正是指针所指内存的有效性。
大大的问题本身就不合理。这表明大大对指针的理解不够未够充分。一般开始学指针的人都有这样的问题。因此不直接回答大大所提的问题。

回答这个问题,可以从抽象的语言上的语法上的范畴来解释。但也可以从实际的,靠近机器的范畴来回答。我尝试选择后者。

先理解以下:
1. C语言有变量,变量有类型,譬如 char, int,float...,也包括指针类型
2. 不同的类型有不同的内存大小。char 1 byte, int 4 或8 byte,所有指针类型都是4或8byte
3. float标记的是浮点,int标记的是整形,而指针标记的是内存地址。
4. 指针用 *标识,而前面的类型(int *的int,char *的char, 等等)所标识的是读取方式。
5. 虽然C语言的变量是有类型的,内存本身并没有这个概念。对于内存来说,它自身只有地址和数值。

如果大大能理解这些的话。请大大回答,没有类型的内存是如何变成有类型的变量的?这估计很多初学者都难以回答。
所谓的类型,本质上是数据的意义和数据的操作的集合。
二进制码:0000 0000 0100 0001 它可以标识 字符‘null'和'A',也可以标识整形65,或者16位的颜色代码。对于字符我们可以打印,对于整形,我们可以加减乘除,对于颜色,我们可以显示。。。
因此,对于前面这块2byte内存,我们可以进行各种不一样的操作。而决定进行怎样的操作有两个关键:1,内存的抽象类型,2,类型的操作集合。
假设,我们之前的这块内存的地址是0x0001
void *p = 0x0001; 则让一个不知类型的指针指向0x0001这块内存。
int *p = 0x0001;则让一个指针指向0x0001这块内存,并告知程序此内存可以执行整形操作,譬如加减乘除,或者 void func(int i);等等。
typedef int16_t color16;
color16 *p = 0x0001;则让一个指针指向0x0001这块内存,并告知程序它可以执行与color16类型相关的操作。

如是,指针存储的是内存地址,前面的类型,则标识指针可执行的操作。
然而还有一个最很关键的问题没有讨论。那就是内存的大小。
虽然指针描述了内存的地址,但内存的有效大小是多少却没有给出。譬如,刚才的那块内存是16位。但指针并不知道它的合法长度。
事实上,每次读取指针内容的时候会按照提示的类型的大小进行读取。如果是char 则每次读取1byte。p+1或者p[1]的内容是0000 0000 (null),p+2或者p[2]的内容是0100 0001 ('A')。
如果是 int16_t或者short,每次读取2byte。p+1或者p[1]的内容是0000 0000 0100 0001。p+2或者p[2]的则语义上不合法(语法上合法),因为,超出有效内存的范围。

最后,malloc是为程序分配动态内存的方法。当调用malloc(100)的时候,系统则分配100byte给程序。这块内存直到程序结束或者使用free(void *)的时候才会被重新释放。而这100type的空间能够作为100个char的空间,或者25个32位int的空间(因此,int *p = (int *)malloc(100); p的最大合法下标是p[24]。它与int arr[100]; 是不同的)。
数组的定义,与指针之间的主要差别是:

数组:
必须有类型。给出单元的大小;
非动态分配。数组的内存是在栈里面的。当栈被pop的时候内存就被释放。也就是说,当数组所在的scope结束,数组就被释放。(所谓scope在C里面就是一对的{}括号的范围)譬如:
if(true){
int arr[10]; //arr 被分配
} //arr 被释放

指针:
可以没有类型。void的时候,算是没有类型,没有给出单元的大小,虽然void某种程度上是一种类型。
可以指向动态内存。指针所指向的未必是动态内存,但可以是动态内存。动态内存是在堆里面分配的,一般是用malloc,calloc,或者realloc,来分配。动态内存是需要用free()来释放的,静态内存一定不要用free()释放,他会自动被释放。例如
void *func(){
void *p = malloc(100);

return p; //p所指向的内存不会被释放。

}

回答2:

int p[100]; 如此声明,其实就是声明了一个int *p;这个p指向了一个有100个int空间的内存区域,有点类似int *p= (int*)malloc(sizeof(int)); 只不过这两者p所指向的区域不同。int p[100]这里的p指向调用栈里的某一块区域,这块区域是随着函数的调用而建立的一块区域,随着函数的返回,这块区域在逻辑上“消亡”,所以当函数返回后,该局部变量不能被继续使用了;而int *p= (int*)malloc(sizeof(int));这里的p指向全局的一个被叫做“堆”的区域,这个区域不随函数返回而消亡,所以这个p是可以在函数外使用的,当然这个区域如果不用free来释放的话会一直存在,可能造成内存泄露。
至于你说的int *p;能写*(p+1)不能写p[1];这个不知道你是根据什么来说的,应该如果直接写的话编译可以通过,但是运行时会报错,因为你的p没有指向一个具体的位置,如果指向了内存不允许你写的一个区域,就会报错。
int *p=int * malloc(100);这个可以写p[1]是正常的,因为p已经指向了内存堆区,所以p[1]就应该是第二个内存地址处。
如果我的回答有不解之处,欢迎追问

回答3:

你理解错了,之所以能写*(p+1)是因为p指向了一个数组的首地址
才能使用*(p+1),可以使用*(p+1)的地方就可以用p[1];

而所以不行,是因为后面的内存空间未被定义
而用一个 int *p = int * malloc (100) ; 函数,为p申请了一个内存,并指向它的首地址
相当于定义了一个数组,而p就是这个数组的名字

你定义a[10];也可以写*(a+5)的

回答4:

int a[10];
int *p = a;

a[1] 等价于 *(a+1) 等价于 p[1] 等价于 *(p+1)
因为【语法规定, 数组名等价与其第一个元素的指针】

int *p = malloc (sizeof(int)*100) );
可以把p理解为一个100个int的数组。 p指向数组第一个元素。 *p等价于 p[0]

p 大部分情况下和 数组a是完全等价的。
极少数情况例外, 比如说 sizeof(a)得到的是 40, 而sizeof(p)得到的是4
因为sizeof(数组)得到整个数组的字节数, 而sizeof(指针)得到一个指针的字节数

回答5:

因为[]在C++中是一种运算符,p[1]等价于 *(p+1),表示取 p+1中的内容。p+1的值是p向后移动sizeof(int)*1个字节,而 p+2就是p向后移动sizeof(int)*2个字节。