c - 我如何理解该程序的输出?

标签 c arrays pointers struct

我的书试图使我熟悉一些概念,例如有关结构的指针取消引用以及访问结构的一些奇怪方法。我是新手,对以下代码感到困惑。

#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



一世。我了解作者已经将* time_ptr重新声明为指向未签名char的指针,但是它如何设法成为一个数组(我认为是字符数组)?我认为这可能与以下事实有关:数组被解释为指向第0个元素的指针,但我不确定。

ii。其次,dump_time_struct_bytes函数的输出(转储的字节)是什么?我知道多数民众赞成在结构中的字节,但我不知道它们应该如何构成存储在其中的4小时22分24秒(如果完全是这种情况)。另外,* time_ptr的地址对应什么?这是结构的开始吗?如果后者为true,则输出中相应的转储字节仅属于其第一个元素(tm_sec)还是属于整个结构?
对于“ 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。

等等。

请注意,因为在您的特定编译器sizeof(int) == 4中,int *移动了4个字节。 “ int”是一种特殊类型,可以根据您的编译器更改大小。如果您使用其他编译器(例如嵌入式微控制器),则sizeof(int)可能为2,并且整数将打印输出为24、0、22(假定内存块完全相同)。

Is the size of C "int" 2 bytes or 4 bytes?

===回应评论===

“(在其他地方偶然评论了)谢谢您的回答。但是,有一件事似乎还不清楚。假设我有一个指向char'c'的指针。该指针现在是一个指向大小为char的数组的指针吗? 1?

是。一个字节的字节数组。

另外,为验证起见,您已提到指向单个结构的指针是指向大小为1的数组的指针。

是的,但是在这种情况下,数组中单个元素的大小为sizeof(mystruct),可能大于单个字节。

因此,将指针类型转换为指向char的指针将导致该数组的大小现在大于1,并且是一个字节数组,负责十六进制转储。

是。

因此,以这种方式进行类型转换时,是否有任何指针会导致这种字节破坏?

是。这就是字节/内存转储的工作方式。

关于sizeof(type)关键字的另一件事。 sizeof(type)报告type实例的大小(以字节为单位)。 sizeof(variable)等效于sizeof(变量类型)。当变量是指针或数组时,这具有微妙的行为。例如:

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)将返回sizeof(char),即1。在这种情况下,代码必须记住在其中分配了多少内存。一个单独的变量(或以其他方式-动态分配完全是一个单独的主题)。

换句话说,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.

关于c - 我如何理解该程序的输出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37468837/

相关文章:

c++ - 为什么在 C++ 中有这么多不同的方法来使用 new 运算符

C++ 转换 : have pointer to object member, 计算指向对象的指针

c++ - 指针和递归性——我正在努力节省内存

c - 如何在 Visual Studio 中测试我的 .exe 后运行脚本

arrays - 如何在 Perl 中合并散列?

objective-c - 通过代码验证应用程序的签名

PHP foreach 多个值

c# - 如何在不遍历元素的情况下将 int 数组转换为可为 null 的 int 数组?

c - 使用 native 编译器和交叉编译器生成相同的目标文件

c++ - 使用 Win32 API 设置程序描述和公司名称