月华剑士1高手视频:关于指针变量在内存中所在的长度(转载)

来源:百度文库 编辑:偶看新闻 时间:2024/05/03 08:09:31

关于指针变量在内存中所在的长度(转载)

作者:又是一年夏天 | 出处:博客园 | 2011/6/24 14:27:15 | 阅读9次

32系统指针变量是4byte,我在xp下用vc6.0测试出4byte,tc是2byte为什么?

不同的计算机是不一样的. 
32位的是4个字节,也就是现在用的最多的(64位快了...),16位就是2个字节. 
虽然是16位,不过他的寻址方式不只是用指针,系统还要配合段继存器(16位系统当然也是16的的继存器了)来寻址,合起来也就是4个字节了. 

现在的32位跟16的差不多,不过由于WINDOWS系统所有的数据段都使用一个地址(人家可能认为32位够用了吧),就不会涉及到段继存器了. 

当然象”zz003pm”说的地址转换的问题我还不太清楚,可能是由硬件实现的,不记得了.

因为tc是16位的编译器,最早就是运行在dos下的,在16位机上int都是2位的,因为指针变量的   sizeof   大小是系统相关的。一般就是4了,因为现在一般都是32   位的系统

指针大小应等同于地址总线的宽度

深入浅出 C语言指针 一

指针是什么? 是地址。这个大家都知道。但是理解得未必深刻。我们以一条条活生生的代码来分析。

   int *pointer;
   
声明一个指针。 pointer是一个指向Int型的指针的变量。
而变量里面的值是一个内存地址(在大部分操作系统中不是物理地址,是经过地址映射后的虚拟地址,即逻辑地址)。
而这个pointer只在程序文件(.c,.cpp)里存在,当编译成可执行文件后,它被替换成一个难看的符号,比如LLC012什么的。这点与java相反,所以C程序是没有办法反编译的。
当运行一个C程序时,操作系统分配给LLC012一块32位大小内存地址,以后你改变指针的值时,改变的就是这块内存地址的值了。

 

菜鸟问与答(指针)

 1、什么是指针

  指针是一种数据类型,与其它的数据类型不同的是指针是一种“用来存放地址值的”变量。举一个简单的例子:

如果定义了一个整型变量,根据整型变量的特点,它可以存放的数是整数。

如:int a; a=100; 这样就把整型常量赋给了变量a。但是如果写成这样:a=123.33;就会出问题,最后输出变量a的值结果是123。现在说到指针,其实地址值也是一个整型数,如某某变量的地址值为36542,说明这个变量被分配在内存地址值为36542的地方。能不能这样进行推理,既然地址值也是整型数,整型变量正好可以用来存放整型数,那不是一个整型变量可以用来存放地址的值吗。程序写成下面这样:

  int a,b;

    a=&b;

很明显,这样写是错误的。原因在于不能简单地把地址理解为整型数。

应有这样的对应关系: 地址值<--->指针;  整型数<--->int 型变量。

所以有这样的说法:“指针就是地址”(指针就是存放地址值的一种数据类型)

  下面是一段正确的程序:

  int a,*p;

    p=&a;     /*把变量a的地址值赋给指针p*/

    2、什么是void指针

  void的意思就是“无值”或“无类型”。void指针一般称为“通用指针”或“泛指针”。之所以有这样的名字是因为使用void指针可以很容易地把void指针转换成其它数据类型的指针。例如在为一个指针分配内存空间的时候:

    int *p;

    p=(int *)malloc(......);  本来函数malloc的返回值是void类型,在这里通过在前面加上一个带括号的int*就把void*类型转换成了int*类型。

  所以不能简单的把void看成“无”的意思。void数据类型是一种很重要的数据类型。

  3、指针可以相加减吗

  可以相互加减。但是一定要作有意义的运算。当二个指针指向同一个数组的时候,它们相加减是有意义的。如果二个指针分别指向二个不同的数组,那么指针之间的相加减就没有什么意义。指向同一个数组时,其相加减的结果为二个指针之间的元素数目。

  4、什么是NULL指针

  NULL指针是不指向任何一个地址的指针。这样的指针一般是允许的。当一个指针为NULL的时候,不要对它进行存取。

  5、什么是“野”指针

  野指针是不由程序员或操作者所能控制的指针。当在一个程序里面定义了一个指针而又没有给这个指针一个具体地址指向的时候,这个指针会随意地指向一个地址,这样的指针就是一个野指针。如果这个地址后面的内存空间没有什么重要的数据则不会造成不好的后果,但是一旦这里面存放了有用的数据,那么这些数据随时都有被野指针存取的危险,如果这样,数据就会被破坏,程序也会崩溃。所以在程序里面是一定要禁止任何野指针的存在。当定义了一个指针的时候,要马上给这个指针分配一个内存地址的指向。这样程序才不会因为指针而出现意外。

  6、NULL的值是什么

  NULL不是被定义为0就是被定义成(void *)0,这二种值基本上是一样的。

  如有这样的语句: if(p==NULL) 或者写成 if(p==0) 其作用是一样。

    7、什么是“内存泄漏”

  当定义了一个指针的时候,立即要为这个指针分配一个内存空间。这只防止了野指针的产生。当一个指针使用完毕要立即释放掉这个指针所占用的内存空间---这有二方面的意义:  1)避免了内存空间的泿费; 2)防止了内存泄漏。为什么会产生内存泄漏:如果没有及时释放掉指针所占用的内存空间,而在下次使用这个指针时又给这个指针分配了内存空间,这样的次数一多,内存空间就慢慢被消耗掉了。所以形象地称这种现象为内存泄漏。

  如下面这样一个程序:

  void *p;

    for(;;)

       p=malloc(20);      /*这20个字节的内存空间是随意指定的*/

这样的一个小程序,大家不要随便运行它。你可以在集成环境中单步调试运行,可以看一下每步运行后的结果。可以看到,每一次循环都会“吃掉”20个字节的内存,无数次之后,再多的内存也慢慢地“泄漏”,最后没有内存可用就死机。(与这个程序配合需要一段检测整机总的内存容量的程序,以观察内存总量的变化。这里虽然没有这一段程序,但是看得到每次分配的内存地址值是不相同的)

    8、near指针和far指针

在DOS下(实模式)地址是分段的,每一段的长度为64K字节,刚好是16位(二进制的十六位)。

near指针的长度是16位的,所以可指向的地址范围是64K字节,通常说near指针的寻址范围是64K。

far指针的长度是32位,含有一个16位的基地址和16位的偏移量,将基地址乘以16后再与偏移量相加,(所以实际上far指针是20位的长度。)即可得到far指针的1M字节的偏移量。所以far指针的寻址范围是1M字节,超过了一个段64K的容量。例如一个far指针的段地址为0x7000,偏移量为0x1244,则该指针指向地址0x71224.如果一个far指针的段地址是0x7122,偏移量为0x0004,则该指针也指向地址0x71224。

    如果没有指定一个指针是near或far,那么默认是near。所以far指针要显式指定。far指针工作起来要慢一些,因为每次访问一个far指针时,都要将数据段或程序段的数据交换出来。另外,far指针的运算也比较反常,例如上面讲到的far指针指向同一个地址,但是比较的结果却不相同。

    9、什么时候使用far指针

当使用小代码或小数据存储模式时,不能编译一个有很多代码或数据的程序。因为在64K的一个段中,不能放下所有的代码与数据。为了解决这个问题,需要指定以far函数或far指针来使用这部分的空间(64K以外的空间)。许多库函数就是显式地指定为far函数的形式。far指针通常和farmalloc()这样的内存分配函数一起使用。 

 int 是指针的类型,刚才说过,指针变量占用的内存空间永远是32位,相当于一个int的大小。但是,指针是有类型的,它不是用来给指针变量本身分配内存,它是用来做什么的呢? 它是用来寻址 的。看下面的代码。

 

(int*)pointer = malloc(sizeof(int) * 5);
printf("%d",*(pointer + 1));

这段代码中先申请5个int大小的内存空间,把这段内存空间的首地址付给pointer。然后打印出第2个int元素的值。
如果这里的pointer是char类型的,那么pointer+1指向的不是第2个Int,而是第一个int的中间部分。也就是说对于不同变量类型的指针来说,+1跨过的内存单元数是不同的。指针的类型也可以是自定义的结构的类型。
    与java不同,C语言的函数是不允许以引用的方式传值的,也就是说对于所有的C函数,传入的参数都是一个复制的值。
    比如这样一个交换数值的函数
void switch(int a ,int b)
{
        int c =a;
        a = b;
        b = c;
}
这个函数调用后实际上并没有交换两个参数的值,因为是传值的,而非传地址(引用)的。
 那么我们如何实现累类似引用传值的功能呢? 答案是,用指针。 
void switch(int* a ,int* b)
{
        int c =*a;
        *a = *b;
        *b = c;
}  
从本质上说指针也是被复制后传到函数体内的。
我们以上面这个函数为例分析一下
函数一 :  当调用 switch(intA,intB)时
假设   :
        intA  地址  0X0000000A  值 1
        intB  地址  0X0000000B  值 2
经过复制之后实际上传入函数的是另外两个。
        a  地址  0X000000AA  值 1
        b  地址  0X000000BB  值 2
当函数执行后,intA,intB的值没变,他们从头到尾都没进入函数,进入的是他们的副本。变的是a , b的值(如下),但是函数执行结束后a , b都已经被丢弃了。没有达到预期目的。
        a  地址  0X000000AA  值 2
        b  地址  0X000000BB  值 1

函数二 :  当调用 switch(*intA,*intB)时
假设   :
        intA  地址  0X0000000A  值 0X00000A0A
        intB  地址  0X0000000B  值 0X00000B0B

              地址 0X00000A0A   值 1
              地址 0X00000B0B   值 2
当函数执行时,先复制参数,结果如下
        a  地址  0X000000AA  值 0X00000A0A
        b  地址  0X000000BB  值 0X00000B0B
  而地址0X00000A0A 和 0X00000B0A 的值没有变化
传入函数后用的都是指针变量的值,也就是说改变的是0X00000A0A 和0X00000B0B的值,而指针intA和intB的值没有变化,始终指向0X00000A0A 和0X00000B0B 所以最终达到交换数值的预期效果。

问:请问 在C中 (TC3.0)一个指针变量 在内存中占几个字节?那 如果 指针变量只占 2字节(tc3.0) 
那它能指向的内存范围是0-65535以内的地址 那其他的地址岂不是无法指到?

答:呵呵,指针指向的是逻辑地址。当构成实际地址的时候指针还要跟一定的段地址寄存器进行运算才能得出真实地地址。这些都是操作系统做的,你不需要关心这个。

// zd_20.cpp : Defines the entry point for the console application. 
// 

#include "stdafx.h" 

int main(int argc, char* argv[]) 

int *p; 
char *c; 
long int *q; 
float *f; 
printf("%d\n",sizeof(p)); 
printf("%d\n",sizeof(c)); 
printf("%d\n",sizeof(q)); 
printf("%d\n",sizeof(f)); 
printf("Hello World!\n"); 
return 0; 

VC下输出为 




TC下输出为 





原因: 
一般来说 
指针长度应该与OS的位数有关 
32位操作系统应该为4个字节 
以后64位操作系统应该为8个字节 
但在编译环境中 
指针长度是由编译环境来设置的 
由于TC是DOS时代的产物 
所以它还是2个字节 
VC自然是4个字节