繁体   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