国嵌C语言(6-10)

国嵌六:

空结构体的内存:

struct D
{

};
int main(void)
{
    struct D d1;
    struct D d2;
    printf("%d\n",sizeof(struct D));
    printf("%d,%0x\n",sizeof(d1),&d1);
    printf("%d,%0x\n",sizeof(d2),&d2);
}

0

0,28ff20

0,28ff20

空结构体占据多大内存不同的编译器结果不同,最合理的是一个字节;codeblocks为1个字节。

由结构体产生柔性数组:

  • 柔性数组即数组大小待定的数组;例子中的array[]。
  • C语言中的结构体的最后一个元素可以是大小未知的数组。
  • c语言中可以有结构体产生柔性数组。

#include <stdio.h>
typedef struct _soft_array
{
    int len;
    int array[];                                //不知道大小的情况下默认为0
}SoftArray;
int main()
{
    int i = 0;
    //printf("%d\n",sizeof(SoftArray));//所以打印出的结果是4
    SoftArray* sa=(SoftArray*)malloc(sizeof(SoftArray)+sizeof(int)*10);
    sa->len = 10; //指定len后面数组的长度为10
    for(i=0; ilen; i++)
    {
        sa->array[i] = i + 1;
    }
    for(i=0; ilen; i++)
    {
        printf("%d\n", sa->array[i]);
    }
    free(sa);
    return 0;
}

1,2,3,4,5,6,7,8,9,10

斐波拉茨序列(杨辉三角):

  • 创建柔性数组的结构: typedef struct
  • 创建柔性数组: create_soft_array
  • 生成斐波拉茨序列: fac
  • 释放柔性数组; delete_soft_array
#include <stdio.h>
typedef struct _soft_array
{
    int len;
    int array[];
}SoftArray;
SoftArray* creat_soft_array(int size)
{
    SoftArray* ret=NULL;
    if(size>0)
    {
        ret=(SoftArray*)malloc(sizeof(*ret)+sizeof(*(ret->array))*size);
        ret->len=size;
    }
    return ret;
}
void fac(SoftArray* sa)
{
    int i=0;
    if(NULL!=sa)
    {
        if(1==sa->len)
        {
            sa->array[0]=1;
        }
        else 
        {
            for(i=2;i<sa->len;i++)
            {
                sa->array[i]=sa->array[i-1]+sa->array[i-2];
            }
        }
    }
}
void delete_soft_array(SoftArray* sa)
{
    free(sa);
}
int main()
{
    int i=0;
    SoftArray* sa=creat_soft_array(10);
    fac(sa);
    for(i=0;i<sa->len;i++)
    {
        printf("%d\n",sa->array[i]);
    }
    delete_soft_array(sa);
    return 0;
}

1,1,2,3,5,8,13,21,34,55

union与struct的区别:

  • struct中的每个域在内存中都是独立分配空间;
  • union只分配最大域的空间,所有的域共享这个空间;
struct A
{
    int a;
    int b;
    int c;
};
union B
{
    int a;
    int b;
    int c;
};
int main(void)
{
    printf("%d\n",sizeof(struct A));
    printf("%d\n",sizeof(union B));
}

12,4

nuion的使用受系统大小端的影响:

 大端模式:在高地址的地方放低位的值

小端模式:在低地址的地方放低位的值

union C                    //占用四个字节
{
    int i;
    char c;
};
int main()
{
    union C c;
    c.i=1;
    printf("%d\n",c.c);
}

大端模式应该是0;小端模式应该是1;

国嵌七:

枚举类型的使用方法:

  • enum(枚举类型)是一种自定义的类型;
  • enum默认常量在前一个值得基础上依次加1,
  • enum类型的变量只能取定义时的离散值。
#include <stdio.h>
#include <malloc.h>
enum Color
{
    GREEN, //注意标点符号,第一个不给初始值,默认为0
    RED=2,
    BLUE
};
int main(void)
{
    enum Color c=GREEN;
    enum Color b=BLUE;
    printf("%d\n",c);
    printf("%d\n",b);
}

0,3

枚举类型与#define的区别:

  • #define宏常量只是简单的进行值替换,枚举常量是真正意义上的常量。
  • #define宏常量无法被调用,枚举常量可以。
  • #define宏常量无类型信息,枚举常量是一种特定类型的常量。

typedef的意义:

  • typedef用于给一个已经存在的数据类型重命名;
  • typedef并没有产生新的类型;
  • typedef重定义的类型不能进行unsigned和signed扩展。

 typedef与#define的区别:

(1)原理不同

  •  #define是C语言中定义的语法,是预处理指令,在预处理时进行简单而机械的字符串替换,不作正确性检查,只有在编译已被展开的源程序时才会发现可能的错误并报错。
  •  typedef是关键字,在编译时处理,有类型检查功能。它在自己的作用域内给一个已经存在的类型一个别名,但不能在一个函数定义里面使用typedef。

(2)功能不同

  • typedef用来定义类型的别名,起到类型易于记忆的功能。另一个功能是定义机器无关的类型。如定义一个REAL的浮点类型,在目标机器上它可以获得最高的精度:typedef long double REAL, 在不支持long double的机器上,看起来是这样的,typedef double REAL,在不支持double的机器上,是这样的,typedef float REAL
  • #define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。

(3)作用域不同

  • #define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,
  • typedef有自己的作用域。

(4)对指针的操作不同 

#define INTPTR1 int*
typedef int* INTPTR2;
INTPTR1 p1, p2;
INTPTR2 p3, p4;

含义分别为,

声明一个指针变量p1和一个整型变量p2

声明两个指针变量p3、p4

#define INTPTR1 int*
typedef int* INTPTR2;
int a = 1;
int b = 2;
int c = 3;
const INTPTR1 p1 = &a;
const INTPTR2 p2 = &b;
INTPTR2 const p3 = &c;

        const INTPTR1 p1是一个常量指针,即不可以通过p1去修改p1指向的内容,但是p1可以指向其他内容。

        const INTPTR2 p2是一个指针常量,不可使p2再指向其他内容。因为INTPTR2表示一个指针类型,因此用const限定,表示封锁了这个指针类型。

        INTPTR2 const p3是一个指针常量。

国嵌八:

符号的技巧:

 那些注释是对的?

#include <stdio.h>
int main()
{
    int/*...*/i;
    char* s = "abcdefgh //hijklmn";
    //一个右斜杠为换行符
    //Is it a \
    valid comment?
    in/*...*/t i;
    return 0;
}

运行结果:

  • 提示in/*...*/t i;中in未命名;分析:int/*...*/i; 编译器对注释的处理是用空格代替(int i)
  • char* s = "abcdefgh //hijklmn"; 注释符号不能出现在“”之间
  • //Is it a \      valid comment?   一个右斜杠为换行符

注释规则:

  • 编译器会在编译的时候删除掉注释,但不是简单的删除而是用空格代替;
  • 编译器认为双引号括起来的内容都是字符串,//也包括;
  • “/*....*/”型注释不能被嵌套。     

你觉得y=x/*p是什么意思?作者本意:把x除以*p的结果赋值给y。

        编译器:将/*作为一段注释的开始,把/* 后面的内容都当成注释内容,直到*/出现为止。在编译器看来,注释和其它程序元素是平等的,因此,作为工程师不能轻视注释,可以用x/ *p表示(空格的重要性)

  • 注释应该准确易懂,防止二义性,错误的注释有害而无利;
  • 注释是对代码的提示,避免出现臃肿和喧宾夺主;
  • 一目了然的代码避免加注释;
  • 不要用缩写来注释代码,这样可以产生误解;
  • 注释要阐述原因而不是用来描述程序的运行过程。

国嵌九:

接续符和转义符:

#include <stdio.h>
#def\
ine MAX\
255
int main()
{
/\
/这是\
\
注释
i\
n\
t\
 *\
 p\
= \
 NULL;
printf("%0X\n", p);    
return 0;
}

0

C语言中的接续符(\)是指示编译器行为的利器。

接续符的使用:

  • 编译器会将反斜杠剔除,跟在反斜杠后面的字符自动解到前一行;
  • 在接续单词时,反斜杠后不能有空格,反斜杠的下一行之前也不能有空格;
  • 接续符适合在定义宏代码块时使用
#include <stdio.h>
#define SWAP(a,b) \
{                 \
    int temp = a; \
    a = b;        \
    b = temp;     \
}
int main()
{
    int a = 1;
    int b = 2;    
    SWAP(a,b);
    printf("a=%d, b=%d\n", a, b);
    return 0;
}

a=2  b=1

C语言中的转义字符(\)主要用于表示无回显字符,也可以用于表示常规字符:

  • C语言中的反斜杠同时具有接续符he转义字符的作用
  • 当反斜杠作为接续符使用时可以直接出现在程序中,
  • 当反斜杠作为转义符使用时需要出现在字符或者字符串中。

国嵌十:

单引号与双引号:

#include <stdio.h>
int main()
{
    char* p1 = 1 ;
    char* p2 = '1';
    char* p3 = "1";
    printf("%s, %s, %s", p1, p2, p3);
    printf('\n');
    printf("\n");
    return 0;
}

运行结果:提示段错误

分析原因:

  • char* p1 =1 ;表示将p1这个指针空间赋值1,那么p1指向了地址为1的内存空间;
  • char* p2='1';将‘1’对应的ASCLL码(49)赋值给p2指针空间,则p2指向了地址为49的内存空间。这两个地址空间的内存段,都是低地址段,是系统预留的空间,用来拷贝内核代码的使用的,所以上层的应用程序,不能占用这部分空间——段错误
  • char* p3="1";p3是指针空间,分配四个字节大小的空间,用来存放地址值,p3指向的内存空间是char类型的,只分配一个字节空间。char* p3="1";此时p3空间存放的值是字符:‘1’和‘\0’。
  • printf('\n');由printf函数的原型:printf的第一个参数是一个指针类型的形参,那么将一个字符赋值该变量,出现的问题如上述2。

c语言中的单引号和双引号:

  • 单引号:表示字符常量,‘a’表示字符常量;在内存中占一个字节;‘a’+1表示‘a’的ASCLL码+1,结果为‘b’。
  • 双引号:表示字符串常量,“a”表示字符串常量;在内存中占两个字节;“a”+1表示指针运算,结果指向“a”的结束符‘\0’。
#include <stdio.h>
int main()
{
    char c = " ";
    while( c=="\t" || c==" " || c=="\n" )
    {
        scanf("%c", &c);
    }
    return 0;
}

本意:输入\t 空格;输入回车会一直循环while;输入其他字符就会退出while。

运行结果:没有循环

分析原因:

        将字符串赋值给一个字符将发生:字符串存在内存的某个区域,char c = " ";代表的是内存的地址。假如c=0xAABBCCDD,编译器认为是合理的,但是将c转换成c=0xDD;与判断 while( c=="\t" || c==" " || c=="\n" )相比较不会存在相等,即while语句不执行。

更改:

#include <stdio.h>
int main()
{
    char c = ' ';
    while( c=='\t' || c==' ' || c=='\n' )
    {
        scanf("%c", &c);
    }
    return 0;
}

        字符串表示的是字符串的首地址,所以:char abc ="a";实际上abc中存放的是“a”首地址的一个字节的内容(高地址位字节/低地址位字节)。

  • 本质上单括号括起来的一个字符代表整数;
  • 双引号括起来的字符代表一个指针;
  • C编译器接受字符和字符串的比较,可是意义是错误的;
  • C编译器允许字符串对字符变量赋值,可是意义是可笑的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值