我认为这可以解释这种方式,由于一个图像相当于一千字s ...
我们将从char name[] = "Fortran"
开始,这是一个字符数组,长度在编译时已知,确切的说是7,对吗?错误!它是8,因为'\ 0'是一个nul结尾字符,所有字符串都必须有。
char name[] = "Fortran";
+======+ +-+-+-+-+-+-+-+--+
|0x1234| |F|o|r|t|r|a|n|\0|
+======+ +-+-+-+-+-+-+-+--+
在链接时,编译器和链接给符号name
的一个0x1234的内存地址。 例如,使用下标运算符(即name[1]
),编译器知道如何计算内存中位于偏移量0x1234 + 1 = 0x1235处的字符,并且确实为'o'。这很简单,此外,对于ANSI C标准,数据类型的数据类型的大小为1个字节,这可以解释运行时如何获得该语义的值name[cnt++]
,假设cnt
是一个int
eger并且具有值3例如,运行时自动递增1,并从零开始计数,偏移值为't'。迄今为止这很简单。
如果执行name[12]
会发生什么情况?那么,代码将会崩溃,否则你会得到垃圾,因为数组的边界是从索引/偏移0(0x1234)到8(0x123B)。之后的任何内容都不属于name
变量,这将被称为缓冲区溢出!
的name
在内存地址为0x1234,而作为例子,如果你做到这一点:
printf("The address of name is %p\n", &name);
Output would be:
The address of name is 0x000
为了简洁起见,用的例子保持一致,内存地址是32位,因此,你看到额外的0。很公平?好吧,让我们继续前进。
现在到指针... char *name
是一个指针类型的char
....
编辑: 我们初始化如图感谢丹的指出了一点误差为NULL ...
char *name = (char*)NULL;
+======+ +======+
|0x5678| -> |0x0000| -> NULL
+======+ +======+
在编译/链接时,该name
没有指向任何东西,但对于符号编译/链接时的地址name
(0x5678),实际上它是NULL
,因此name
的指针地址是未知因此0x0000。
现在,请记住,这是至关重要的,符号的地址是在编译/链接时已知,但指针的地址是未知的,与任何类型的指针
假设我们做交易时此:
name = (char *)malloc((20 * sizeof(char)) + 1);
strcpy(name, "Fortran");
我们称为malloc
分配一个内存块为20个字节,不,它不是21,I上的尺寸增加1的原因是为“\ 0” NUL结束符。假设在运行时,给出的地址为0x9876,
char *name;
+======+ +======+ +-+-+-+-+-+-+-+--+
|0x5678| -> |0x9876| -> |F|o|r|t|r|a|n|\0|
+======+ +======+ +-+-+-+-+-+-+-+--+
所以,当你这样做:
printf("The address of name is %p\n", name);
printf("The address of name is %p\n", &name);
Output would be:
The address of name is 0x00005678
The address of name is 0x00009876
现在,这是那里的错觉,“数组和指针是一样的进场这里“
当我们这样做:
char ch = name[1];
什么发生在运行时是这样的:
- 符号
name
的地址查找
- 取该符号,即0x5678的内存地址。
- 在该地址,包含另一个地址,一个指向内存的指针地址,并获取它,即0x9876
- 获取基于下标值1的偏移量并将其添加到指针地址,即0x9877以检索那个内存地址,即'o'并被分配到
ch
。
,上面是理解这种区别的关键,数组和指针的区别就是运行时如何获取数据,在指针上,有获取额外的间接。
记住,类型T的阵列将总是衰变成类型T的第一个元素的指针。
当我们这样做:
char ch = *(name + 5);
- 符号
name
的地址查找
- 取该符号,即0x5678的内存地址。
- 在这个地址,包含了另一个地址,指针地址,内存,把它拿来,即0x9876
- 获取基于对5的值,并将其添加到地址指针偏移,即0x987A检索在该值内存地址,即'r',并被分配到
ch
。
顺便说一下,你也可以做到这一点的字符数组也...
更进一步,通过在一个阵列,即char name[] = "...";
和name[subscript_value]
的环境中使用标运算符是真的一样*(名称+下标_值)。 即
name[3] is the same as *(name + 3)
而且由于表达*(name + subscript_value)
是交换,这是反向,
*(subscript_value + name) is the same as *(name + subscript_value)
因此,这就解释了为什么在回答一个上面可以写这样的( 尽管它,尽管它是非常合法的,不推荐这种做法!)
3[name]
好的,我如何获得指针的值? 这是*
是做什么用的, 假设指针name
有0x9878该指针的内存地址,再次,指的是上面的例子,这是它是如何实现的:
char ch = *name;
这意味着,获得内存地址0x9878指向的值,现在ch
的值将为'r'。这被称为解引用。我们只取消了一个name
指针来获取值并将其分配给ch
。
此外,编译器知道sizeof(char)
是1,因此你可以这样做
*name++;
*name--;
指针递增/递减操作指针自动步上/下由一个结果。
当我们做到这一点,假设0x9878指针存储器地址:
char ch = *name++;
什么是* name的值和地址是什么,答案是,在现在将包含“T”和将其分配给ch
,并且指针存储器地址为0x9879。
在这里你必须小心谨慎,其原理和精神与前面关于第一部分内存边界的内容相同(参见'如果名字[12]被执行会发生什么'以上)结果将是相同的,即代码崩溃和烧伤!
+======+ +======+
|0x5678| -> |0x0000| -> NULL
+======+ +======+
是,内存块被释放:现在
,如果我们解除分配的内存块指向name
通过调用C函数free
与name
作为参数,即free(name)
会发生什么并交还给运行时环境供其他即将到来的代码执行malloc
使用。现在
,这就是分段故障的通用符号进场,因为name
没有指向任何东西,会发生什么,当我们取消对它的引用,即
char ch = *name;
是,该代码将崩溃,用“分段错误”进行刻录,这在Unix/Linux下很常见。在Windows下,对话框将沿着“不可恢复的错误”或的线条出现“已与应用程序时发生错误,你希望将报告发送给Microsoft?” ......如果指针一直没有malloc
d和任何解除引用的企图都会被保证崩溃并烧毁。
另外:记住这一点,每一个malloc
有相应free
,如果没有相应的free
,你必须在内存分配但未被释放了内存泄漏。
有你有它,那就是指针工作,以及如何阵列如何为指针不同,如果你正在阅读一本教科书,说他们是一样的,撕了该网页,撕了! :)
我希望这是帮助您了解指针。
哇!感谢这个美好的,详细的答案! :O – lamas 2010-01-24 01:22:22
未初始化并不意味着初始化为NULL。 – 2010-01-24 01:40:53
@丹:哦...好...我会解决这个问题了...谢谢指点出来... – t0mm13b 2010-01-24 01:59:20