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

首先我们声明 int *p ;
我们可以写 *(p +1);但是不能写 p[1]; 因为数组没有定义

但是如果 int *p = int * malloc (100) ; 我们接下来就能直接写 p[1]; p[2] ;而不用事先声明数组 ,这是为什么?

简单的说,使用指针的时候,*(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所指向的内存不会被释放。

}追问

我试了一下,确实有 *(p+1)就可以有p[1] 。无报错。但是只有*(p+1)时程序正常运行,如果加上p[1] 运行就会出错,内存无法read.

这就很奇怪。有*(p+1)的地方可以有p[ ] ,说可以是可以,无报错。
说不可以,也好像有道理,因为程序无法正常运行。

再说本质上,*(p+1)跟p[1]是一样的吧,但为何内存无法read呢

我又试了下int * p = (int *)malloc(40);
结果是两者都可以。无报错运行无错

追答

没有报错主要是因为语法上合法。
在编程的错误有很多种,我们可以未各种错误进行分类。其中一种分类方法是,语法错误 vs 语义错误。
例如:
int a,b;
b = 0;
a = 100/b;
这段代码是可以通过编译的,所以语法上是OK的。但语义上是有错的,因为它尝试除以零。
大大的情况正是如此!

如之前所说的,使用指针的时候需要关注一个很重要的问题。那就是,你的指针所指向的内存是否合法。以下面的代码进行测试:

int *p;
int a;
a = 100;
p = &a;
printf("%d\n", *(p + 1));
printf("%d\n", p[1]);

你会发现,程序是可以编译的,但未必可以正常运行。即使可以运行,他的运行结果一般我们认为是没有意义的。
这主要是因为,p + 1或者p[1]这块内存是没有被(明显)定义的,而程序却想读取这块内存。如果这块内存在程序的内部的其他地方定义了,则可以读取,但读取出来的内容应该不是大大原来想要读取的东西(所以没有意义)。但如果这块内存它并不属于这个程序,则很有可能在运行的时候报错。另外,当指针指向不合法的内存的时候报错比不报错的好。在不报错的情况,你所执行的操作的结果是不明确的,而且这种操作很难察觉,造成的后果可以非常严重。

此外,大大的所谓p+1不报错,p[1]报错的原因有多种。其中一些可能的问题有:编译器的特性(虽然数组和指针本质上是一致的,但不同的编译器对指针和数组的处理是有微小的区别,部分编译器为了减少一些隐形错误可能对数组或这种进行额外的查错);大大对所报错误是否正确理解;代码上是否有其他地方引发错误,等等。
大大的代码运行出错的原因要具体看 大大的代码以及大大所用的编译器。

最后总结,大大所说的第一个报错,是因为,大大本来就将p指向了一个不合法的内存。
而后面说不报错,是因为这片内存是合法的。

结尾,给一个介绍C指针不错的参考材料(英文)

参考资料:http://www.cplusplus.com/doc/tutorial/pointers/

温馨提示:答案为网友推荐,仅供参考
第1个回答  2013-02-16
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]就应该是第二个内存地址处。
如果我的回答有不解之处,欢迎追问追问

我试了一下,确实有 *(p+1)就可以有p[1] 。无报错。但是只有*(p+1)时程序正常运行,如果加上p[1] 运行就会出错,内存无法read. p我给他一个地址,那么p +1 p[1 ]不算没指向吧
这就很奇怪。有*(p+1)的地方可以有p[ ] ,说可以是可以,无报错。
说不可以,也好像有道理,因为程序无法正常运行。
再说本质上,*(p+1)跟p[1]是一样的吧,但为何内存无法read呢

追答

*(p+1), p[1]是等价的,具体为何加上p[1]就会报错,能否贴上您的测试程序的这一段来共同研究下?

*(p+1), p[1]是等价的,具体为何加上p[1]就会报错,能否贴上您的测试程序的这一段来共同研究下?

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

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

你定义a[10];也可以写*(a+5)的本回答被网友采纳
第3个回答  2013-02-16
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(指针)得到一个指针的字节数
第4个回答  2013-02-17
因为[]在C++中是一种运算符,p[1]等价于 *(p+1),表示取 p+1中的内容。p+1的值是p向后移动sizeof(int)*1个字节,而 p+2就是p向后移动sizeof(int)*2个字节。
相似回答