在C中,为什么你不能声明一个指针并让它直接指向一个数字而没有malloc?

时间:2015-06-01 18:55:19

标签: c pointers segmentation-fault

在C中,为什么X和Y工作但不是Z?

//X     
int num = 5;
int *ptr;
ptr = #

//Y
int *mptr = (int *)malloc(sizeof(int *));       
*mptr = 5;       

//Z
int* myptr;
*myptr = 5;

在Z中,我声明了一个指针并尝试使其指向一个数字,但是我得到了一个分段错误。但在我看来,我在Z和X和Y做同样的事情。在XI中直接使用变量num而不是数字5,而在YI中只使用堆来保存变量而不是堆。那么为什么X和Y会起作用,但Z不会呢?

11 个答案:

答案 0 :(得分:5)

//X     
int num = 5;
int *ptr;
ptr = #

对于上面的内容,int值“num”在堆栈或程序数据段中分配,因此它有一个地址。让我们假装它是由编译器分配的地址0x12345678。你创建一个int * ptr。这也有地址让我们说0x20000000。该地址当前指向随机数据。我们想让指针在0x20000000处指向0x12345678处的数据值,这样我们就可以读取0x12345678的内容并返回值5 ...所以我们将0x12345678放在0x20000000的存储空间内(我们设置ptr =& ; NUM)

//Z
int* myptr;
*myptr = 5;

对于第二个例子,我们只有0x20000000(myptr)。它是一个指针,它目前指向无处或任何地方。当我们执行* myptr = 5时,我们查看存储在0x20000000的地址。它是随机的,所以它可能是0xffff0000让我们使用那个例子。然后它会尝试将值5写入此地址(0xffff0000),该地址不存在并导致段错误。

所以在你的上一个例子中,指针存在,但它没有指向任何有效的位置,因此当你尝试写入指向的位置时,你要么破坏有效的内存,要么导致段错误。

答案 1 :(得分:2)

您没有指定myptr指向的内容。您只将它声明为指针(即用于指向某处的变量),但您没有初始化它。在X和Y中你确实初始化了指针:

//X     
int num = 5;
int *ptr;       /* Declaration */
ptr = #     /* Initialization */

//Y
int *mptr = (int *)malloc(sizeof(int));   /* Declaration + Initialization*/ 
*mptr = 5;      /* De-referencing*/ 

//Z
int* myptr;  /* Declaration only */
*myptr = 5;  /* De-referencing before initialization */

在Z中只有声明:int* myptr。您试图取消引用以下行中的指针,即*myptr = 5;。这会导致seg-fault,因为myptr尚未初始化。

修改

请注意,正如David Hoelzer所指出的,当你调用malloc时,你可能想写malloc(sizeof(int))而不是malloc(sizeof(int*))

答案 2 :(得分:2)

指针必须指向内存位置。 (当然,除非指针为空或无效,但在这种情况下,无论如何都无法通过它写入。)

表达式5不表示内存位置。没有一些记忆坐在那里,5等待被指出。出于这个原因,我们说5(或者有时是 r-value )。

执行的表达式表示内存位置称为 l-values 。 (您可以将 l 视为代表位置)。如果要指向包含5的位置,则必须包含一些保留内存区域的代码。此代码将使用l值来引用该内存位置。需要明确的是,术语 l-value 表示表达式,而不是内存位置。

在您的代码中:

  • X:int num = 5;保留一个位置,将其命名为num,并将5存储在其中。 l值为num
  • Y:malloc(sizeof(int) (原文如此)保留一个位置,*mptr = 5;在其中存储一个值。 l值为*mptr
  • Z:*myptr是一个l值,但是它没有指定一个内存位置,因为你没有将myptr指向任何内存。所以这段代码会编译,但会导致未定义的行为:l值必须在评估它时指定一个有效的内存位置。

NB。 l值和r值通常通过教程解释得很差,所以在谷歌搜索时要小心。 (我找不到任何只有C的好页面。)

答案 3 :(得分:1)

指针是保存其他空间的内存地址的变量。当声明指针时,它没有任何内存,必须通过将其分配给某些已经存在的变量或使用{{分配的内存来显式初始化它。 1}} C库函数。

在你的X情况下,ptr被指向malloc()的地址,因此它反映了一次被引用的num的值。

同样在Y的情况下, 显式分配内存,并使用

指针num指向它
mptr

但在Z情况下,指针只是声明,但它没有分配给或分配任何内存位置。因此指针指向某个超出执行程序范围的疯狂位置,因此最终会产生分段错误,这通常意味着超出范围内存访问。

答案 4 :(得分:0)

在使用该值之前,必须为变量赋值。在Z中,在分配之前使用myptr的值。你的另外两个例子都没有这样做。

//X     
int num = 5; // Assign num a value
int *ptr;
ptr = # // Assign ptr a value

//Y
int *mptr = (int *)malloc(sizeof(int *));     // Assign mptr a value
*mptr = 5;       // Use the value

//Z
int* myptr; // Doesn't assign myptr a value
*myptr = 5; // Uses myptr's value

答案 5 :(得分:0)

int num = 5; // name a memory location "num" and store 5 in it
int *ptr;    // name a memory location "ptr"
ptr = #  // store address of "num" in "ptr"

//Y
int *mptr = (int *)malloc(sizeof(int *)); // name a memory location mptr
// then (wrongly, should have been sizeof(int)) allocate enough memory to store
// a pointer and store the address of that allocated memory in "mptr"
*mptr = 5;  // write value 5 in that anonymous memory location allocated by malloc
// that mptr is pointing at

//Z
int* myptr; // name a memory location "myptr", don't write anything to it
*myptr = 5; // try to write value of 5 into memory pointed by myptr -- 
//except myptr is not pointed anywhere yet, get undefined behavior

答案 6 :(得分:0)

什么阻止你不一定是C语言,而是你的操作系统。

  • 如果是X,您的内存要求可以从您的代码中推断出来,并且在执行程序之前会分配内存。
  • 如果是Y,则请求动态分配内存,操作系统会尝试满足该请求。
  • 在Z情况下,无法从代码中推断出内存需求,也没有向操作系统请求动态分配内存;因此,没有分配任何内存:您在尝试依赖未分配的内存时遇到分段错误。

分段错误由操作系统发出,保护您免取消分配未分配的内存;它是现代操作系统中的一项安全功能,旨在防止您的程序破坏系统中其他地方使用的数据。

如果是Z,您的指针可能会被设置为随机地址。如果你尊重这个地址并在那个位置存储5,你可能会在内存中覆盖另一个程序的数据。

想象一下程序会因为程序干扰内存中的数据而产生的混乱:你不能再相信你的变量,因为另一个程序可能会搞乱它们,或者更糟糕的是 - 程序中的一个小错误会覆盖你的操作系统在内存中崩溃你的整个机器;这就是现代操作系统阻止你这样做的原因。

如果您使用裸机进行编程(即没有操作系统),您可以使用您想要的所有随机存储器地址,因为没有什么可以阻止您在脚下拍摄自己。

答案 7 :(得分:0)

事实上,从语法上讲,你可以通过使用显式强制转换来实现:

//Z
int* myptr;
myptr = (void *)5;

所以它可以(危险地)用作int但是如果你尝试取消引用它,你将面临分段错误,因为 myptr 指向地址 0x5无效

答案 8 :(得分:0)

  1. 由于某种原因,指针被称为“指针”。数据指针只能指向 lvalues ,即指向存储位置(在内存中)的对象。

    “数字”在内存中没有位置。 “数字”不是左值。不可能指向没有位置的东西。

  2. 标记为Z的示例甚至看起来并不像尝试使指针指向数字。标记为X和Y的示例清楚地表明,为了使指针ptr指向某处,您必须直接将值分配给ptr,而不是*ptr。在您的Z示例中,您永远不会将任何值分配给myptr

答案 9 :(得分:0)

您的失败代码

//Z
int* myptr;
*myptr = 5;

执行以下操作(假设32位地址和32位int):

  1. int* myptr;声明myptr是指向int的指针。也就是说拥有int 地址。由于您尚未初始化 myptr,因此myptr包含未识别的值。它可能是绝对地址0x00000000或一些看似随机的值(最可能在内存中最后占用该位置的位)。

  2. *myptr = 5;是*在5中包含的地址的4个连续字节的内存中保存整数值myptr(0x00000005)的指令。您是否可以到内存中的该位置完全取决于该地址。如果运气好的话,你的程序就会出错并死掉。如果你运气不好,它会在一些重要的东西(你的调用堆栈链接,或者更坏的东西)上破坏内存。

  3. 然后,你的程序运行得很好......直到它以奇怪和神秘的方式崩溃。你会扯掉你的头发试图找出发生了什么。不要问我怎么知道这个。

    现在,你可以说出类似

    的内容
    myptr = 5 ;
    

    执行不同的操作:它为myptr分配绝对地址0x00000005。如果你说的话如下:

    *myptr = 3*5 ;
    

    它将尝试在位于绝对地址0x00000005的4个字节中存储整数值15(0x0000000F)。它仍将(几乎可以肯定)崩溃,但至少它会以一致且可重复的方式实现。

    为了参数起见,我的示例地址假定为32位地址空间。

答案 10 :(得分:-1)

你不能这样做,因为在这个声明中,Z只是一个指针而且没有任何记忆。 你需要用Z“指向”一个新的记忆

你可以用3种方式做到这一点:

input

OR

int* myptr;
myptr(int *)malloc(sizeof(int));

myptr = 5;

OR

int* myptr = new int;