简体   繁体   English

在C中,为什么不能声明指针并使其直接指向数字,而不使用malloc?

[英]In C, why can't you declare a pointer and make it point to a number directly, without malloc?

In C, why do X and Y work but not Z? 在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;

In Z, I declare a pointer and try to make it point to a number, but I get a segmentation fault. 在Z中,我声明一个指针,并尝试使其指向一个数字,但我得到一个分段错误。 But it seems to me like I'm doing the same thing in Z as X and Y. In XI just use the variable num instead of the number 5 directly, and in YI just use the heap to hold the variables instead of the stack. 但在我看来,我在Z和X和Y做同样的事情。在XI中直接使用变量num而不是数字5,而在YI中只使用堆来保存变量而不是堆栈。 So how come X and Y work but Z doesn't? 那么X和Y怎么工作但是Z没有?

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

For the above, the int value "num" is allocated on stack, or in program data segment so it has an address. 对于上面的内容,int值“num”在堆栈或程序数据段中分配,因此它具有一个地址。 Lets pretend it was assigned by the compiler the address 0x12345678. 让我们假装它是由编译器分配的地址0x12345678。 You create an int* ptr. 你创建一个int * ptr。 This also has address lets say 0x20000000. 这也有地址让我们说0x20000000。 The address currently point to random data. 该地址当前指向随机数据。 We want to make the pointer at 0x20000000 point to the data value at 0x12345678, so that we can read the contents of 0x12345678 and get back the value 5... so we place 0x12345678 inside the storage space at 0x20000000 (we set ptr = &num). 我们想让0x20000000处的指针指向0x12345678处的数据值,这样我们就可以读取0x12345678的内容并返回值5 ...所以我们将0x12345678放在0x20000000的存储空间内(我们设置ptr =&num )。

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

For the second example, we only have 0x20000000 (myptr). 对于第二个示例,我们只有0x20000000(myptr)。 Its a pointer and it currently pointing nowhere or anywhere. 它是一个指针,它目前指向无处或任何地方。 When we do *myptr = 5, we look at the address stored at 0x20000000. 当我们执行* myptr = 5时,我们查看存储在0x20000000的地址。 Its random so it may be 0xffff0000 lets use that example. 它是随机的,所以它可能是0xffff0000让我们使用那个例子。 It will then try and write the value 5 to this address (0xffff0000) which does not exist and causes the segfault. 然后它会尝试将值5写入此地址(0xffff0000),该地址不存在并导致段错误。

So in your last example, the pointer exists, but it does not point anywhere valid, so when you try to write where it points, you either corrupt valid memory or cause a segment fault. 因此,在上一个示例中,指针存在,但它并未指向任何有效位置,因此当您尝试写入指向的位置时,您要么损坏有效内存,要么导致段错误。

You didn't specify what myptr is pointing to. 您没有指定myptr指向的内容。 You only declared it as a pointer (ie variable used to point somewhere), but you didn't initialize it. 您只将它声明为指针(即用于指向某处的变量),但您没有初始化它。 In X and Y you indeed do initialize the pointers: 在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 */

In Z there's only the declaration: int* myptr . 在Z中只有声明: int* myptr You're attempting to de-reference the pointer in the following line, ie *myptr = 5; 您试图在以下行中取消引用指针,即*myptr = 5; . This leads to a seg-fault as myptr hasn't been initialized yet. 由于myptr尚未初始化,因此会导致seg-fault。

Edit 编辑

Note that, as pointed out by David Hoelzer, when you call malloc you probably wanted to write malloc(sizeof(int)) rather than malloc(sizeof(int*)) . 请注意,正如David Hoelzer所指出的,当您调用malloc时,您可能想要编写malloc(sizeof(int))而不是malloc(sizeof(int*))

Pointers must point to memory locations. 指针必须指向内存位置。 (Unless of course the pointer is null or invalid, but in that case you cannot write through it anyway). (当然,除非指针为空或无效,但在这种情况下,无论如何都无法通过它写入)。

The expression 5 doesn't indicate a memory location. 表达式5不表示存储器位置。 There isn't some memory sitting out there with 5 in it waiting to be pointed at. 没有一些记忆坐在那里,其中有5等待被指出。 We say that 5 is a value (or sometimes r-value ) for this reason. 因此,我们说5是一个 (或有时是r值 )。

Expressions that do indicate memory locations are called l-values . 表达式确实显示内存位置被称为左值 (You can think of the l as standing for location ). (您可以将l视为代表位置 )。 If you want to point to a location that contains 5 , you will have to include some code that reserves an area of memory. 如果要指向包含5的位置,则必须包含一些保留内存区域的代码。 This code will use an l-value to refer to that memory location. 此代码将使用l值来引用该内存位置。 To be clear, the term l-value means the expression, not the memory location. 要清楚,术语l值表示表达式,而不是内存位置。

In your code: 在你的代码中:

  • X: int num = 5; X: int num = 5; reserves a location, names it num , and stores 5 in it. 保留一个位置,将其命名为num ,并在其中存储5 The l-value is num . l值是num
  • Y: malloc(sizeof(int) (sic) reserves a location, and *mptr = 5; stores a value in it. The l-value is *mptr . Y: malloc(sizeof(int) (sic)保留一个位置, *mptr = 5;在其中存储一个值*mptr值为*mptr
  • Z: *myptr is an l-value, however it does not designate a memory location because you have not made myptr point to any memory. Z: *myptr是一个l值,但它没有指定一个内存位置,因为你没有将myptr指向任何内存。 So this code compiles but it causes undefined behaviour: an l-value must designate a valid memory location at the point it is evaluated. 因此,此代码编译但会导致未定义的行为:l值必须在评估它时指定有效的内存位置。

NB. NB。 l-values and r-values are usually explained poorly by tutorials so be careful when googling. l值和r值通常通过教程解释得很差,所以在谷歌搜索时要小心。 (I couldn't find any good pages that were C-only). (我找不到任何只有C的好页面)。

Pointers are variables to hold memory address of other spaces.When a pointer is declared, it does not have any memory, it has to be explicitly initialized by assigning it to some already existing variable or memory be allocated by using the malloc() C library function. 指针是保存其他空间的内存地址的变量。当声明指针时,它没有任何内存,必须通过将其分配给某些已存在的变量或使用malloc() C库分配的内存来显式初始化它。功能。

In your X case, ptr is made to point to the address of num , and hence it reflects the value of num once deferenced. 在你的X情况下,ptr被指向num的地址,因此它反映了一旦被引用的num的值。

Similarly in case Y, memory is allocated explicitly and the pointer mptr is made to point to it using 类似地,在Y的情况下,显式地分配存储器并且使用指针mptr指向它

int *mptr = (int *)malloc(sizeof(int *)); 

But in the Z case, the pointer is just declared, but it is not assigned to or allocated any memory location. 但在Z情况下,指针只是声明,但它没有分配或分配任何内存位置。 So the pointer points to some wild location which is beyond the scope of the executing program, and hence ends up giving segmentation fault, which generally means out of scope memory access. 因此指针指向一些超出执行程序范围的野性位置,因此最终会产生分段错误,这通常意味着超出范围内存访问。

You have to assign a variable a value before you use that value. 在使用该值之前,必须为变量赋值。 In Z, you are using the value of myptr before you assign it one. 在Z中,在分配之前使用myptr的值。 Neither of your other two examples do that. 你的另外两个例子都没有这样做。

//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
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

What's stopping you isn't necessarily the C language, but rather your operating system. 什么阻止你不一定是C语言,而是你的操作系统。

  • In case X, your memory requirements can be deduced from your code and memory is allocated before your program is executed. 在X的情况下,您的内存要求可以从您的代码中推断出来,并且在执行程序之前会分配内存。
  • In case Y, you request memory to be dynamically allocated and the operating system attempts to fulfill that request. 如果是Y,则请求动态分配内存,操作系统会尝试满足该请求。
  • In case Z, the memory requirements cannot be deduced from the code, nor is there a request made to the operating system to dynamically allocate memory; 在Z的情况下,不能从代码中推断出内存需求,也没有向操作系统请求动态分配内存; thus, no memory has been allocated: you get a segmentation fault attempting to deference the unallocated memory. 因此,没有分配任何内存:您尝试分离未分配的内存时出现分段错误。

The segmentation fault is issued by the operating system protecting you from dereferencing unallocated memory; 分段故障由操作系统发出,保护您免取消分配未分配的内存; it's a safety feature in modern operating systems designed to prevent your program from clobbering data being used elsewhere in the system. 它是现代操作系统中的一项安全功能,旨在防止您的程序破坏系统中其他地方使用的数据。

In case Z, your pointer may be set to a random address. 在Z的情况下,您的指针可以设置为随机地址。 If you deference this address and store a 5 at that location, you may be overwriting another program's data in memory. 如果你尊重这个地址并在那个位置存储5 ,你可能会在内存中覆盖另一个程序的数据。

Imagine the chaos that would arise from programs interfering with each other's data in memory: you couldn't trust your variables anymore since another program could have messed with them, or worse -- a small bug in your program could overwrite your operating system in memory and crash your whole machine; 想象一下程序会因为程序干扰内存中的数据而产生的混乱:你不能再相信你的变量,因为另一个程序可能会搞乱它们,或者更糟 - 程序中的一个小错误可能会覆盖你内存中的操作系统并使整个机器崩溃; this is why the modern operating system prevents you from doing that. 这就是现代操作系统阻止您这样做的原因。

If you were programming on the bare metal (ie no operating system), you could deference all the random memory addresses you desire since nothing would be around to stop you from shooting yourself in the foot. 如果你是在裸机上编程(即没有操作系统),你可以尊重你想要的所有随机存储器地址,因为没有什么可以阻止你在脚下拍摄自己。

In fact, syntaxically speaking you can do it by using explicit cast: 事实上,从语法上讲,你可以通过使用显式转换来实现:

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

So it can be (dangerously) used as an int but if you try to dereference it you will face a segmentation fault since myptr point at address 0x5 that is invalid . 所以它可以(危险地)用作int,但如果你试图取消引用它,你将面临一个分段错误,因为myptr指向地址0x5是无效的

  1. Pointers are called "pointers" for a reason. 由于某种原因,指针被称为“指针”。 Data pointers can only point to lvalues , ie to objects that have a location in storage (in memory). 数据指针只能指向左值 ,即指向存储位置(在内存中)的对象。

    "A number" does not have location in memory. “数字”在内存中没有位置。 "A number" is not an lvalue. “数字”不是左值。 It is not possible to point to something that does not have a location. 不可能指向没有位置的东西。

  2. Your example labeled Z does not even remotely look like an attempt to make a pointer to point to a number. 标记为Z的示例甚至看起来并不像尝试使指针指向数字。 Your examples labeled X and Y clearly show that in order to make a pointer ptr to point somewhere, you have to assign a value directly to ptr , not to *ptr . 标记为X和Y的示例清楚地表明,为了使指针ptr指向某处,您必须直接将值赋给ptr ,而不是*ptr In you Z example you never assign any value to myptr . 在你Z的例子中,你永远不会给myptr

Your failing code 你失败的代码

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

does the following (assuming 32-bit addresses and 32-bit int ): 执行以下操作(假设32位地址和32位int ):

  1. int* myptr; declare myptr to be a pointer to int. 声明myptr是一个指向int的指针。 Which is to say that it holds the address of an int . 也就是说它保存了int地址 Since you have not initialized myptr , myptr holds an unidentified value. 由于您尚未初始化 myptr ,因此myptr保留了一个未识别的值。 It could be the absolute address 0x00000000 or some seemingly random value (the bits from whatever last occupied that location in memory most likely). 它可能是绝对地址0x00000000或一些看似随机的值(来自最后一次占用内存中位置的最可能的位)。

  2. *myptr = 5; is an instruction to *save the integer value 5 (0x00000005) in the 4 contiguous bytes of memory located at the address contained in myptr . 是一个指令*将整数值5 (0x00000005)保存在位于myptr包含的地址的4个连续字节的内存中。 Whether or not you can write to that location in memory is entirely dependent on what that address is . 是否可以在内存中写入该位置完全取决于该地址是什么 If you are lucky, your program will fault and die. 如果运气好的话,你的程序就会出错并死掉。 If you are unlucky, it will corrupt memory by tromp on something that matters (your call stack linkage, perhaps, or something worse). 如果你运气不好,那么就会因为某些重要的东西(你的调用堆栈链接,或者更糟糕的东西)会破坏内存。

Then, your program will run just fine...until it crashes in strange and mysterious ways. 然后,你的程序运行得很好......直到它以奇怪和神秘的方式崩溃。 And you will rip your hair out trying to figure out what's going on. 你会扯掉你的头发试图找出发生了什么。 Don't ask me how I know this. 不要问我怎么知道这个。

Now, you could say something like 现在,你可以这样说

myptr = 5 ;

which does something different: it assigns myptr the absolute address 0x00000005. 它做了不同的事情:它为myptr分配绝对地址0x00000005。 Then if you say something like: 如果你说的话:

*myptr = 3*5 ;

It will try to store the integer value 15 (0x0000000F) in the 4 bytes located at absolute address 0x00000005. 它将尝试在位于绝对地址0x00000005的4个字节中存储整数值15(0x0000000F)。 It will still (almost certainly) crash, but at least it will do so in a consistent and reproducible way. 它仍然(几乎可以肯定)崩溃,但至少它会以一致且可重复的方式实现。

For the sake of argument, my example addresses are assuming a 32-bit address space. 为了便于论证,我的示例地址假定为32位地址空间。

You cant do that since that in this declaration, Z is only a pointer and does not hold any memory. 你不能这样做,因为在这个声明中,Z只是一个指针而且没有任何记忆。 you need to "point" with the Z to a new memory 你需要用Z“指向”一个新的记忆

you can do that in 3 ways: 你可以用3种方式做到这一点:

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

myptr = 5;

OR 要么

int* myptr = new int;

OR 要么

int A;
int* myptr = A;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM