簡體   English   中英

這是從函數返回結構數組的正確方法嗎?

[英]Is this the correct way to return an array of structs from a function?

正如標題所說(並建議),我是C的新手,我試圖從函數中返回任意大小的結構數組。 我選擇使用malloc作為互聯網上的某個人,比我聰明,指出除非我分配給堆,否則當points_on_circle完成執行時,數組將被銷毀,並返回一個無用的指針。

我提交的代碼曾經工作,但現在我在我的代碼中越來越多地調用函數,我得到一個運行時錯誤./main: free(): invalid next size (normal): 0x0a00e380 我猜這是由我的黑客一起實現的數組/指針。

我還沒有free調用,因為我正在構建的許多數組都需要在程序的整個生命周期中持續存在(我將向其余部分添加free()調用!)。

xy* points_on_circle(int amount, float radius)
{
  xy* array = malloc(sizeof(xy) * amount);

  float space = (PI * 2) / amount;

  while (amount-- >= 0) {
    float theta = space * amount;

    array[amount].x = sin(theta) * radius;
    array[amount].y = cos(theta) * radius;
  }

  return array;
}

我的突破性xy結構定義如下:

typedef struct { float x; float y; } xy;

我如何調用該函數的示例如下:

xy * outer_points = points_on_circle(360, 5.0);
for(;i<360;i++) {
    //outer_points[i].x
    //outer_points[i].y
}

可以理解正確方向的指針

在一個功能中分配內存並將其釋放到另一個功能中充滿了危險。

我將分配內存並將其(內存緩沖區)傳遞給函數,並帶有一個參數,指示允許將多少結構寫入緩沖區。

我見過有兩個函數的API,一個用於獲取所需內存,另一個用於在分配內存后實際獲取數據。

[編輯]找到一個例子: http//msdn.microsoft.com/en-us/library/ms647005%28VS.85%29.aspx

編輯: 您正在正確返回數組 ,但......


考慮你正在制作一個包含1元素的數組

xy *outer_points = points_on_circle(1, 5.0);

功能內部會發生什么?
讓我們檢查 ...

  xy* array = malloc(sizeof(xy) * amount);

1元素分配空間。 好!

  while (amount-- >= 0) {

1大於或等於0因此循環執行(並且數量減少)
設置array[0] ,返回循環頂部

  while (amount-- >= 0) {

0大於或等於0因此循環執行(並且數量減少)
您現在正在嘗試設置array[-1] ,這是無效的,因為索引引用了數組之外的區域。

我會說這個程序設計存在根本缺陷。 首先,邏輯上一個正在進行計算的函數與內存分配無關,這是兩個不同的東西。 其次,除非分配內存的函數和釋放內存的函數屬於同一個程序模塊,否則設計很糟糕,你可能會遇到內存泄漏。 相反,將分配留給調用者。

該代碼還包含各種危險的做法。 避免使用 - 和++運算符作為復雜表達式的一部分,它是導致錯誤的常見原因。 您的代碼看起來好像有一個致命的錯誤並且正在寫出數組的界限,只是因為您正在與其他運算符混合。 在C語言中沒有任何理由這樣做,所以不要這樣做。

另一個危險的做法是依賴C的隱式類型轉換,從int到float(平衡,又稱“通常的算術轉換”)。 這段代碼中的“PI”是什么? 它是int,float還是double? 代碼的結果將根據此情況而有所不同。

這是我建議的(未經測試):

void get_points_on_circle (xy* buffer, size_t items, float radius)
{
  float space = (PI * 2.0f) / items;
  float theta;
  signed int i;

  for(i=items-1; i>=0; i--)
  {
    theta = space * i;

    buffer[i].x = sin(theta) * radius;
    buffer[i].y = cos(theta) * radius;
  }
}

我認為這:

while (amount-- >= 0 ) {

應該:

while ( --amount >= 0 ) {

考慮最初金額為零的情況。

就我而言,你做的是正確的事情,當然你的呼叫者可以免費獲得結果。

有些人喜歡在同一個地方(對稱)進行內存分配和釋放責任,即在你的功能之外。 在這種情況下,您將傳遞預先分配的xy*作為參數,並在指針為null時返回緩沖區的大小:

int requiredSpace = points_on_circle(10, 10, NULL);
xy* myArray = (xy*)malloc(requiredSpace);
points_on_circle(10, 10, myArray);
free(myArray);

你正在迭代一個元素。

您應該使用降至零運算符:

while ( amount --> 0) {
    ...
}

在一個功能中分配記憶並在另一個功能中釋放它就像給一個四歲的孩子槍。 你不應該這樣做。

雖然您決定倒數和使用而不是使用臨時值可以節省一些內存周期,但它在概念上更令人困惑。 特別是在數學一直在這里的情況下,你只需要保存分數。

但這並不是你應該浪費少量時間的原因。 這個問題是原因:你浪費了幾個小時! 這適用於即使是最有經驗和最聰明的程序員。 錯誤只是更復雜,超出了stackoverflow答案的范圍! 換句話說,彼得原則也適用於編碼。

不要犯這個錯誤,因為你獲得了經驗,你可以通過采取這些風險來挽救一兩個循環。 這就是為什么代碼完成中的McConnell將Humility列為積極的程序員屬性。

以下是您可能想到的解決方案:

xy* points_on_circle(int amount, float radius)
{
  xy* array = malloc(sizeof(xy) * amount);

  float space = (PI * 2) / amount;

  int index;

  for (index=0;index<amount;index++) {
    float theta = space * index;

    array[index].x = sin(theta) * radius;
    array[index].y = cos(theta) * radius;
  }

  return array;
}

如果你需要速度,你可以做的一件小事就是將theta設置為0並且每次都添加'space',因為+必然比浮點數低*。

加速10倍或更多?

如果您需要嚴格的速度,來自answers.com的這個提示將為您提供10倍的改進,如果你做得對:

根據畢達哥拉斯定理,x2 + y2是radius2。 然后,對於x或y,求解另一個以及半徑是簡單的。 您也不需要計算整個圓圈 - 您可以計算一個象限,並通過對稱生成其他三個象限。 例如,您生成循環將簡單地從原點到半徑迭代為x乘以delta x,生成y,並在其他三個象限中反映出來。 您還可以計算象限的一半,並使用對稱和反射來生成其他七個半象限。

當我12歲的時候,我覺得自己很酷,在我的atari 800上用圖形8中的sin和cos繪制一個圓圈。我的堂兄Marty(當時最近的工作形式是微軟機器人)擦除了我的程序並實現了上述解決方案,僅使用了加法如果我沒記錯的話,在循環中,並在4秒而不是一分鍾內繪制相同的圓圈! 如果我沒有受洗,我會在敬拜中屈服。 太糟糕了我沒有方便的代碼,但我打賭一點點谷歌搜索會帶來它。 任何人?

暫無
暫無

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

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