簡體   English   中英

遞增指針是否發生在 C 中的連續 memory 上

[英]does incrementing pointer happens on contiguous memory in C

我正在為分配增加指針變量,但它給了我段錯誤消息。 程序沒有任何問題,因為我的程序一直執行到結束,除了 return 語句。 所以基本上我在想

當我們像pointer+1這樣遞增指針時,我們在連續的 memory 和 function 內部向前移動 - 可能在現實中 - 變量和 function 的地址的地址 - 如果我以分散的方式調用錯誤,則可能是正確的。 And when we increment pointer to move into next contiguous memory address and that memory could be anything (some other function/program) and my allocation is done on some area that serves memory for something else and may be something outside the memory region of abc function . 這是我的代碼

    #include <stdio.h>

    struct abc{ char *c;};

    void abc(struct abc **t1);

    int main() {
        struct abc *k;
        abc(&k);
        printf("%s\n",k->c);
     
       // printf("%s\n",k->c);
        printf("%s\n",(k+1)->c);
       // printf("%s\n",(k+2)->c);

       return 1;
    }

    void abc(struct abc **t1){


        *t1=(struct abc *)malloc(sizeof(struct abc ));
        (*t1)->c="hello";
        *(t1+1)=(struct abc *) malloc((sizeof(struct abc )));
        (*t1+1)->c="cool";
        *(t1+2)=(struct abc *) malloc((sizeof (struct abc )));
        //(*t1+2)->c="Super cool";
    }

還有一個問題是為什么我在main function 的返回時似乎出現了段錯誤。 因為主 memory 區域不同於abc memory 區域。 我需要對此進行一些解釋,並且有關這種分配方式是否錯誤的問題。 如果它沒有錯,如何糾正以使其在沒有段錯誤的情況下工作

更新

    #include <stdio.h>

    struct abc{ char *c;};

    void abc(struct abc **t1);
    void abc2(struct abc *t1);
    int main() {
        struct abc *k;
        abc(&k);
        //printf("%s\n",k->c);
      // k->c="hi"; Seg Fault causes because ->c is immutable from assigning string literal
       // printf("%s\n",k->c);
        printf("%s\n",(k)->c);
        printf("%s\n",(k+1)->c);
        printf("%s\n",(k+2)->c);
    //   / printf("%s\n",(k+2)->c);

       return 1;
    }

    void abc(struct abc **t1){


        *t1=(struct abc *)malloc(sizeof(struct abc)*3);
        abc2((*t1));
           abc2((*t1+1));
        abc2((*t1+2));
     
    }

    void abc2(struct abc *t1){
        (t1)->c="cool";
    }

上面的代碼在沒有 segFault 的情況下工作,但它與答案不同,所以上面的代碼有什么問題

如下也有效

        #include <stdio.h>

        struct abc{ char *c;};

        void abc(struct abc **t1);
        void abc2(struct abc *t1);
        int main() {
            struct abc *k;
            abc(&k);
            //printf("%s\n",k->c);
          // k->c="hi"; Seg Fault causes because ->c is immutable from assigning string literal
           // printf("%s\n",k->c);
            printf("%s\n",(k)->c);
            printf("%s\n",(k+1)->c);
            printf("%s\n",(k+2)->c);
        //   / printf("%s\n",(k+2)->c);

           return 1;
        }

        void abc(struct abc **t1){


            *t1=(struct abc *)malloc(sizeof(struct abc)*3);
            (*t1)->c="Cool";
            (*t1+1)->c="more cool";
            (*t1+2)->c="super cool";
            //below also works
            abc2((*t1));
             abc2((*t1+1));
         abc2((*t1+2));
         /*   (*t1)->c="hello";
            //*(t1+1)=(struct abc *) malloc((sizeof(struct abc)));
            (*t1+1)->c="cool";
            //*(t1+2)=(struct abc *) malloc((sizeof (struct abc)));
            (*t1+2)->c="Super cool";*/
        }

        void abc2(struct abc *t1){
            (t1)->c="cool";
        }

您傳遞給 abc() 的值指向未初始化的 memory。 您必須為要存儲的三個結構提供malloc空間。 否則,取消引用它是未定義的行為。 在我看來,您的目標是讓k成為一個由 3 個指向結構的指針組成的數組。 如果是這種情況,您應該像這樣聲明它:

struct abc **k = (struct abc **)malloc(sizeof(struct abc *) * 3);

這應該在 32 位系統上分配 12 個字節(每個指針 4 個),或在 64 位系統上分配 24 個字節(每個指針 8 個)。

由於k現在是一個指向指針的指針而不僅僅是一個指針,所以在調用 abc() 時不再需要獲取它的地址,因此下一行變為:

abc(k);

在 abc function 內部,您不應手動將值遞增到指針。 這就是[]運算符的目的。 您還應該手動將字符串轉換為char* s。 進行這些更改后,您的 abc function 應如下所示:

void abc(struct abc **t1){
    t1[0]=(struct abc *)malloc(sizeof(struct abc));
    t1[0]->c=(char *) "hello";
    
    t1[1]=(struct abc *)malloc(sizeof(struct abc));
    t1[1]->c=(char *) "cool";
    
    t1[2]=(struct abc *)malloc(sizeof(struct abc));
    t1[2]->c=(char *) "Super cool";
}

注意t1[0]*t1如何做同樣的事情。 后者應始終與 arrays 一起使用,以明確t1實際上是一個數組。

您應該在 printf() 調用中進行類似的更改,使它們看起來像這樣:

printf("%s\n",k[0]->c);
printf("%s\n",k[1]->c);
printf("%s\n",k[2]->c);

最后,您應該始終釋放 malloc 的 memory:首先釋放數組中的每個指針:

free(k[0]);
free(k[1]);
free(k[2]);

然后釋放數組本身:

free(k);

養成始終釋放數據的習慣將非常有助於防止大型程序中出現痛苦的 memory 錯誤。

畢竟,您的最終程序應該如下所示:

#include <stdio.h>

struct abc {
    char *c;
};

void abc(struct abc **t1);

int main() {
    struct abc **k = (struct abc **)malloc(sizeof(struct abc *) * 3);
    abc(k);
    
    printf("%s\n",k[0]->c);
    printf("%s\n",k[1]->c);
    printf("%s\n",k[2]->c);
    
    free(k[0]);
    free(k[1]);
    free(k[2]);
    
    free(k);

    return 1;
}

void abc(struct abc **t1){
    t1[0]=(struct abc *)malloc(sizeof(struct abc));
    t1[0]->c=(char *) "hello";
    t1[1]=(struct abc *)malloc(sizeof(struct abc));
    t1[1]->c=(char *) "cool";
    t1[2]=(struct abc *)malloc(sizeof(struct abc));
    t1[2]->c=(char *) "Super cool";
}

要回答您的最后一個問題,段錯誤的行為和時間可能是一件非常棘手且不可預測的事情。 它們通常是由未定義的行為引起的,在同一程序的不同執行之間甚至可能不一樣。 您應該知道的是,程序中發生段錯誤的位置與發生段錯誤的memory中的位置無關。

分配方法是正確的,但是您忘記了初始化k ,所以k是一個懸空指針。 這就是您有分段錯誤的原因。 我通過添加一個編譯器將初始化的變量來應用一個簡單的“臟”修復,只是為了證明這是問題所在。

#include <stdio.h>
#include <stdlib.h>

struct abc{ char *c;};

void abc(struct abc **t1);

int main() {
    struct abc _abc[2], *k;
        k = &_abc[0];
    abc(&k);
    printf("%s\n",k->c);

    printf("%s\n",k->c);
    printf("%s\n",(k+1)->c);
    //printf("%s\n",(k+2)->c);

    return 1;
}

void abc(struct abc **t1){


    *t1=(struct abc *)malloc(sizeof(struct abc ));
    (*t1)->c=malloc(50);
    sprintf((*t1)->c, "hello");
    *(t1+1)=(struct abc *) malloc((sizeof(struct abc )));
    (*t1+1)->c=malloc(50);
    sprintf((*t1+1)->c, "cool");
    *(t1+2)=(struct abc *) malloc((sizeof (struct abc )));
    //(*t1+2)->c="Super cool";
}

編譯后

~$ gcc memtest.c -o memtest
~$ ./memtest
hello
hello
cool

要了解為什么您在main而不是abc中出現分段錯誤,您需要在匯編程序gcc test.c -S -ggdb3中轉換示例。 在我的例子中,在 ARM64 上, k被優化了。 在調用abc()之前,編譯器會插入一個__stack_chk_guard 這是匯編中的段(ARM64)

    .arch armv8-a
    .file   "memtest_bug.c"
    .text
.Ltext0:
    .align  2
    .global main
    .type   main, %function
main:
.LFB6:
    .file 1 "memtest_bug.c"
    .loc 1 8 16
    .cfi_startproc
    stp x29, x30, [sp, -32]!
    .cfi_def_cfa_offset 32
    .cfi_offset 29, -32
    .cfi_offset 30, -24
    mov x29, sp
    .loc 1 8 16
    adrp    x0, :got:__stack_chk_guard
    ldr x0, [x0, #:got_lo12:__stack_chk_guard]
    ldr x1, [x0]
    str x1, [sp, 24]
    mov x1,0
    .loc 1 10 9
    add x0, sp, 16
    bl  abc

當調用abc()時, k指向一個屬於進程的 memory 區域,所以它仍然是一個有效的段,不會產生分段錯誤。 當 main 完成時,編譯器插入一個代碼來檢查堆棧保護,如果檢查失敗,它會調用__stack_chk_fail

    mov x3, 0
    beq .L3
    bl  __stack_chk_fail
.L3:
    mov w0, w1
    ldp x29, x30, [sp], 32
    .cfi_restore 30
    .cfi_restore 29
    .cfi_def_cfa_offset 0
    ret
    .cfi_endproc

此代碼檢測 memory 保護的故障並生成分段故障。 這是生成故障時的堆棧(在 ARM64 上)

#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x0000fffff7e7cd68 in __GI_abort () at abort.c:79
#2  0x0000fffff7eca29c in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0xfffff7f89f58 "*** %s ***: terminated\n") at ../sysdeps/posix/libc_fatal.c:155
#3  0x0000fffff7f3c600 in __GI___fortify_fail (msg=msg@entry=0xfffff7f89f40 "stack smashing detected") at fortify_fail.c:26
#4  0x0000fffff7f3c5d4 in __stack_chk_fail () at stack_chk_fail.c:24
#5  0x0000aaaaaaaaa8e4 in main () at memtest_bug.c:18

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM