簡體   English   中英

我如何理解該程序的輸出?

[英]How can I understand the output of this program?

我的書試圖使我熟悉一些概念,例如有關結構的指針取消引用和一些訪問結構的怪異方法。 我是新手,發現以下代碼令人困惑。

#include <stdio.h>
#include <time.h>
void dump_time_struct_bytes(struct tm *time_ptr, int size) {
    int i;
    unsigned char *raw_ptr;

    printf("bytes of struct located at 0x%08x\n", time_ptr);
    raw_ptr = (unsigned char *)time_ptr;
    for (i = 0; i < size; i++)
    {
        printf("%02x ", raw_ptr[i]);
        if (i % 16 == 15) // Print a newline every 16 bytes.
            printf("\n");
    }
    printf("\n");
}
int main() {

    long int seconds_since_epoch;
    struct tm current_time, *time_ptr;
    int hour, minute, second, i, *int_ptr;

    seconds_since_epoch = time(0); // Pass time a null pointer as argument.
    printf("time() - seconds since epoch: %ld\n", seconds_since_epoch);
    time_ptr = &current_time; // Set time_ptr to the address of
                              // the current_time struct.
    localtime_r(&seconds_since_epoch, time_ptr);

    // Three different ways to access struct elements:
    hour = current_time.tm_hour; // Direct access
    minute = time_ptr->tm_min; // Access via pointer
    second = *((int *)time_ptr); // Hacky pointer access
    printf("Current time is: %02d:%02d:%02d\n", hour, minute, second);
    dump_time_struct_bytes(time_ptr, sizeof(struct tm));

    minute = hour = 0; // Clear out minute and hour.

    int_ptr = (int *)time_ptr;
    for (i = 0; i < 3; i++) {
        printf("int_ptr @ 0x%08x : %d\n", int_ptr, *int_ptr);
        int_ptr++; // Adding 1 to int_ptr adds 4 to the address,
    } // since an int is 4 bytes in size.
}

輸出:

time() - seconds since epoch: 1189311744
Current time is: 04:22:24
bytes of struct located at 0xbffff7f0
18 00 00 00 16 00 00 00 04 00 00 00 09 00 00 00
08 00 00 00 6b 00 00 00 00 00 00 00 fb 00 00 00
00 00 00 00 00 00 00 00 28 a0 04 08
int_ptr @ 0xbffff7f0 : 24
int_ptr @ 0xbffff7f4 : 22
int_ptr @ 0xbffff7f8 : 4
  1. 一世。 我了解作者已經將* time_ptr重新聲明為指向未簽名char的指針,但是它如何設法成為一個數組(我認為是字符數組)? 我認為這可能與以下事實有關:數組被解釋為指向其第0個元素的指針,但我不確定。

    II。 其次,dump_time_struct_bytes函數的輸出(轉儲字節)是什么? 我知道那是結構中的字節,但是我不知道它們應該如何構成存儲在其中的4小時22分24秒(如果完全是這種情況)。 另外,* time_ptr的地址對應什么? 這是結構的開始嗎? 如果后者為true,則輸出中相應的轉儲字節僅屬於其第一個元素(tm_sec)還是屬於整個結構?

  2. “ hacky指針”的解釋有點奇怪-為什么取消引用轉換后的整數指針僅顯示結構tm_sec中第一個元素的內容?

先感謝您。

“我知道作者已經將* time_ptr重新聲明為指向未簽名char的指針,但是它如何設法成為一個數組(我認為是字符數組)?”

指針指向內存。 內存是字節數組。 指針指向多少字節取決於所指事物的解釋(類型)。 除了這個簡單的事實之外,編譯器不會在C / C ++中進行邊界檢查。 因此,從本質上講,每個指針都是指向該指針所指向類型的元素數組的指針。 因此,指向無符號字符的指針是指向單字節字符數組的指針。 指向結構的指針是指向元素數組的指針,每個元素數組的長度與一個結構的大小一樣長。

因此,一個指針到一個單一的結構一個指向尺寸1.在語言沒什么的陣列防止代碼被壞,並試圖在下一位置來訪問的元素。

這既是指針的力量,也是詛咒。 並且是C / C ++中許多錯誤和安全問題的根源。 這也是為什么您可以使用該語言高效地完成很多很酷的事情的原因。

“擁有權利的同時也被賦予了重大的責任。”

因此,此代碼首先將struct指針解釋為字節數組,然后輸出十六進制轉儲,然后將其解釋為整數數組。 當將指針作為int *處理時,單次遞增操作將移動4個字節。

因此,第一個元素是0x00000018(4個字節的小尾數:18 00 00 00)。 0x18十六進制為24。

第二個整數是0x00000016(16 00 00 00的小尾數)= 22。

等等。

請注意,int *移動了4個字節,因為在您的特定編譯器中, sizeof(int) == 4 “ int”是一種特殊類型,可以根據您的編譯器更改大小。 如果您使用其他編譯器(例如嵌入式微控制器),則sizeof(int)可能為2,並且整數將輸出為24、0、22(假定完全相同的內存塊)。

C的大小是“ int” 2字節還是4字節?

===回應評論===

“(在其他地方偶然評論了)謝謝您的回答。但是,有一件事似乎還不清楚。假設我有一個指向char'c'的指針。該指針現在是一個指向大小為char的數組的指針嗎? 1?

是。 一個字節的字節數組。

另外,為驗證起見,您已提到指向單個結構的指針是指向大小為1的數組的指針。

是的,但是在這種情況下,數組中單個元素的大小為sizeof(mystruct) ,它可能大於單個字節。

因此,將指針類型轉換為指向char的指針將導致數組大小現在大於1,並且是一個字節數組,負責十六進制轉儲。

是。

因此,以這種方式進行類型轉換時,是否有任何指針會導致這種字節破壞?

是。 這就是字節/內存轉儲的工作方式。

關於sizeof(type)關鍵字的另一件事。 sizeof(type)報告type實例的大小(以字節為單位)。 sizeof(variable)等同於sizeof(variable)類型)。 當變量是指針或數組時,這具有微妙的行為。 例如:

char c = '0'   // in memory this is the single byte 0x30
char str[] = { 0x31, 0x32, 0x00 }; // an array of bytes 0x31, 0x32, 0x00

sizeof(char) == sizeof(c) == 1
sizeof(str) == 3 // compiler knows the array was initialized to 3 bytes
sizeof(p) == 4 // assuming your compiler is using 32-bit pointers.  On a 64-bit machine this would be 8.

char* p = &c;  //  note that assigning a pointer to the address of a variable requires the address-of operator (&)

sizeof(*p) == 1 // this is the size of the thing pointed to.

p = str; // note that assigning an ARRAY variable name to a pointer does not require address-of (because the name of an array IS a pointer - they *are* the same type in all ways except with respect to sizeof() where sizeof() knows the size of an initialized array.)

sizeof (*p) == 1; // even though p was assigned to str - an array - sizeof still returns the answer based on the type of the thing p is pointing to - in this case a single char.  This is subtle but important.  p points to a single character in the array.

// Thus at this point, p points to 0x31.
p++; // p advances in memory by sizeof(*p), now points at 0x32.
p++; // p advances in memory by sizeof(*p), now points at 0x00.
p++; // p advances in memory by sizeof(*p), now points BEYOND THE ARRAY.

重要說明-由於指針已超前數組末尾,因此p可能指向無效的內存,或者指向內存中的其他一些隨機變量。 如果它指向未按預期使用的“有效”內存,則可能導致崩潰(在無效內存的情況下),錯誤和內存損壞(以及可能的安全錯誤)。 在這種特定的情況下,假設變量存在於堆棧中,它指向變量或函數的返回地址。 無論哪種方式,超出陣列的都是BAD。 非常非常糟糕。 並且編譯器不會阻止您!

另外,順便說一句sizeof不是函數。 它由編譯器在編譯時根據編譯器的符號表進行評估。 因此,無法獲得這樣分配的數組大小:

char* p = malloc(sizeof(char)*100);

編譯器沒有意識到您要分配100個字節,因為malloc是運行時函數。 (實際上,100通常是一個值不斷變化的變量)。 因此sizeof(p)將返回一個指針的大小(如前所述,為4或8),而sizeof(*p)將返回為1的sizeof(char) 。在這種情況下,代碼必須記住多少內存是通過單獨的變量分配的(或以其他方式-動態分配完全是單獨的主題)。

換句話說,sizeof()僅適用於類型和靜態初始化的數組(在代碼中初始化的數組),例如:

char one[] = { 'a' };
char two[] = "b";  // using the string quotes results in a final zero-byte being automatically added.  So this is an array of 2 bytes.
char three[3] = "c"; // the specified size overrides the string size, so this produces an array of 'c', 0, <uninitialized>
char bad[1] = "d"; // trying to put 2 bytes in a 1 byte-bag. This should generate a compiler error.
unsigned char *raw_ptr;
raw_ptr = (unsigned char *)time_ptr;

這將創建一個類型為unsigned char的指針,並使用指向struct tm指針的指針進行初始化(通過強制轉換完成)​​。

但是它如何設法成為一個數組(我認為是字符數組)

time_ptr 程序被告知要查看與time_ptr相同的內存位置,但將其視為unsigned char類型數組。

我認為這可能與以下事實有關:數組被解釋為指向其第0個元素的指針,但我不確定。

數組類型衰減到指針。 是的,數組由指針表示。 但是,指針不必與第0個索引相關聯,但是在首次創建數組時將采用這種方式。

其次,dump_time_struct_bytes函數的輸出(轉儲字節)是什么?

是。 沒有byte類型,因此經常使用charunsigned char

另外,* time_ptr的地址對應什么? 這是結構的開始嗎?

是。

如果后者為true,則輸出中相應的轉儲字節僅屬於其第一個元素(tm_sec)還是屬於整個結構?

整個結構,因為第二個參數的size是使用sizeof(struct tm)初始化的(即,包括該類型的所有字節)。

“ hacky指針”的解釋有點奇怪-為什么取消引用轉換后的整數指針僅顯示結構tm_sec中第一個元素的內容?

似乎第一個數據成員是tm_sec ,它的類型是int 因此,指向struct tm的指針指向用於存儲tm_sec的相同內存。 因此,存儲位置被轉換為int*因為tm_sec的類型為int和我們正在處理的一個指針。 然后它被取消引用以查看該地址的值(當將它視為/被視為int而不是struct tm )。


注意:給定任意4個字節。 什么意思 如果將它們視為無符號的32位整數類型,則會生成某個值。 如果將它們視為32位浮點類型,則可能會產生不同的值。 強制轉換是強制字節的特定“視圖”的方法,而不管字節真正代表什么。

指針struct tm *time_ptr被強制轉換為char * ,這意味着它所指向的內存現在將被視為1字節數據序列。 這是用於指針節流的主要概念,指針的類型決定了指針遞增時將移動多少字節。 由於這是一個char指針,因此將其遞增將僅向前移動一個字節,並且您可以看到正在逐字節打印內存轉儲。

在第二種情況下,指針的類型為(int*) ,指向相同的內存位置,該位置現在會將內存視為sizeof(int)序列(取決於平台,大小可能有所不同)。 在這種情況下,它是4個字節。 現在您可以看到4字節組0x00 00 00 18等於24個十進制。 同樣,0x00 00 00 16等於十進制的22,0x00 00 00 04等於十進制的4。 (在這里要考慮字節序)。

暫無
暫無

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

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