c语言关键字(下)

1. unsigned 和 signed
首先来看各值的表示范围:
32 位 signed int 类型整数表示法范围:-2^31 ——2^31-1;
8 位 signed char类型数其值表示范围:-2^7—— 2^7-1;
32位 unsigned int 类型数表示法范围:0 —— 2^32-1;
8 位 unsigned char 类型数其值表示法范围:0 —— 2^8-1。

下来看几个例子:

例1:

int main()
{
    char  arr[100];
    int i;
    for (i = 0; i < 1000; i++)
    {
        arr[i] = -1 - i;
    }
    printf("%d", strlen(arr));
    return 0;

}

上面程序的结果是 255,strlen 操作符是计算字符数组的大小,当遇到‘\0’时停止计算,此时 arr 数组的‘\0’在什么位置?首先来分析一下这个数组的存储;
i=0时;a[0]=-1, 在内存里的存储为 1111 1111 1111 1111 1111 1111 1111 1111
i=1时;a[1]=-2, 在内存里的存储为 1111 1111 1111 1111 1111 1111 1111 1110
i=2时;a[2]=-3, 在内存里的存储为 1111 1111 1111 1111 1111 1111 1111 1101
…………………….
i=127时;a[127]=-128, 在内存里的存储为 1111 1111 1111 1111 1111 1111 1000 0000
i=128时;a[128]=-129,在内存里的存储为 1111 1111 1111 1111 1111 1111 0111 1111
……………………..
i=255时;a[255]=-256,在内存里的存储为 1111 1111 1111 1111 1111 1111 0000 0000
i=256时;a[256]=-257, 在内存里的存储为 1111 1111 1111 1111 1111 1111 1111 1111
………………..
从上面几个特殊位置的二进制存储可以看到,i=127时候的值就是最小的负数 -2^7(-128),故这是计算机能存储负数的最大限度,当i继续增加时,就会发生溢出,从而最高位会被丢弃,这时存放的就是原来补码低 8 位的值,当 i 增加到255时,我们看到它的低 8 位全为 0,这就是‘\0’,i 继续增加时,-257 在内存里存储的低 8 位全为1,和 -1 是相同,以此类推,后面的存储 i 每增加255就会重新进行循环……..
从以上的分析可知,‘\0’的位置在 i=255 ,故而数组的长度为 255。

例2:

int main()
{
    int i = -20;
    unsigned j = 10;
    printf("%u", i + j);
    return 0;
}

运程序结果:

这里写图片描述

这里定义了无符号数 j ,那么在计算时,结果被自动转换成十进制无符号整型数;没有转换成无符号类型之前,结果为 -10,此时以无符号类型输出,则不考虑其符号,无符号整型 -10 的结果为 4294967286。

这里写图片描述

例3:

#include <stdio.h>
unsigned char i = 0;
int main()
{
    for (i = 0; i <= 255; i++)
    {
        puts("hello wolrd\n");
    }
    return 0;
}

分析:此运行结果为一个死循环; 由内存里的存储,我们知道unsigned int 在内存里存储的范围是 0~255,在这里,当 i 增加到255,继续增加时,发生溢出,并且又开始新一轮的循环,故而 i<=255 的条件永远成立,形成死循环。

2.1 struct

将一些向关联的数据打包成一个整体,方便使用。

2.1.1 结构体的大小

结构体所占的内存大小是其成员所占内存大小之和;对于结构体成员,可以是标量、数组、指针甚至是其他结构。

struct student
{
}stu;
sizeof(stu)= ?

根据上面的描述,sizeof(stu)的结果应该是0,然而在编译器运行出来的结果是1;对于编译器而言,每一种数据结构都有其大小,故而结构体也是有大小的,即使是一个空的结构体;对于我们数据的类型,最小所占空间为1byte(不考虑内存对齐),故而编译器为结构体至少预留一个byte的大小,所以这里的结果是1而不是0。

2.1.2 初始化

struct COMMPLEX{
float f;
int a[20];
long *lp;
struct SIMPLE s;
struct SIMPLE *P;
};

需要注意的是,一个结构成员的名字可以和其他结构的成员名字相同,这个结构的成员a并不会与 struct SIMPLE s 的成员 a 冲突。

2.1.3 结构体中的柔性数组

typedef struct st_type
{
int i;
int a[];
}type_a;

最后一个元素允许是未知大小的数组称之为柔性数组;但需要注意的是结构中的柔性数组成员前面必须至少包括一个其他成员。此时 sizeof 返回的结构的大小也不包含柔性数组的内存。故而柔性数组不是结构体的正式成员。

2.1.4 内存对齐

引用块内1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8
Linux中的默认值为4
3. 结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

了解以上规则,看几个例子:

例1:

int main()
{
    struct S2
    {
        char c1; //1
        char c2; //1+1
        int i;   //1+1+4
    };
    printf("%d\n", sizeof(struct S2));  //4
    return 0;
}

分析:根据对齐规则,原本三个变量的大小之和为 6,根据对齐规则,要满足结构体中的最大对齐数的整数倍,就需要内存对齐,此时结果为 8

例2:

int main()
{
    struct S1
    {
        char c1;//1
        int i;  //1+3+4
        char c2; //1+3+4+1
    };
    printf("%d\n", sizeof(struct S1));  //12
    return 0;
}

分析:与例1相比,此结构体内部成员位置发生了变化,故而内存对齐也要发生变化。根据对齐规则,原本三个变量的大小之和为 9,根据对齐规则,要满足结构体中的最大对齐数的整数倍,就需要内存对齐,此时结果为 12

例3:

int main()
{
    struct S3
    {
        double d;  //8
        char c;    //1
        int i;     //8+1+3+4=16
    };

    struct S4
    {
        char c1;      //1
        struct S3 s3;  //1+7+16
        double d;      //1+7+16+8
    };
    printf("%d\n", sizeof(struct S4)); //32
    return 0;
}

分析:结构体S4中套了一个结构体S3,根据计算我们得出结构体S3的大小是16,最大对齐数为8,故而在计算结构体S4的大小时,要考虑其内部结构体的最大对齐数以及自身的最大对齐数

2.2 Union

2.2.1 定义

联合也是一种特殊的自定义类型
这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。

2.2.2 类型的声明以及变量的定义

union Un //类型的声明
{
char c;
int i;
};
union Un un; //联合变量的定义

2.2.3 联合的特点

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的成员)

几个例子

例1:

int main()
{
    union Un
    {
        char c;
        int i;
    };
    union Un un;
    printf("%d\n", sizeof(un));
    return 0;
}

分析:联合体又称共用体,故而内部成员共用同一块内存,根据规则,联合体的大小为 4

例2:

int main()
{
    union Un
    {
        char c;
        int i;
    };
    union Un un;

    printf("%p\n", &(un.c));
    printf("%p\n", &(un.c));
    return 0;
}

分析:例 2 的结果是相同的,也就是说联合成员的变量的起始位置是一样的,这更加说明联合体共用同一块空间

2.2.4 大小端存储
联合还有一个作用就是判断当前所用平台的大端和小端存储问题,在这里不在说明,如有疑问,请戳链接大小端存储

2.2.5联合体的大小计算

1)联合的大小至少是最大成员的大小。
2)当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

几个例子

例1:

int main()
{
    union Un1
    {
        char c[5];
        int i;
    };
    union Un2
    {
        short c[7];
        int i;
    };
    printf("%d\n", sizeof(union Un1));
    printf("%d\n", sizeof(union Un2));
    return 0;
}

分析:结果运行出来分别是 8 和 16;首先来看第一个联合体,Un1中的成员有 大小为5的字符数组,整型变量 i。存放两个成员时,数组大小为5,为最大成员,此联合体的最大对齐数为4,显然5不是最大对齐数的整数倍,故而需要对齐到最大对齐数的整数倍,故而联合体Un1的大小为8,同理课算出Un2的大小为16

注:以上所有例子结果均在vs2013测试

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值