user-0 提问: C/C++ 指针的应用

回复:
user: user-1
content: int * p = 0x12345678; LZ说说我这样做还行？

user: user-2
content: 引用 楼主 zhuxuekui3 的回复:问题有三： 一：为什么我用指针作为函数的参数，返回的值却是错误的，而用指针的指针或者指针的引用就没问题        程序如下：     void GetMemory(char **p,int num)  //首先**p是二级指针，它指向的字符指针的地址，字符指针指向的是一个字符串，    p指向的是字符指针的地址，因为字符字符指针也是变量，它占用的是4个字节。 { 	*p = (char*)malloc(sizeof(char)*num); }  void main()       {	 	char *str = NULL;  //str是个指向字符串的指针，是一个指针变量，占用4个字节； 	GetMemory(&str,100); //刚才上面的**p已经讲过，p指向的是指向字符串的指针变量，str是指向字符串的指针变量，而形参需要的是指向字符指针的指针变量，因此需要将指向字符串的指针变量str的地址&str传给形参p； 	strcpy(str,"hello"); }  希望大神能够直观的给我说明下，         问题二：我在栈空间中去开辟堆空间，是不是可以认为 从栈的地址出发，可以找到堆里面的空间？                     还有就是，我在程序里面开辟100个char*型的空间，竟然可以打印200个空间，且每次都是+1，结果如下：                    那么剩下的100个地址空间到底是栈空间里面的还是堆空间里面的？       解答：首先，我们使用malloc第一次申请堆空间内存的时候，系统会在堆中分配的内存不是你需要的100字节，而是按页分配的，一次给你3页（具体好像不太确定），但是肯定是大于你申请的100字节，这些申请的内存空间是虚拟的内存空间，系统会将这些虚拟地址映射到实际的物理内存；而且我们访问其中数据使用的是虚拟内存地址；                其次，在C中编译器对数组越界问题不敏感，也就是说你定义的是100，但是输出的有200（这个200有一定前提，是因为你在申请空间的时候系统给你的是按页分配的，而且这些空间是虚拟内存空间，系统已经将这些虚拟内存地址映射到了实际的内存空间；               但是打印出来的东西可能是一些意想不到的垃圾数据，这些因为从101-200之间的实际物理内存里面存的是以前其他的进程存储数据留下的垃圾数据，操作系统并没有将他们清空，所以一般数组越界打印出来的都是垃圾数据；                 所以会打印出后面的100个字节的内容也不奇怪。        char *str = NULL; 	str = (char*)malloc(sizeof(char)*100);   //这个是在堆中申请的虚拟内存空间； 	for(int i = 0; i<200; ++i) 	   printf("%d  ",&str[i]); 	 printf("\n");          问题三：我突然有种想法，说是动态开辟空间，假如没有栈空间里面的地址，我们就无法找到堆空间里面的地址。也就是说，   每次从堆空间开辟内存，其实在栈空间中有唯一的地址值与之对应。也就是说，我在问题一当中，传入指针，然后在开辟内存空间，那么此函数空间里面的栈空间非主函数里面的栈空间，函数执行完毕，栈就被释放掉了，无法完成动态内存的开辟。而我们传入指针的指针，就可以保存刚刚在主函数里面的地址空间，把指针的地址传入进去，然后我们就可以操作此指针，并可以改变，然后copy出100个与此类型一样的堆空间，然后返回，虽然临时的栈被干掉了，但指针的地址没有被干掉，它返回了。这样是不是可以认为相当于在main函数当中开辟了100个栈地址来存储堆空间里面的东西。这就是索引的索引的意思。     解答：第三个问题不用给你讲了，刚才已经给你说了，就算是在函数内存使用malloc ，虽然函数是在栈里里面申请局部变量的，但是它用malloc 申请的内存却在堆中，如果你还想在main函数中使用这块堆空间的话，可以将指向这块内存的指针返回给函数： 但是：要记住，函数时不能返回局部变量的地址，但是可以返回局部变量的。可以这样定义函数： char * GetMemory(char **p,int num)   { 	*p = (char*)malloc(sizeof(char)*num);         return p； } 然后将函数返回值传给另一个main中的char指针变量，这样就可以继续使用这块堆内存空间了。

user: user-3
content: 第一次回帖啊，就像十楼说的那样，首先你的*str指向的是一个null，即0x00000000的地址，如果你在GetMemory（）中还是使用一级指针的话，形参p和和str指向同一块无效内存区域，然后你用malloc修改了形参p的地址，实际上str还是指向那个0x00000000的地址；如果你使用的是二级指针的话，通过修改一个二级指针所指向的对象的值达到修改了str原来的地址，就像swap()时如果传递的是指针，那么交换的实际上是指针的所指向的对象的值，指针还是原来那个指针。

user: user-4
content: 一级指针也是值，回想下学函数时交换两个数的值，我们传参传的是一级指针也就是要交换的数的地址，现在我们要在函数体内改一级指针的值，所以必须传一级指针的地址，也就是二级指针，通过传二级指针才能改一级指针的值。

user: user-5
content: 计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构……  对学习编程者的忠告： 眼过千遍不如手过一遍！ 书看千行不如手敲一行！ 手敲千行不如单步一行！ 单步源代码千行不如单步对应汇编一行！  VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 对VC来说，所谓‘调试时’就是编译连接通过以后，按F10或F11键单步执行一步以后的时候，或者在某行按F9设了断点后按F5执行停在该断点处的时候。 （Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。）  想要从本质上理解C指针，必须学习汇编以及C和汇编的对应关系。 从汇编的角度理解和学习C语言的指针，原本看似复杂的东西就会变得非常简单！ 指针即地址。“地址又是啥？”“只能从汇编语言和计算机组成原理的角度去解释了。” 但我又不得不承认：  有那么些人喜欢或者适合用“先具体再抽象”的方法学习和理解复杂事物；  而另一些人喜欢或者适合用“先抽象再具体”的方法学习和理解复杂事物。 而我本人属前者。  不要企图依赖输出指针相关表达式的值【比如printf("%p\n",...)】来理解指针的本质， 而要依赖调试时的反汇编窗口中的C/C++代码【比如void *p=...】及其对应汇编指令以及内存窗口中的内存地址和内存值来理解指针的本质。  这辈子不看内存地址和内存值；只画链表、指针示意图，画堆栈示意图，画各种示意图，甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗？本人深表怀疑！ 这辈子不种麦不收麦不将麦粒拿去磨面；只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头，压面条，烤面包，……能从本质上理解面粉、理解面食吗？本人深表怀疑！！  提醒： “学习用汇编语言写程序” 和 “VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 （Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。） 想要从本质上理解C指针，必须学习C和汇编的对应关系。” 不是一回事！  不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！  有人说一套做一套，你相信他说的还是相信他做的？ 其实严格来说这个世界上古往今来所有人都是说一套做一套，不是吗？  不要写连自己也预测不了结果的代码！  电脑内存或文件内容只是一个一维二进制字节数组及其对应的二进制地址； 人脑才将电脑内存或文件内容中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、汇编指令、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息……  十字链表交换任意两个节点C源代码（C指针应用终极挑战）http://download.csdn.net/detail/zhao4zhong1/5532495

user: user-6
content: 引用 3 楼 haolly 的回复:[quote=引用 1 楼 user-11 的回复:] 指针本身是一个值，放在栈上 指针可以指向一个地址，堆或栈，是内存或虚拟内存的一个部分，如果有权限，当然可以越界访问 其实我也有点晕。。。 #include <stdio.h> #include <stdlib.h> #include <string.h> void getMemory(char* p, int num) {     printf("in function before:%p\n",p);//0xb，这里的地址和函数外面的一样，说明这里的p和外面的p是一样的     char* pt = (char*)malloc(sizeof(char)* num);     p = pt;     printf("in function p:%p\n",p); //这里显示改变了啊 } int main() {     char* p = "a";     char* pp = "b";     printf("before p:%p\n",p);//0xa     printf("before pp:%p\n",pp);//0xb       p = (char*)malloc(100);     getMemory(pp,100);     printf("after p:%p\n",p);   //0xc      printf("after pp:%p\n",pp); //still 0xb， 但是这里又显示没有改变，怎么回事？       strcpy(p,"hello1\n");     printf(p);      return 0; }  [/quote]  真不太好解释，我姑且试试。  传指针本质上还是传值，传递的是内存的地址。getMemory里的p是形参，main函数里的pp是实参。p只不过是pp的一个拷贝。（这里可以将指针的值理解为内存地址，函数调用是将形参复制成实参） 我们可以通过修改*p来修改*pp，因为p和pp指向同一个地方。 但是这个p=pt，并不能改变pp。只不过将p指向另一个地方，pp还是指向原来的地方。 想要修改pp的话，必须传递指向pp的一个东西。也就是指针的指针。  我们两用同一茅坑，我拉屎了，你的茅坑也就有屎了，相当于修改*p。 p=pt相当于我换了个茅坑，跟你没啥关系啊。

user: user-7
content: char* p = "a"; 定义一个指针，&p是该指针的地址，p是该指针所指向的内容 p = (char*) malloc(100);改变了p所指向的内容，但是&p是没有变的，即p指针本身没有改变 getMemort(p,100); void getMemory(char* pp , int num) { // 这里的pp是外部p的一个值副本，&pp != &p,即他们不是同一个指针，只是他们所指向的内容一样(因为 pp = p, 可以通过输出地址看到 pp = (char*) malloc(100); // 将pp指向另一块内容 } //函数返回后，临时变量pp不复存在 p还是原来的p  -------------------------------------------------------------------------------------------------- 我是这么理解的，不知道对不对

user: user-8
content: 指针本身也是一个类型，也是作为传值作为参数的。 所以传值无法创建的。这样理解就OK了。 要把指针本身传入参数，只有传名和传地址。

user: user-9
content: char *str = NULL; str = (char*)malloc(sizeof(char)*100); for(int i = 0; i<200; ++i)    printf("%d  ",&str[i]);  printf("\n"); 你要去百度一下内存页 而且你是答应str的地址

user: user-10
content: 引用我在程序里面开辟100个char*型的空间，竟然可以打印200个空间，且每次都是+1，结果如下：                    那么剩下的100个地址空间到底是栈空间里面的还是堆空间里面的？        char *str = NULL; str = (char*)malloc(sizeof(char)*100); for(int i = 0; i<200; ++i)    printf("%d  ",&str[i]);  printf("\n")  1. printf("%d  ",&str[i]); 和 printf("%d  ",str+i); 等价， 这里只有地址偏移运算， 并没有真正读取str的内容。  2. 就算真正读取内容， 也是未定义行为。 这次可以，未必下次也可以。在一个系统上可以，未必在另一个系统也可以。 就看运气了。

user: user-7
content: 引用 1 楼 user-11 的回复:指针本身是一个值，放在栈上 指针可以指向一个地址，堆或栈，是内存或虚拟内存的一个部分，如果有权限，当然可以越界访问 其实我也有点晕。。。 #include <stdio.h> #include <stdlib.h> #include <string.h> void getMemory(char* p, int num) {     printf("in function before:%p\n",p);//0xb，这里的地址和函数外面的一样，说明这里的p和外面的p是一样的     char* pt = (char*)malloc(sizeof(char)* num);     p = pt;     printf("in function p:%p\n",p); //这里显示改变了啊 } int main() {     char* p = "a";     char* pp = "b";     printf("before p:%p\n",p);//0xa     printf("before pp:%p\n",pp);//0xb       p = (char*)malloc(100);     getMemory(pp,100);     printf("after p:%p\n",p);   //0xc      printf("after pp:%p\n",pp); //still 0xb， 但是这里又显示没有改变，怎么回事？       strcpy(p,"hello1\n");     printf(p);      return 0; }

user: user-5
content: VMMap 是进程虚拟和物理内存分析实用工具。http://technet.microsoft.com/zh-cn/sysinternals/dd535533

user: user-11
content: 指针本身是一个值，放在栈上 指针可以指向一个地址，堆或栈，是内存或虚拟内存的一个部分，如果有权限，当然可以越界访问