-9

我正在使用 gcc 并编写了一个小程序,如下所示:

#include<stdio.h>

void doing(){
    char buf[4096 + 1];
    printf("buf %d\n", buf);
    printf("buf %f\n", buf);
    printf("buf %d\n", (unsigned) buf);
    printf("buf %s\n", buf+2);
    printf("buf %d\n", buf+2);

}


int main (void){
    char buf[4096 + 1];
    printf("(unsigned) buf %d\n", (unsigned) buf);
    doing();
    printf("(unsigned) buf %d\n", (unsigned) buf);
    return 0;
}

程序的输出是:

(unsigned) buf 2268303
buf 2264159
buf 0.000000
buf 2264159
buf
buf 2264161
(unsigned) buf 2268303

我不知道为什么将 2268303 打印为整数以及这个 2268303 值来自哪里,一个函数将值作为 2268303 和另一个 2264159

4

4 回答 4

1

请阅读 C 基础知识。你会得到你的答案。

关于,

一个函数给出的值为 2268303 和另一个 2264159

请注意,一个 char buf[] 在 main() 中声明,另一个在 do() 中。两者都有不同的存储空间,并且都将在各自的范围内工作。[读取 C 中的变量范围]

于 2013-04-12T08:09:10.070 回答
1

让我们把它分解一下,看看我们可以对每一行代码说些什么,一个接一个:

int main (void){
    char buf[4096 + 1];

您声明一个char[4097], 通常通过在main输入时调整堆栈指针来分配在堆栈上。数组的内容是不确定的。

    printf("(unsigned) buf %d\n", (unsigned) buf);

数组类型的表达式,除非它是地址 ( &)sizeof_Alignof运算符的操作数,或者是用于初始化字符数组的字符串文字,否则将转换为指向数组第一个元素的指针 (6.3.2.1 p. 3 ),所以这条线相当于

    printf("(unsigned) buf %d\n", (unsigned) &buf[0]);

获取 中第一个字节的地址buf,将其转换为unsigned并将结果数字打印为有符号整数。请注意,如果结果unsigned值不能表示为int. 指针的转换&buf[0]unsigned实现定义的,并且可能会调用未定义的行为(6.3.2.3 p. 6)。通常,sizeof(unsigned)指针值的字节被解释为无符号整数(通常包括指针的大小不小于 的大小unsigned)。在你的情况下,结果是

(unsigned) buf 2268303

被打印出来了。下一个

    doing();

那么让我们看看doing

void doing(){
    char buf[4096 + 1];

另一个char[4097]是声明的,通常是通过在doing进入时调整堆栈指针来分配的。这个数组的内容也是不确定的。

    printf("buf %d\n", buf);

buf同样, type的表达式char[4097]被转换为 a char*,即&buf[0],并且传递给printfwhich 需要一个int参数。类型不匹配会调用未定义的行为,通常sizeof(int)指针值的字节被解释为有符号整数。结果是输出

buf 2264159

这强烈暗示bufindoing已分配 4144 字节远离main's 并且堆栈向下增长。

    printf("buf %f\n", buf);

我们再次进行了数组到指针的转换,现在printf需要一个double参数,但得到一个char*. 更多未定义的行为,表现是

buf 0.000000

被打印。一般无法回答这个问题(毕竟,这是未定义的行为),在 64 位系统上,常见的行为是printf指针或整数类型的参数在通用寄存器中传递,浮点参数在浮点寄存器中,这样printf就可以读取一个浮点寄存器——它恰好包含一个 0 值。

    printf("buf %d\n", (unsigned) buf);

此行与 的对应行具有相同的语义main,但由于它是不同的数组buf,因此转换得到的(无符号)整数是不同的。

buf 2264159

它打印与第一个printfin相同doing,这并不奇怪(但不能保证,因为涉及未定义的行为)。

    printf("buf %s\n", buf+2);

buf转换为&buf[0],然后将 2 添加到其中,得到&buf[2]. 这被传递给printf,由于%s转换,它需要一个指向char作为参数的以 0 结尾的数组的指针。这是printf整个程序中唯一的调用,其中printf第二个参数的类型与转换说明符所期望的类型完全匹配。但是 的内容buf是不确定的,所以如果数组中没有 0 字节,就会导致printf. 然而,显然buf[2]是 0,所以只是

buf 

被打印出来了。

    printf("buf %d\n", buf+2);

buf + 2再次评估为,并且在期望的&buf[2]地方传递。类型不匹配会调用未定义的行为,但输出printfint

buf 2264161

表明没有发生任何恶意事件,并且由于&buf[2]落后 两个字节&buf[0],因此打印的数字比doing's first中打印的数字大两个printf

}

返回main

    printf("(unsigned) buf %d\n", (unsigned) buf);

该行与第一个printf调用相同main,因此它具有相同的语义,如上所述,

(unsigned) buf 2268303

并产生相同的输出。

    return 0;
}
于 2013-04-12T09:19:56.020 回答
0

如果您使用%p,您将获得hex价值。您还需要重新审视范围。也通过这些 链接

(unsigned) buf 2268303 // You have defined this in main its a local array so it has                      
                       //2268303 as address. And for an array the array name acts as a 
                       //pointer too
buf 2264159            // You have defined this in fn doing, it is local to that function     
                       //and has a different address (it is a different array than the one 
                       //in your main)
于 2013-04-12T08:09:09.310 回答
0

这是解释:

这:

printf("buf %d\n", buf);

将打印“buf”数组中第一个元素的地址(内存位置)

这:

printf("buf %f\n", buf);

将尝试从“buf”数组中第一个元素的地址读取浮点数,printf 可能返回零,因为它不知道如何处理它。

这:

printf("buf %d\n", (unsigned) buf);

将与第一个 printf 执行相同的操作,因为“buf”的地址始终大于或等于零。

这:

printf("buf %s\n", buf+2);

将尝试从“buf”数组中第一个元素的地址读取浮点数,printf 可能会返回一个空字符串,因为它不知道如何处理它。

最后:

printf("buf %d\n", buf+2);

返回“buf”数组中第三个元素的位置

需要注意的重要一点: 您声明的两个“buf”数组(一个在 main 中,另一个在 do() 中)是两个不同的数组,即使您给它们提供了相同的名称。在doing() 里面的“buf”指的是你在那里创建的那个,在main 里面的“buf”也指的是你在那里创建的那个。这就是为什么从内部做或主要打印时得到两个不同地址的原因

于 2013-04-12T08:14:30.800 回答