嵌入式开发要编的第一个程序:测试各种数据类型字节长度,
心中有数
示例程序
#include <stdio.h>
int main(int argc, char * argv[])
{
printf("sizeof(char) = %d/n", sizeof(char)); /*测试char 类型数据的字节长度*/
printf("sizeof(short) = %d/n", sizeof(short)); /*测试short 类型数据的字节长度*/
printf("sizeof(int) = %d/n", sizeof(int)); /*测试int 类型数据的字节长度*/
printf("sizeof(float) = %d/n", sizeof(float)); /*测试float 类型数据的字节长度*/
printf("sizeof(long) = %d/n", sizeof(long)); /*测试long 类型数据的字节长度*/
printf("sizeof(double) = %d/n", sizeof(double)); /*测试double类型数据的字节长度
return 0;
}
程序解释
作为一个嵌入式开发的工程师,首先要对自己工作的平台的一些特性要有清晰的认识,一种数据类型您开发平台上占多少内存空间一定要清楚,不能想当然,一定要测一下才对,才是一个负责任、合格的嵌入式开发工程师。
通过上面编写的程序我们可以清楚的了解到当前我使用的平台各个数据类型所占字节数,程序很简单,这里就不再对程序作出解释了。嵌入式对空间(内存空间)关注的比较多,不象在一般通用的、非嵌入式平台上对内存不关心,我们这些嵌入式工程师是很在意的。
嵌入式开发要编的第二个程序:数是咋存的呢? 存放顺序问题
示例程序
#include <stdio.h>
int main(int argc, char * argv[])
{ int x = 0x11223344;
char * y = (char *)&x; /* y是指针,指向x变量内存首地址 */
char z; z = x & *y; /* x与*y做位与运算,并赋值给z */
printf("z = 0x%x/n", z); /* 低字节数据存在低地址,小端系统 */
if (z == 0x44) /* 低字节数据存在高地址,大端系统 */
printf("Little Endian/n");
else
printf("Big Endian/n");
return 0;
}
程序解释
现在的计算机多数都8位以上的,那一个32位的数据在计算机内存中的存储通常会有两种情况:
1 低位数据存储在内存的低地址内存单元,我们称之为little endian存储
2 低位数据存储在内存的低地址内存单元,我们称之为big endian存储
以数据0x11223344,这是一个32位4字节数据,对于低位数据0x44,如果在内存中存储在地址单
元低地址单元里,那么我们称当前的计算机是little endian计算机,如下图所示
如果0x44在内存中存储在地址单元高地址单元里,那么我们称当前的计算机是big endian计算机,如下图所示。
在明晰了大端可和小端的概念以后我们再来看看程序是怎么测出您正在使用的系统是大端还是小端?
x = 0x11223344这句语句告诉我们,x是一个整形变量,需要使用四个字节的内存单元,y = &x
这句话的意思是字符指针指向变量x的首地址,由于指针y是char型的指针, *y只能访问0x8048360这
个数据单元里的数据,即只能取出一个字节的数据。假设当前的计算机是小端little endian系统,那么通过 *y 能取回的数据(z = x & *y;)应该是0x44(if (z == 0x44)),那么就可以证明该系统是小端系统,否则就是大端系统。
嵌入式开发要编的第三个程序:内存的存的啥? 存放内容问题
示例程序
#include <stdio.h>
int main(int argc, char * argv[]) { int x = -2; int arr[32] = {0}; int i = 0;
printf("x = %d/n", x); printf("x = 0x%x/n", x);
for(i = 31; i >= 0; i--) { arr[i] = x >> i & 0x1; printf("%d", arr[i]); if (i % 4 == 0) printf(" "); } printf("/n");
return 0; } |
/* arr数组用于记录x每一位的值 */
/* x: 10进制、16进制打印 */
/* x: 2进制打印,呵呵 */
/* 取 x 的每一位上的数 */
/* 每4位输出后打印一个空格分隔 */ | 程序解释
在计算机中,数据是以补码的形式存储的,所以补码在c语言的教学中有比较重要的地位,在学习和理解补码之前我们需要了解三种码:
1 原码
将最高位作为符号位(以0代表正,1代表负),其余各位代表数值本身的绝对值。那我们看看数
0x08000000原码,-0x08000000原码各是多少?
0x08000000 的原码二进制为 0000 1000 0000 0000 0000 0000 0000 0000
-0x08000000 的原码二进制为 1000 1000 0000 0000 0000 0000 0000 0000
特殊问题:
+0的原码为: 0000 0000 0000 0000 0000 0000 0000 0000
-0的原码为: 1000 0000 0000 0000 0000 0000 0000 0000
2 反码
一个数如果为正,则它的反码与原码相同;
一个数如果为负,则符号位为1保持不变,其余各位是对原码取反。
0x08000000 的反码二进制为 0000 1000 0000 0000 0000 0000 0000 0000
-0x08000000 的反码二进制为 1111 0111 1111 1111 1111 1111 1111 1111
(-0x08000000 的原码二进制为 1000 1000 0000 0000 0000 0000 0000 0000)
特殊问题:
+0的反码为: 0000 0000 0000 0000 0000 0000 0000 0000
-0的反码为: 1111 1111 1111 1111 1111 1111 1111 1111
3 补码
一个数如果为正,则它的原码、反码、补码相同;
一个数如果为负,则符号位为 1,其余各位是对原码取反,然后整个数加 1(反码加 1)
0x08000000 的补码二进制为 0000 1000 0000 0000 0000 0000 0000 0000
-0x08000000 的补码二进制为 1111 1000 0000 0000 0000 0000 0000 0000
(-0x08000000 的反码二进制为 1111 0111 1111 1111 1111 1111 1111 1111)
(-0x08000000 的原码二进制为 1000 1000 0000 0000 0000 0000 0000 0000)
特殊问题:
+0的反码为: 0000 0000 0000 0000 0000 0000 0000 0000
-0的反码为: 0000 0000 0000 0000 0000 0000 0000 0000
( 解释
-0 原码 1000 0000 0000 0000 0000 0000 0000 0000 反码 1111 1111 1111 1111 1111 1111 1111 1111 加1 1 0000 0000 0000 0000 0000 0000 0000 0000
第32位的 1 溢出,丢掉,所以-0的补码和+0一致。 )
那好,我们看看-2的补码是什么呢?
-2 的原码 1000 0000 0000 0000 0000 0000 0000 0010
反码 1111 1111 1111 1111 1111 1111 1111 1101
补码 1111 1111 1111 1111 1111 1111 1111 1110
好,基础知识回忆完了,那我们来看看这个程序里的比较难懂的一条语句,
arr[i] = x >> i & 0x1;
我们对其进行解释一下。x = -2 那么 x 所在的内存单元里存储的应该是 -2 的补码(1111
1111 1111 1111 1111 1111 1111 1110), x >> i 的意思是 x 变量右移 i 位。
那么 (x >> i) & 0x01有什么作用呢?
从图可以看出与0x01作位与操作,可以
取出一个数的最后一位, x >> i的作用是将x的第i位上的
数字移到最后。& nbsp;
所以我们可以总结一下,数在计算机里存储的是该数的补码。
嵌入式开发要编的第四个程序:字节对齐问题
示例程序一:
#include <stdio.h>
struct st1
{
int x;
short y;
char z;
};
struct st2
{
short y;
int x;
char z;
};
int main(int argc, char * argv[])
{
printf("sizeof(struct st1) = %d/n", sizeof(struct st1));
printf("sizeof(struct st2) = %d/n", sizeof(struct st2));
return 0; }
程序解释
什么是字节对齐?
为了使CPU能够对变量进行快速的访问,变量的起始地址应该具有某些特性,即所谓的”对齐”. 比
如在32位系统下的的int型变量占 4 个字节,在分配内存时其起始地址应该位于4字节的边界上,即起始
地址能够被4整除。
示例程序二:
#pragma pack(1)
struct st1
{
int x;
short y;
char z;
};
struct st2
{
short y;
int x;
char z;
};
#pragma pack()
int main(int argc, char * argv[])
{
struct st1 s1;
struct st2 s2;
printf("sizeof(struct st1) = %d/n", sizeof(struct st1));
printf("sizeof(struct st2) = %d/n/n", sizeof(struct st2));
printf("st1 s1 Addr 0x%x /n", &s1);
printf("st1 s1.x Addr 0x%x x int/n", &s1.x);
printf("st1 s1.y Addr 0x%x y short/n", &s1.y);
printf("st1 s1.z Addr 0x%x z char/n/n", &s1.z);
printf("st1 s2 Addr 0x%x /n", &s2);
printf("st1 s2.y Addr 0x%x y short/n", &s2.y);
printf("st1 s2.x Addr 0x%x x int /n", &s2.x);
printf("st1 s2.z Addr 0x%x z char/n", &s2.z);
return 0;
}
程序解释
修改默认字节对齐
但是如果采用使用 1字节或者 2 字节对齐,会降低变量访问速度降低。
编辑器默认 4 字节对齐。如果确实需要,可以通过下面的方法来改变缺省的对界条件:
· 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
· 使用伪指令#pragma pack (),取消自定义字节对齐方式。
所以,我们需要在时间(速度)和空间之间进行取舍,如果取消默认的4字节对齐,那么CPU在访
问变量寻址的时候会花去大量的时间去找某些变量在内存中存在那了?如果就使用4字节对齐的方式,
我们或多或少要浪费一些内存空间。
|
|