2012-12-22 98 views
2

我正在學習bison/yacc(並查看一些c),並嘗試構建一個json解析器作爲簡單的測試項目。指針,野牛和yacc

使用在http://www.json.org/上找到的術語我有一個結構對,它表示一個字符串/值對和一個結構對象,該結構對象具有一個基本上包含指向鏈接對的鏈表的成員字段。

我有一個簡單的c函數(create_pair),返回一個新的對。我注意到一個奇怪的行爲,我無法解釋:

  • 如果我從「main」調用這樣的函數並打印返回結構的內存地址,它們的地址總是不同的。
  • 如果我在野牛「動作」中調用了非常相同的函數,我發現我的函數返回的是一個總是駐留在同一個內存地址上的指針。

這是否有意義?

詳細信息/代碼如下:

這裏的代碼(該鏈接,包含列入「工程」的指向四個不同的文件4個引擎收錄鏈接列表):

你可以編譯和運行它:

lex t.l 
yacc -d t.y 
cc y.tab.c lex.yy.c t.c 
./a.out 

如果啓動代碼,並與下面的輸入運行:

{ 「名字」: 「A」, 「姓氏」: 「B」 }

你會發現:

1)中的「主」(檢查文件TY)執行的代碼,創建四個不同的一對對象,那麼我打印他們的存儲器地址,並且輸出是類似的信息(通知不同的地址):

p 0x7fff52476be8 //(<-memory address for pair p) 
print pair: P, Hellov 
q 0x7fff52476bc8 //(<-memory address for pair q) 
print pair: Q, Hellox 

2)只要粘貼上面的json樣本,我們兩次擊中「pair」規則,第一次爲「firstName」:「A」,第二次爲「lastName」:「B」,我創建了一個新對在這兩種情況下都打印出內存地址,它們是一樣的:

Creating pair 0x7fff52475c88 
print pair: firstName, A 
Creating pair 0x7fff52475c88 
print pair: lastName, B 

爲什麼會發生這種情況?

回答

2

你應該不在乎pair的地址是什麼。與他們進行的工作無關,您看到的地址是偶然的,沒有後果。

您的功能create_pair不返回指針。它被聲明爲pair create_pair(…),所以它按照價值返回pair

main中,您定義了pair p = create_pair(l, v);。這會創建一個自動對象p,通常通過在堆棧上爲其留出空間。然後它調用create_paircreate_pair返回的值被複制到p。後來,當您打印&p時,您正在打印地址p,而不是create_pair返回的地址。

同樣,當您定義pair q = create_pair(l, x);時,您將創建另一個對象q。由於此對象的使用期限與p的使用期限重疊,因此它們必須位於不同的地方,因此它們具有不同的地址。當您打印&q時,您會看到這個不同的地址。

接下來,請考慮您放置在Bison規則中的代碼,pair p = create_pair($<u_string>1, $<u_value>3);。 Bison在處理規則時執行此代碼。它創建一個自動對象,並打印它的地址。然後執行離開這個代碼的範圍,Bison無疑會繼續做其他事情,並退出當前正在執行的處理。自動對象的生命週期結束,並且彈出堆棧中的數據。後來,野牛回到了這個規則。在那個時候,僅僅因爲計算機在操作中是機械的,堆棧指針具有與之前相同的地址。因此,當創建新的p時,它恰好與舊的p處於相同的位置。與pq不同,它們必須位於不同的地方,因爲它們同時存在,這個舊的p和新的p只存在於不同的時間,所以它們可能在相同的地方。

這不一定總是會發生。如果你的語法比較複雜,Bison可能會同時在堆棧中存儲其他東西,而不是另一個(或者不是; Bison生成的解析機器可能不會這樣做;我不知道如何)。或者,如果您在另一個規則中使用相同的代碼,則在處理該規則時,堆棧可能會有所不同。

+0

好的,這是清楚的,絕對是我的問題的正確答案:謝謝!但讓我重新啓動一下:當「成員」規則被擊中時(成員:成員COMMA對),我怎麼可能檢查$ 1和$ 3的值(其中$ 1是從成員:對返回的值)我看到1美元和3美元是指向同一對?希望這是明確的。 – user1417009

+0

@ user1417009:在生產'pair:string COLON value'中,您分配'$$ =&p;'。據我們所知,'&p'是棧上自動對象的地址。只要此規則的代碼退出其塊(緊接在此賦值之後),對象生存期結束,並且該堆棧被重新用於其他事情。所以你與這個'pair'關聯的地址是無效的。當執行達到'members'的規則時,它會嘗試使用這個地址,這恰好指向了'members'的對象。 [續...] –

+0

您不應該在'yylval'對象中保留自動對象的地址。您必須複製對象或使用指針指向您動態分配的地址(以及後來的空閒地址)。 –

0

所以,你所看到的是一個堆棧變量的地址變化。這是完全正常的。如果他們都有相同的地址,你會得到一個值覆蓋另一個值,這不會很有用。

編輯:當你調用一個函數時(來自同一個調用者函數,例如main),堆棧變量的地址總是相同的 - 因爲在調用時堆棧開始是相同的[通常 - 當然,有時編譯器會對堆棧做些有趣的事情,所以它不是100%保證]

編輯2:澄清,如果調用是相同的調用鏈,例如從函數C中的函數B調用函數A,則堆棧在A中與OFTEN相同,無論在B或C中進行調用。當然,如果我們從函數C的函數D調用函數A,那麼所有的投注在A中的局部變量的地址都是關閉的[好吧,它很可能非常相似,但是如果函數D有一些巨大的局部變量,它可以是非常不同的]。這是TYPICAL的警告仍然適用。編譯器可能會將堆棧清理掉,直到「足夠麻煩」爲止,而不是清理每一個調用,這意味着對函數A的三次調用可能會在堆棧上累積一些「垃圾」,而這些垃圾不會被清理乾淨到後來。

我有點困惑,爲什麼你認爲它應該與此不同?

+0

那麼,在情況1,我總是得到不同的內存地址 - >並不感到驚訝。只要我在野牛行動中調用它(情況2),返回的「對」總是具有相同的地址 - >「驚奇」。現在更清楚了嗎?程序中實際發生的是這些變量實際上被覆蓋(例如:檢查成員規則滿足時會發生什麼)。 – user1417009

+0

說一個函數從同一個調用者被調用時,堆棧指針是相同的,這是不正確的。考慮從'c'調用的'b'調用'a',以及''從'd'調用'b'調用'a'的時候。 'c'和'd'中不同的堆棧使用會導致'a'中的堆棧指針不同。此外,其他事情可能會影響堆棧,例如從'b'中的一個點調用'a',其中有一個嵌套的範圍並聲明瞭額外的變量。 –

+0

是的,我想試着說在我的文章的[]部分沒有保證堆棧地址。但通常情況下,如果您從函數B多次調用函數A,則函數B中的局部變量將每次都是相同的。不以任何方式保證,但是這很常見。我想我們解釋了大致相同的事情 - 或者我很困惑。 –