簡體   English   中英

如何使用 C 中的指針返回值?

[英]How do i use a pointer in C to return a value?

我正在嘗試使用我寫的計數 function 使指針 P 返回給我數組中給定數字第一次出現的索引(J 的工作是返回出現的次數),這里的想法是我試圖寫一個 function 以這種方式返回 2 個值而不是 1(P 返回第一次出現的索引,J 返回其編號):

int count(int h[],int *p,int size,int d)
{
    int j=0;
    int bool = 0;
    for(int i=0; i<size ;i++)
    {
        if(h[i]==d && bool == 0)
        {
            j++;
            bool = 1;
            p = &i;
        }
        else if(h[i]==d)
            j++;
    }
    if(bool==0)
    {
        j=-1;
        *p=-1;
    return j;
    }
    else
    return j;
}

void main () 
{
int j;
int *p = NULL;
int h[] = {1,2,3,4,5,1,1,3,4};

j = count(h, p , sizeof(h)/sizeof(int) , 1);
printf("%d %d", *p,j);
}

我完全意識到,我可以簡單地使用 P 並以這種方式傳遞其地址,而不是使用 int *p:

void printarray(int h[],int size){
    for(int i=0;i < size ;i++)
    {
        printf(" %d ",h[i]);
    }
    printf("\n");
}

int count(int h[],int *p,int size,int d)
{
    int j=0;
    int bool = 0;
    for(int i=0; i<size ;i++)
    {
        if(h[i]==d && bool == 0)
        {
            j++;
            bool = 1;
            *p = i;
        }
        else if(h[i]==d)
            j++;
    }
    if(bool==0)
    {
        j=-1;
        *p=-1;
    return j;
    }
    else
    return j;
}

void main () 
{
int j;
int p;
int h[] = {1,2,3,4,5,1,1,3,4};

printarray(h,sizeof(h)/sizeof(int));

j = count(h, &p , sizeof(h)/sizeof(int) , 1);
printf("%d %d", p,j);
}

真正讓我感興趣的是,為什么在使用指針時會顯示分段錯誤? 還是我首先使用指針是錯誤的,如果我希望我的 function 為我返回 2 個值,我應該傳遞一個 int 的地址?

指針必須指向某物。 所以當你寫int *p = NULL; 你沒有給它指向的地方。 你可以做int n; int *p = &n; int n; int *p = &n; 並繼續通過p

但它從這里變得顛簸。 你試圖寫p = &i; function 內部。 雖然這是完全有效的代碼,但從那時起,分配給*p會弄亂您的循環控制 function。 這不是你想要的。 在“我知道我可以”部分你有更合理的*p = i; .

我猜你想知道如何將 function 中分配的內容傳遞給它的調用者。 您實際上可以這樣做; 有為此任務設計的標准庫函數。 你實際上可以寫

int count(int h[],int **p,int size,int d)
{
    int j=0;
    int bool = 0;
    for(int i=0; i<size ;i++)
    {
        if(h[i]==d && bool == 0)
        {
            j++;
            bool = 1;
            *p = malloc(sizeof(int));
            **p = i;
        }
        else if(h[i]==d)
            j++;
    }
    if(bool==0)
    {
        j=-1;
        return j;
    }
    else
        return j;
}

void main () 
{
int j;
int *p = NULL;
int h[] = {1,2,3,4,5,1,1,3,4};

j = count(h, &p , sizeof(h)/sizeof(int) , 1);
if (p) {
    printf("%d %d", *p,j);
    free(p);
} else {
    printf("(no index) %d", j);
}
}

所以這里發生的事情是我們在main()中有一個指針,它被初始化為不指向任何地方,可能會或可能不會在count中設置為指向索引。 如果沒有找到, p仍將指向 NULL(無處)。 function malloc()返回新分配的 memory 以便p有指向的地方; function free退貨。

**是一個簡單的想法,讓人頭疼。 由於指針是*我們在需要指向指針的指針時添加另一個* ,因此我們可以更改該指針。 只是不要問go能有多深。 答案是不要嘗試。 兩個經常發生; 三個需要一個很好的理由,而超過三個幾乎從來沒有做過。 超過兩個真的很頭疼,沒有人想在他們的頭疼上頭疼。 上限要高很多。 沒有人去那里。

這可能看起來毫無意義,而且確實如此。 malloc()用於在 function 中分配整個 arrays (其大小直到運行時才知道)並將它們返回給調用者。 單個 int 不值得像這樣管理(指針比它大),但對於數組來說它是有意義的。 如此之多,以至於在大型程序中,幾乎所有東西都在堆上; 與小程序不同,每個人都從幾乎所有東西都在堆棧上(局部變量在堆棧上)開始。

你在想你想正確地做什么,但是你把如何做有點復雜了。 而不是所有的if... else...語句,如果你只是使用一個你稱之為bool的標志(但讓我們將它重命名為不會與stdbool.h中的類型沖突的東西),稱之為first_set反而。 0 (false) 值開始初始化它,然后當你找到第一次出現時,更新first_set = 1;

如果您退后一步,您的主要檢查需要是什么,那么您真正擔心的是在數組中定位的是第一個,以及給定值(讓我們調用val而不是d )等於數組的所有剩余索引元素。 (並且由於countlength和數組索引不能為負數,因此我們選擇size_t作為正計數值的類型)。 為了跟蹤出現的次數,讓我們使用occurs作為變量(有助於在長程序中保持可讀性)

因此,您真正想做的就是遍歷數組中的每個元素並檢查當前元素是否等於您正在搜索的值。 這是可以采取所有其他行動的主要關注點。 您可以按照自己喜歡的順序保留參數,但我發現更容易考慮 function (1) 采用數組,(2) 具有給定數量的元素,我會在其中找到 (3) 第一次出現或 ( 4) 給定值。 (無論對您有意義,但以后處理您的代碼的人也可以閱讀)因此,稍微調整一下順序(至少對於我的老眼睛來說),您可以設置您的 function 循環遍歷每個元素並找到所有給定值的出現次數為:

size_t arr_occurrences (int *a, size_t nelem, size_t *first, int val)
{
    int first_set = 0;                              /* flag if 1st occurrence set */
    size_t occurs = 0;                              /* total no. of occurrences */
    
    for (size_t i = 0; i < nelem; i++) {            /* loop over each element */
        if (a[i] == val) {                          /* is it equal to val? */
            ...
            occurs += 1;                            /* increment no. of occurrences */
        }
    }
    
    return occurs;      /* return total number of occurrences */
}

唯一要添加的是如何設置第一次出現。 當您談論定位 function 中第一次出現的“地址”時,我認為您真正的意思是定位數組中第一次出現的“索引”。 您可以將索引轉換回調用 function 中的地址,而不必擔心傳遞可以保存地址的東西(這需要傳遞指向指針而不是指向int的指針)

因此,第一次a[i] == val測試為真時,您要捕獲索引並保存它,確保在其他任何時候都不會更改它a[i] == val測試為真你的循環。 您的first_set標志當前設置為0 ( false ),因此您真正需要在val出現的數組中捕獲第一個索引是:

            if (!first_set) {                       /* if first not set */
                *first = i;                         /* update value at address of first */
                first_set = 1;                      /* set first_set flag true */
            }

如果你把它代替上面 function 中的...占位符,你將擁有:

size_t arr_occurrences (int *a, size_t nelem, size_t *first, int val)
{
    int first_set = 0;                              /* flag if 1st occurrence set */
    size_t occurs = 0;                              /* total no. of occurrences */
    
    for (size_t i = 0; i < nelem; i++) {            /* loop over each element */
        if (a[i] == val) {                          /* is it equal to val? */
            if (!first_set) {                       /* if first not set */
                *first = i;                         /* update value at address of first */
                first_set = 1;                      /* set first_set flag true */
            }
            occurs += 1;                            /* increment no. of occurrences */
        }
    }
    
    return occurs;      /* return total number of occurrences */
}

簡短而甜蜜,不需要else任何東西,只需檢查您的標志first_set如果未設置,請捕獲索引並將first_set設置為 true,這樣您就不會在循環的其余部分進行更改。 您的 function 通過更新指針first保存的地址處的值,使第一次出現val的索引在調用 function 中可用。 function 返回數組中val的出現總數,因此調用者也可以使用它。 (將類型略微更改為size_t

(在size_t大於int的平台上使用size_t的另一個好處是您的數組(或分配的塊)可以容納超過INT_MAX的值,並且如果計數超過int可以表示的值,您的代碼仍然可以正常工作而沒有 integer 溢出的風險)

將其完全放在一個簡短的示例中,該示例創建一個包含 100 個元素的數組,其中包含從-1010的隨機值,然后通過選擇該范圍內的隨機值來選擇要查找的值,您可以檢查所有操作是否按預期工作,如下所示:

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

#define NELEM       100         /* if you need a constant, #define one (or more) */
#define PERROW       10
#define LO_LIM      -10
#define HI_LIM       10

int rand_in_range (int lowlim, int highlim)
{
    return rand() % (highlim - lowlim + 1) + lowlim;
}

void arr_fill_rand (int *a, size_t nelem, int lowlim, int highlim)
{
    for (size_t i = 0; i < nelem; i++)
        a[i] = rand_in_range (lowlim, highlim);
}

void arr_prn (int *a, size_t nelem, const char* fmt)
{
    const char *format = fmt != NULL ? fmt : " %d";
    
    for (size_t i = 0; i < nelem; i++) {
        if (i && i % PERROW == 0)
            putchar ('\n');
        printf (format, a[i]);
    }
    putchar ('\n');
}

size_t arr_occurrences (int *a, size_t nelem, size_t *first, int val)
{
    int first_set = 0;                              /* flag if 1st occurrence set */
    size_t occurs = 0;                              /* total no. of occurrences */
    
    for (size_t i = 0; i < nelem; i++) {            /* loop over each element */
        if (a[i] == val) {                          /* is it equal to val? */
            if (!first_set) {                       /* if first not set */
                *first = i;                         /* update value at address of first */
                first_set = 1;                      /* set first_set flag true */
            }
            occurs += 1;                            /* increment no. of occurrences */
        }
    }
    
    return occurs;      /* return total number of occurrences */
}

int main (void) {
    
    int arr[NELEM] = {0},                           /* array of NELEM set all zero */
        val = 0;                                    /* value to find */
    size_t  first = 0,                              /* index of first occurrence */
            occurs = 0;                             /* total no. of occurrences */
    
    srand (time(NULL));                             /* seed random number generator */
    
    val = rand_in_range (LO_LIM, HI_LIM);           /* random in LO to HI (inclusive) */
    
    arr_fill_rand (arr, NELEM, LO_LIM, HI_LIM);     /* fill with random values in range */
    arr_prn (arr, NELEM, " % 3d");
    
    occurs = arr_occurrences (arr, NELEM, &first, val);     /* get occurrences of val */
    
    printf ("\n%d occurs first in array of %d "             /* output results */
            "elements ranging from %d to %d at:\n"
            "  index   : %zu\n"
            "  address : %p\n"
            "and occurs a total of %zu times.\n",
            val, NELEM, LO_LIM, HI_LIM, 
            first, (void*)&arr[first], occurs);
}

注意:您永遠不需要多次調用printf() (或fputs()puts() )來提供 output 塊,無論它包含多少行。您可以通過中斷在更長的 output 中保持可讀性將字符串向上轉換為相鄰字符串(忽略它們之間的換行符和其他空格)和編譯器從所有相鄰的字符串文字中創建一個到 output 的單個字符串)

示例使用/輸出

程序輸出數組,然后輸出結果與第一次出現的索引(如果這是您想要的,它是實際地址)以及找到感興趣值的總次數:

$ ./bin/arr_rand_1st_occ+count
   3 -10   2  10   2  -9   4   5  -3   7
  -4  -6  10  -4   8  -5   8   5 -10  10
  10   6  -3   2  -5   1   1   8  10  -1
   6   0  -1  -5  -1  10  -6 -10   4   1
   5 -10   6   5  -4   1   8  -8  -4   8
  -9  -7   2  -4   5   7   5  -7   3   4
   2  -4  -6  10   1   1   9   5   0   0
  -7  -6  -2   9   7   3  -2   4   3   4
   2   4   5  -9   8  -3   6   2   0  -2
  -6 -10   4  -2   8  -8   7   6   5  -4

8 occurs first in array of 100 elements ranging from -10 to 10 at:
  index   : 14
  address : 0x7ffea2fb5938
and occurs a total of 7 times.

該數組是 output 具有 10 個值PERROW以使事情更容易閱讀。 請記住,在確認結果時,arrays 在 C 中是零索引的。

最后一點是除非你在獨立環境中編程(沒有任何操作系統的好處), main for 的允許聲明是int main (void)int main (int argc, char *argv[]) (你會看到用等效的char **argv編寫)。 請參閱: C11 標准 - §5.1.2.2.1 程序啟動(p1) 另請參閱: main() 在 C 和 C++ 中應該返回什么?

在沒有操作系統優勢的獨立環境(例如在微控制器上編程)中,在程序啟動時調用的 function 的名稱和類型是實現定義的。 請參閱: C11 標准 - 5.1.2.1 獨立環境

如果您還有其他問題,請仔細查看並告訴我。

您的程序行為未定義,因為它最終在此處取消引用NULL指針:

    int *p = NULL;
    int h[] = {1,2,3,4,5,1,1,3,4};

    j = count(h, p , sizeof(h)/sizeof(int) , 1);
    printf("%d %d", *p,j);
                    ^^
                     |
              Dereferencing p which is a NULL pointer

您將p指針從main() function 傳遞到count() function 的方式,您正在傳遞NULL 這意味着,在您的程序的上下文中,這

j = count(h, p , sizeof(h)/sizeof(int) , 1);

相當於這個

j = count(h, NULL , sizeof(h)/sizeof(int) , 1);

The count() function parameter p will receive NULL as argument ie it's equivalent to p = NULL (where p is count() function parameter).

無論您在count() function 中為p分配什么,實際上都是在修改count() function 的局部變量。 main() function 的指針變量p不會有任何變化,它將保持為NULL指針。

現在,讓我們來看看count() function。

count() function 的代碼片段中:

    for(int i=0; i<size ;i++)
    {
        if(h[i]==d && bool == 0)
        {
            j++;
            bool = 1;
            p = &i;
        }
        else if(h[i]==d)
            j++;
    }

scope 和循環變量i的壽命在for循環塊內。 一次,循環退出i的生命周期結束。 JFYI - 在其生命周期之外訪問 object 是未定義的行為。 因此,如果p持有&i並且如果您的程序訪問p並且超出循環體,那么它將導致未定義的行為。 此外, *p=-1; count()中的語句也會導致未定義的行為,因為在count()中,如果bool==0 ,function p將是NULL指針。

Remember, if a function is passing a pointer to another function as a argument and expecting that function to modify the value of pointer, which it receive as parameter, then the pointer should be pointing to a valid memory location before the calling function access and modify it's memory contents or the caller function should pass address of pointer as argument and let the calling function allocate memory dynamically to it so that it should be accessible from the caller function as well (which is main() function, in your case).

有幾種方法可以實現這一目標。

方法一:
將有效的 memory 位置傳遞給調用 function。
將調用者 function 的局部變量的地址作為參數傳遞(這與您的第二個代碼片段相同)

int main (void) {
    int j;
    size_t res = 0;
    size_t *p = &res;     // p is pointing to res variable
    int h[] = {1, 2, 3, 4, 5, 1, 1, 3, 4};
    int num = 1;

    j = count (h, p, sizeof (h) / sizeof (int), num);
    if (j != -1) {
        printf ("%zu %d", *p, j);
    } else {
        printf ("%d not found\n", num);
    }
    return 0;
}

或者將 memory 分配給調用者 function 中的指針並傳遞它

int main (void)
{
    int j;
    size_t *p = NULL;
    int h[] = {1, 2, 3, 4, 5, 1, 1, 3, 4};
    int num = 1;

    p = malloc (sizeof (size_t));

    if (p == NULL) {
        fprintf (stderr, "Failed to allocate memory");
        exit (EXIT_FAILURE); // or whatever you want to do in case of allocation failure
    }

    j = count (h, p, sizeof (h) / sizeof (int), num);
    if (j != -1) {
        printf ("%zu %d", *p, j);
    } else {
        printf ("%d not found\n", num);
    }

    free(p);

    return 0;
}

以及上述兩種情況的count() function 實現:

int count (int h[], size_t *p, size_t size, int d) {
    int j = 0;

    for (size_t i = 0; i < size ; ++i) {
        if(h[i] == d) {
            if (j == 0) {
                *p = i;
            }
            ++j;
        }
    }

    return j == 0 ? -1 : j;
}

方法二:
傳入p的地址,在訪問之前在count() function 中動態分配 memory 給p 請注意,具有分配存儲持續時間的對象會一直存在,直到它們被調用free()銷毀。 So, if count() function allocates memory to a pointer, whose address passed as argument to count() function from main() function, then that memory will be accessible in main() function as well via pointer variable p .

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

int count (int h[], size_t **p, size_t size, int d) {
    int j = 0;

    for(size_t i = 0; i < size ; ++i) {
        if(h[i] == d) {
            if ((j == 0) && (p != NULL)) {
                *p = malloc (sizeof (size_t));
                if (p == NULL) {
                    fprintf (stderr, "Failed to allocate memory");
                    exit (EXIT_FAILURE);
                }
                **p = i;
            }
            ++j;
        }
    }

    return j == 0 ? -1 : j;
}

int main (void) {
    int retval;
    size_t *p = NULL;
    int h[] = {1, 2, 3, 4, 5, 1, 1, 3, 4};
    int num = 3;

    retval = count (h, &p, sizeof (h) / sizeof (int), num);
    if (retval != -1) {
        printf ("%zu %d\n", *p, retval);
    } else {
        printf ("%d not found\n", num);
    }

    // free memory once you are done with it
    if (p != NULL) {
        free (p);
    }

    return 0;
}

額外的:

  • 使用void作為main function 的返回類型不符合標准。 main function 的返回類型應該是int

您正在嘗試返回指向一旦循環完成執行后將不再有效的位置的指針,要么for外部聲明i要么不使用指針部分,因為取消引用 null 指針是非法的,而且,你得到分段當您嘗試訪問 memory 時出現故障,您沒有業務,也沒有讀取或寫入權限,在這種情況下,指向 memory 位置,該位置不在 scope 中。

不用返回指針,只需在 function 中創建一個相同類型的本地數組,給定數字的第一次出現的第一個索引,總出現的第二個索引,並返回數組但確保返回類型應該是指針同類型。

編輯:感謝 Joshua 先生指出錯誤。

暫無
暫無

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

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