【问题标题】:Pointer initialisation gives segmentation fault指针初始化导致分段错误
【发布时间】:2013-07-26 06:19:29
【问题描述】:

我写了一个C程序如下:

案例 1

int *a; /* pointer variable declaration */

int b; /* actual variable declaration */

*a=11;

a=&b;/* store address of b in pointer variable*/

运行程序时出现分段错误。

我把代码改成如下:

案例 2

int *a; /* pointer variable declaration */

int b; /* actual variable declaration */

a=&b;/* store address of b in pointer variable*/

*a=11;

现在一切正常。

如果有人知道,请解释为什么它在 CASE 1 中给出了分段错误。

【问题讨论】:

    标签: c pointers segmentation-fault variable-assignment


    【解决方案1】:
    CASE .1
    
    int *a; /* pointer variable declaration */
    
    int b; /* actual variable declaration */
    
    *a=11;//Not valid means you are not owner of the address where a now pointing it is unknown and accessing this will segfault/
    
    a=&b;/* store address of b in pointer variable*/
    

    这将是分段错误,因为您使用的地址不是有效地址,并且您存储的 11 是非法的。

                                   b
          +-------+            +--------+
          |       +            |   11   |
          |Unknown|            |        |
          +---+---+            +---+----+
              |                    |
              |                    |
              +                    +
              a                    a
    CASE .2
    
    int *a; /* pointer variable declaration */
    
    int b; /* actual variable declaration */
    
    a=&b;/* store address of b in pointer variable*/
    
    *a=11;
    

    现在它可以正常工作了,因为 b 的地址是有效的,而你存储的 11 是合法的。

    上述情况也不是正确的指针声明方式

      int *a  = NUll;
      a = malloc(sizeof(int));
      *a=5;
      free(a);//must
    

     int *a  = NUll;
     int b;
     a = &b;
     *a=5;
    

    这将消除多次难以找到的分段错误。

    【讨论】:

      【解决方案2】:
      int *a; // a pointer variable that can hold a memory address of a integer value.
      

      在情况 1 中,

        *a = 10; // here you have asigned 10 to unknown memory address;
      

      它显示分段错误,因为将值分配给未定义的内存地址。 未定义的行为。

      在情况 2 中,

      a=&b; // assigning a proper memory address to a.
      *a=11;// assigning value to that address
      

      考虑这个例子:

      #include<stdio.h>
      int main()
      {
         int *a,b=10;
         printf("\n%d",b);
         a=&b;
         *a=100;
         printf("-->%d",b);
      }
      
      Output: 10-->100
      

      这就是它的工作原理。

               b        // name
           ----------
           +   10   +   // value
           ---------- 
              4000      // address 
      

      假设b的内存位置是4000。

      a=&b => a=4000;
      *a=100 => *(4000)=100 => valueat(4000) => 100
      

      操作后是这样的。

               b        // name
           ----------
           +  100   +   // value
           ---------- 
              4000      // address 
      

      【讨论】:

        【解决方案3】:

        一行:第一个代码您取消引用未初始化的指针,该指针表现出未定义的行为,在第二个代码中您取消引用初始化的指针,该指针将访问地址处的值。

        一点解释:

        首先你需要意识到指针只不过是一个整数,通过*var我们告诉编译器我们将使用变量var(其中的整数)的内容作为地址获取该地址中的值。如果有**var,类似地我们告诉编译器我们将首先使用变量var的存储值来获取地址处的值,然后再次使用这个获取的值作为地址并获取存储在其中的值。

        因此在您的第一个声明中是:

                   +----------+        +----------+
                   |  garbage |        |  garbage |
                   +----------+        +----------+
                   |    a     |        |    b     |
                   +----------+        +----------+
                   |  addr1   |        |  addr2   |
                   +----------+        +----------+
        

        然后您尝试使用存储在a 中的值作为地址。 a 包含垃圾,它可以是任何值,但您无权访问任何地址位置。因此,下一次您执行*a 时,它将使用a 中存储的值作为地址。因为存储的值可以是任何东西,所以任何事情都可能发生。

        如果您有权访问该位置,则代码将继续执行而不会出现分段错误。如果地址恰好是堆簿记结构中的地址,或者您的代码从堆或堆栈中分配的其他内存区域,那么当您执行*a = 10 时,它将简单地用10 擦除该位置的现有值.这可能会导致未定义的行为,因为现在您在不知道上下文的情况下更改了具有实际内存权限的内容。如果您没有访问内存的权限,您只会遇到分段错误。这称为取消引用未初始化的指针。

        下一个语句a = &amp;b 只是在a 中分配b 的地址。这无济于事,因为上一行取消了对未初始化指针的引用。

        接下来的代码你在第三条语句之后有这样的东西:

                   +----------+        +----------+
                   |  addr2   |---+    |  garbage |
                   +----------+   |    +----------+
                   |    a     |   +--> |    b     |
                   +----------+        +----------+
                   |  addr1   |        |  addr2   |
                   +----------+        +----------+
        

        第三条语句将b的地址分配给a。在此之前a 没有被取消引用,因此在初始化之前存储在a 中的垃圾值永远不会用作地址。现在,当您将您的知识的有效地址分配给 a 时,取消引用 a 现在将使您能够访问由 a 指向的值。

        扩展答案,您需要注意,即使您已为指针分配了有效地址,您也必须确保在取消引用指针时,指针指向的地址的生命周期具有没有过期。例如返回局部变量。

        int foo (void)
        {
          int a = 50;
          return &a;  //Address is valid
        }//After this `a' is destroyed (lifetime finishes), accessing this address
         //results in undefined behaviour
        
        int main (void)
        {
          int *v = foo ();
          *v = 50; //Incorrect, contents of `v' has expired lifetime.
          return 0;
        }
        

        在从堆访问释放的内存位置的情况下也是如此。

        int main (void)
        {
          char *a = malloc (1);
          *a = 'A'; //Works fine, because we have allocated memory
          free (a); //Freeing allocated memory
          *a = 'B'; //Undefined behaviour, we have already freed 
                    //memory, it's not for us now.
          return 0;
        }
        

        【讨论】:

          【解决方案4】:

          在第一种情况下,您已经声明了一个指针,但您没有分配它必须指向的地址,因此该指针将包含一个属于系统中另一个进程的地址(或者它会包含一个垃圾值根本不是地址,或者它包含一个不能是内存地址的空值)因此操作系统会发送一个信号来防止无效的内存操作,因此会发生分段错误。

          在第二种情况下,您将必须更新的变量的地址分配给指针并存储正确的值,因此没有分段错误。

          【讨论】:

            【解决方案5】:

            int a 存储一个随机整数值。所以说 *a,你可能正在访问一个超出范围或无效的内存位置。所以这是一个段错误。

            【讨论】: