先上代码,这是一段CMU上的经典程序,清晰地揭示了数在计算机内部是如何存储的。
我在里面写入了注释以及不带参数的运行结果。
通过该代码,我们能够了解到数在计算机内部的储存方式。
/* show-bytes - prints byte representation of data */
/* $begin show-bytes */
#include <stdio.h>
/* $end show-bytes */
#include <stdlib.h>
#include <string.h>
/* $begin show-bytes */
typedef unsigned char *byte_pointer;
//typedef char *byte_pointer;
//typedef int *byte_pointer;
void show_bytes(byte_pointer start, size_t len) {
size_t i;
for (i = 0; i < len; i++)
printf("%p\t0x%.2x\n", &start[i], start[i]);
printf("\n");
}
void show_int(int x) {
show_bytes((byte_pointer) &x, sizeof(int));
}
void show_float(float x) {
show_bytes((byte_pointer) &x, sizeof(float));
}
void show_pointer(void *x) {
show_bytes((byte_pointer) &x, sizeof(void *));
}
/* $end show-bytes */
/* $begin test-show-bytes */
void test_show_bytes(int val) {
int ival = val;
//float fval = (float) ival;
double fval = (double) ival;
int *pval = &ival;
printf("Stack variable ival = %d\n", ival);
printf("(int)ival:\n");
show_int(ival);
printf("(float)ival:\n");
show_float(fval);
printf("&ival:\n");
show_pointer(pval);
}
/* $end test-show-bytes */
void simple_show_a() {
/* $begin simple-show-a */
int val = 0x87654321;
byte_pointer valp = (byte_pointer) &val;
show_bytes(valp, 1); /* A. */
show_bytes(valp, 2); /* B. */
show_bytes(valp, 3); /* C. */
/* $end simple-show-a */
}
void simple_show_b() {
/* $begin simple-show-b */
int val = 0x12345678;
byte_pointer valp = (byte_pointer) &val;
show_bytes(valp, 1); /* A. */
show_bytes(valp, 2); /* B. */
show_bytes(valp, 3); /* C. */
/* $end simple-show-b */
}
void float_eg() {
int x = 3490593;
float f = (float) x;
printf("For x = %d\n", x);
show_int(x);
show_float(f);
x = 3510593;
f = (float) x;
printf("For x = %d\n", x);
show_int(x);
show_float(f);
}
void string_ueg() {
/* $begin show-ustring */
const char *s = "ABCDEF";
show_bytes((byte_pointer) s, strlen(s));
/* $end show-ustring */
}
void string_leg() {
/* $begin show-lstring */
const char *s = "abcdef";
show_bytes((byte_pointer) s, strlen(s));
/* $end show-lstring */
}
void show_twocomp()
{
/* $begin show-twocomp */
short x = 12345;
short mx = -x;
show_bytes((byte_pointer) &x, sizeof(short));
show_bytes((byte_pointer) &mx, sizeof(short));
/* $end show-twocomp */
}
int main(int argc, char *argv[])
{
int val = 12345;
if (argc > 1) {
val = strtol(argv[1], NULL, 0);
printf("calling test_show_bytes\n");
test_show_bytes(val);
} else {
printf("calling show_twocomp\n");
show_twocomp();
printf("Calling simple_show_a\n");
simple_show_a();
printf("Calling simple_show_b\n");
simple_show_b();
printf("Calling float_eg\n");
float_eg();
printf("Calling string_ueg\n");
string_ueg();
printf("Calling string_leg\n");
string_leg();
}
return 0;
}
/*不带参数的运行结果:
typedef unsigned char *byte_pointer的运行结果
calling show_twocomp
0xbf9a601c 0x39
0xbf9a601d 0x30
0xbf9a601e 0xc7
0xbf9a601f 0xcf
Calling simple_show_a
0xbf9a6018 0x21
0xbf9a6018 0x21
0xbf9a6019 0x43
0xbf9a6018 0x21
0xbf9a6019 0x43
0xbf9a601a 0x65
Calling simple_show_b
0xbf9a6018 0x78
0xbf9a6018 0x78
0xbf9a6019 0x56
0xbf9a6018 0x78
0xbf9a6019 0x56
0xbf9a601a 0x34
Calling float_eg
For x = 3490593
0xbf9a6000 0x21
0xbf9a6001 0x43
0xbf9a6002 0x35
0xbf9a6003 0x00
0xbf9a6000 0x84
0xbf9a6001 0x0c
0xbf9a6002 0x55
0xbf9a6003 0x4a
For x = 3510593
0xbf9a6000 0x41
0xbf9a6001 0x91
0xbf9a6002 0x35
0xbf9a6003 0x00
0xbf9a6000 0x04
0xbf9a6001 0x45
0xbf9a6002 0x56
0xbf9a6003 0x4a
Calling string_ueg
0x8048950 0x41
0x8048951 0x42
0x8048952 0x43
0x8048953 0x44
0x8048954 0x45
0x8048955 0x46
Calling string_leg
0x8048957 0x61
0x8048958 0x62
0x8048959 0x63
0x804895a 0x64
0x804895b 0x65
0x804895c 0x66
改为typedef char *byte_pointer的运行结果
calling show_twocomp
0xbf92b5bc 0x39
0xbf92b5bd 0x30
0xbf92b5be 0xffffffc7
0xbf92b5bf 0xffffffcf
Calling simple_show_a
0xbf92b5b8 0x21
0xbf92b5b8 0x21
0xbf92b5b9 0x43
0xbf92b5b8 0x21
0xbf92b5b9 0x43
0xbf92b5ba 0x65
Calling simple_show_b
0xbf92b5b8 0x78
0xbf92b5b8 0x78
0xbf92b5b9 0x56
0xbf92b5b8 0x78
0xbf92b5b9 0x56
0xbf92b5ba 0x34
Calling float_eg
For x = 3490593
0xbf92b5a0 0x21
0xbf92b5a1 0x43
0xbf92b5a2 0x35
0xbf92b5a3 0x00
0xbf92b5a0 0xffffff84
0xbf92b5a1 0x0c
0xbf92b5a2 0x55
0xbf92b5a3 0x4a
For x = 3510593
0xbf92b5a0 0x41
0xbf92b5a1 0xffffff91
0xbf92b5a2 0x35
0xbf92b5a3 0x00
0xbf92b5a0 0x04
0xbf92b5a1 0x45
0xbf92b5a2 0x56
0xbf92b5a3 0x4a
Calling string_ueg
0x8048950 0x41
0x8048951 0x42
0x8048952 0x43
0x8048953 0x44
0x8048954 0x45
0x8048955 0x46
Calling string_leg
0x8048957 0x61
0x8048958 0x62
0x8048959 0x63
0x804895a 0x64
0x804895b 0x65
0x804895c 0x66
改为typedef int *byte_pointer的运行结果
Calling simple_show_b
0xbfb04d48 0x12345678
0xbfb04d48 0x12345678
0xbfb04d4c 0xbfb04d48
0xbfb04d48 0x12345678
0xbfb04d4c 0xbfb04d48
0xbfb04d50 0x00
Calling float_eg
For x = 3490593
0xbfb04d30 0x354321
0xbfb04d34 0x354321
0xbfb04d38 0x10
0xbfb04d3c 0xb756f940
0xbfb04d30 0x4a550c84
0xbfb04d34 0x354321
0xbfb04d38 0x10
0xbfb04d3c 0xb756f940
For x = 3510593
0xbfb04d30 0x359141
0xbfb04d34 0x359141
0xbfb04d38 0x10
0xbfb04d3c 0xb756f940
0xbfb04d30 0x4a564504
0xbfb04d34 0x359141
0xbfb04d38 0x10
0xbfb04d3c 0xb756f940
Calling string_ueg
0x8048950 0x44434241
0x8048954 0x61004645
0x8048958 0x65646362
0x804895c 0x61630066
0x8048960 0x6e696c6c
0x8048964 0x65742067
Calling string_leg
0x8048957 0x64636261
0x804895b 0x63006665
0x804895f 0x696c6c61
0x8048963 0x7420676e
0x8048967 0x5f747365
0x804896b 0x776f6873
带参数的运行结果:
calling test_show_bytes
Stack variable ival = 1024
(int)ival:
0xbfd06b20 0x00
0xbfd06b21 0x04
0xbfd06b22 0x00
0xbfd06b23 0x00
(float)ival:
0xbfd06b20 0x00
0xbfd06b21 0x00
0xbfd06b22 0x80
0xbfd06b23 0x44
&ival:
0xbfd06b20 0x30
0xbfd06b21 0x6b
0xbfd06b22 0xd0
0xbfd06b23 0xbf
*/
我们可以看到第九、十、十一行有三段类似的代码(如下图),从上述代码后面的注释中可以看到分别用九、十、十一行作为代码的运行结果,并且只有第九行是对的,而十、十一行是错的,我在这里解释一下为什么第九行是对的,而十、十一行是错的。
typedef unsigned char *byte_pointer;
//typedef char *byte_pointer;
//typedef int *byte_pointer;
用unsigned char型指针的正确输出如下
0xbf9a601c 0x39
0xbf9a601d 0x30
0xbf9a601e 0xc7
0xbf9a601f 0xcf
而用char型指针输出结果则为
0xbf92b5bc 0x39
0xbf92b5bd 0x30
0xbf92b5be 0xffffffc7
0xbf92b5bf 0xffffffcf
那么char型指针为什么出现这种错误结果呢?
原因很明显,0xc7,0xcf作为地址,是省略了符号位(前面6个0)的,而将无符号指针变为有符号指针时,符号位为1,因此没有省略,在前面进行符号位扩展,于是变成了0xffffffc7,0xffffffcf。
而用int型指针输出结果为什么会出错呢,这是因为加1后移动的字节数,int型移动4个字节,char型则移动1个字节,因此第十一行是错误的。
在CSAPP的学习中,我了解到了在大端、小端模式下,数据存储方式不同:大端模式下高字节保存在低地址,低字节保存在高地址;小端模式下,低字节保存在低地址,高字节保存在高地址。
由带参数的测试结果可知,该系统为小端模式。