<<C语言深度剖析>>学习笔记之二:关键字详解

来源:互联网 发布:idea java文件 蓝色j 编辑:程序博客网 时间:2024/06/09 22:23

1.auto

   

    编译器在默认的缺省情况下,所有变量都是auto.


2.register


    2-1.这个关键字的作用:

    请求编译器尽可能的将变量存在 CPU 内部寄存器中而不是通过内存寻址访问以提高效率.

其访问速度比内存还要快得多.


    2-2.被这个关键字修饰的特性:

        2-2-1.register 变量必须是能被 CPU 寄存器所接受的类型.意味着 register 变量必须是一个单个的值,并且其长度应小

于或等于整型的长度;

        2-2-2.register 变量可能不存放在内存中. 所以不能用取址运算符“&"来获取 register 变量的地址.


3.static

    在C语言中这个关键字主要有两个作用.

    3-1.修饰变量:

        变量又分为局部和全局变量,被static修饰的变量特性如下:

        3-1-1:存放在内存的静态区域;

        3-1-2:作用域是从定义之处开始,到文件结尾处结束,在定义之处前面的那些代码行也不能使用它,想要使用就得在前面再加 extern;

        3-1-3:静态局部变量,在函数体里面定义的,就只能在这个函数里用了,同一个文档中的其他函数也用不了;

    3-2.修饰函数:

        被static修饰的函数,函数的作用域仅局限于本文件(所以又称内部函数).


4.数据类型

        short、int、long、char、float、double 这六个关键字代表 C 语言里的六种基本数据类型.

    4-1.变量的命名规则:

        规则一:命名应当直观且可以拼读,可望文知意,便于记忆和阅读;

        规则二:当标识符由多个词组成时,每个词的第一个字母大写,其余全部小写;

            如:int CurrentVal;

        规则三:尽量避免名字中出现数字编号,如 Value1,Value2 等;

        规则四:对在多个文件之间共同使用的全局变量或函数要加范围限定符(建议使用模块名
(缩写)作为范围限定符).(GUI_ ,etc);

        规则五:一个函数名禁止被用于其它之处.意指函数名和变量名同一个名字;

        规则六:所有宏定义、枚举常数、只读变量全用大写字母命名,用下划线分割单词;

        规则七:一般来说习惯上用 n,m,i,j,k 等表示 int 类型的变量;c,ch 等表示字符类型变量;a 等表
示数组;p 等表示指针.当然这仅仅是一般习惯,除了 i,j,k 等可以用来表示循环变量外,别
的字符变量名尽量不要使用;

        规则八:定义变量的同时千万千万别忘了初始化.定义变量时编译器并不一定清空了
这块内存,它的值可能是无效的数据.


5.sizeof

    sizeof 的作用是算出一个内存存在对象占用内存的大小.是关键字不是函数.例如下面的语句是编译出错的:

    sizeof int;

    因为编译器没办法知道这两个关键字放一起(typedef除外)是什么意思.

   由于sizeof工作编译中比较常用,下面给出一个示例来解析sizeof的各种用法:

    sizeof.c

   

#include <stdio.h>#include <stdlib.h>void func(int b[100]){    printf("sizeof(b) = %d\n",sizeof(b));}int main(int argc,char **argv){    int *p  = NULL;    int a[100];    printf("sizeof(p) = %d\n",sizeof(p));    printf("sizeof(*p) = %d\n",sizeof(*p));    printf("sizeof(a) = %d\n",sizeof(a));    printf("sizeof(a[100]) = %d\n",sizeof(a[100]));    printf("sizeof(&a) = %d\n",sizeof(&a));    printf("sizeof(&a[0]) = %d\n",sizeof(&a[0]));    func(a);    return 0;}
输出结果如下:
root@seven-laptop:~/learn/C_Program# ./sizeofsizeof(p) = 4sizeof(*p) = 4sizeof(a) = 400sizeof(a[100]) = 4sizeof(&a) = 4sizeof(&a[0]) = 4sizeof(b) = 4root@seven-laptop:~/learn/C_Program# 

    注:想掌握sizeof的用法,深刻理解其定义是王道:sizeof的作用是算出一个内存存在对象占用内存的大小.一定

要明确"内存存在对象"是什么?例如把上述sizeof.c中把所有int 数据类型换成char.代码如下:

    sizeof.c

#include <stdio.h>#include <stdlib.h>void func(char b[100]){    printf("sizeof(b) = %d\n",sizeof(b));}int main(int argc,char **argv){    char *p  = NULL;    char a[100];    printf("sizeof(p) = %d\n",sizeof(p));    printf("sizeof(*p) = %d\n",sizeof(*p));    printf("sizeof(a) = %d\n",sizeof(a));    printf("sizeof(a[100]) = %d\n",sizeof(a[100]));    printf("sizeof(&a) = %d\n",sizeof(&a));    printf("sizeof(&a[0]) = %d\n",sizeof(&a[0]));    func(a);    return 0;}
输出结果如下:

root@seven-laptop:~/learn/C_Program# ./sizeofsizeof(p) = 4sizeof(*p) = 1sizeof(a) = 100sizeof(a[100]) = 1sizeof(&a) = 4sizeof(&a[0]) = 4sizeof(b) = 4root@seven-laptop:~/learn/C_Program# vim sizeof.c
对比输出结果的不同之外有:

sizeof(*p)、sizeof(a)、sizeof(a[100])三个.只要明确sizeof处理的"内存存在对象"就很好理解为什么输出的结果不一样:

*p分别指向int、char;

a分别代表100个整型的数组、100个字符型的数组;

void func(int b[100])是一个比较奇葩的例子.其参数int b[100]可以分开下面两步理解:

首先是一个整型数组(这里可以理解成指针);其次这个数组是有要求的,数组大小是100个元素.


6.signed 和unsigned:

    计算机内部都是以补码来表示存储.

    正数的补码是它本身;

    负数的补码是它的符号位外的所有位取反再加1.例如:

    -7的补码是:11111001;

    下面给出一个示例:

include <stdio.h>#include <stdlib.h>#include <string.h>int main(int argc,char **argv){    char a[1000];    int i;    for(i=0; i<1000; i++)    {        a[i] = -1-i;    }    printf("%d\n",strlen(a));    return 0;}
输出结果:255.

   下面解释这个结果的由来:

    按照负数补码的规则,可以知道-1 的补码为 0xff,-2 的补码为 0xfe......当 i 的值为 127时,

a[127]的值为-128,而-128 是 char 类型数据能表示的最小的负数.当 i 继续增加,a[128]
的值肯定不能是-129.因为这时候发生了溢出,-129 需要 9 位才能存储下来,而 char 类型
数据只有 8 位,所以最高位被丢弃.剩下的 8 位是原来 9 位补码的低 8 位的值,即 0x7f.
当 i 继续增加到 255 的时候,-256 的补码的低 8 位为 0。然后当 i 增加到 256 时,-257 的

补码的低 8 位全为 1,即低八位的补码为 0xff,如此又开始一轮新的循环......

    按照上面的分析,a[0]到 a[254]里面的值都不为 0,而 a[255]的值为 0.strlen 函数是计
算字符串长度的,并不包含字符串最后的‘\0’.而判断一个字符串是否结束的标志就是看
是否遇到‘\0’.如果遇到‘\0’.则认为本字符串结束.

示例二:

include <stdio.h>#include <stdlib.h>#include <string.h>int main(int argc,char **argv){    unsigned i ;    for (i=9;i>=0;i--)    {        printf("%u\n",i);    }    return 0;}
这个程序其实是个死循环.因为i是无符号数据类型.当i=0时条件满足,继续执行,随即变成-1.

但是i是一个无符号数据类型,当其等于-1时,在内存在以补码存储时,变成了一个很大的正数.

因此,"i>=0"依然被满足.

 

7.bool类型

    bool类型定义的变量只有两个逻辑值:要么是"真",要么是"假".实际编程中最常用的就是用bool

类型的变量作为if判断条件.实际的使用最规范的习惯是用取反运算符“!”,这样更能表现其逻辑值

的意义.下面给出示例:

#include <stdio.h>#include <stdlib.h>#include <stdbool.h>#include <string.h>int main(int argc,char **argv){    bool bTestFlag = false;    if(!bTestFlag)    {        printf("The Really World Is False!\n");    }    return 0;}
输出结果:

The Really World Is False!
[注:]GNU C中要使用bool类型,需要包含头文件stdbool.h.


8.float

    float类型变量和double类型变量是有精度要求的.因此这种类型比较时要特别注意.

下面给出一个示例:

#include <stdio.h>#include <stdlib.h>int main(int argc,char **argv){    float fTestVal = 0.001;    printf("fTestVal = %f\n",fTestVal);    if(0.001 == fTestVal)    {        printf("fTestVal = 0.001\n");    }    else    {        printf("fTestVal != 0.001\n");    }    return 0;}
输出结果:

fTestVal = 0.001000fTestVal != 0.001
虽然二者的值在我们日常的数学知识里面是一样,但是在编程里面却不是那么一回事.

另外还要注意一点的就是:不要在很大的浮点数和很小的浮点数之间进行运算.


9.空指针比较的规范写法:

    if(NULL == p); if(NULL != p);

    这样可以避免手误出现少写一个"="号的情况.例如上述的if(NULL==p)如果是写成if(p==NULL)它们

的效果是一样的.但是后者很容易手误写成了if(p=NULL).编译器是不会报错的.


10.空执行语句:

    有时候我们需要在满足一定的条件下执行一个空语句.如下:

    if(!bTestVal);

    其实际等价于:

    if(!bTestVal)

    {

        ;

    }

    if(!bTestVal)由于一时手误很有可能导致后面多了个分号.而导致程序不在我们的预期内执行.类似这样的写法

也很容易让人疑惑if(!bTestVal);.到底if后面的分号是故意的还是无意的.很让人迷惑.如果确实需要用到空语句.建

议写法如下:

    if()

    {

        ;

    }

    或者:

    if()NULL;


10.switch case组合

    case后面只能是常量,可以是整型或字符型的常量,也可以是常量表达式.


11.do、while、for关键字

    11-1.break和continue的区别:

    break:表示终结本层循环,contiune表示结束本轮循环.

    示例: 

#include <stdio.h>#include <stdlib.h>int main(int argc,char **argv){    int i = 0,j = 0;    for(j = 0; j < 3; ++j)    {        for(i = 0; i < 5; ++i)            {                if(3 == i)                    {                        break;//                          continue;                    }                printf("i = %d\t",i);            }        printf("\n");        printf("j = %d\t",j);    }    printf("\n");    return 0;}

   输出结果:

i = 0i = 1i = 2j = 0i = 0i = 1i = 2j = 1i = 0i = 1i = 2j = 2

示例:

#include <stdio.h>#include <stdlib.h>int main(int argc,char **argv){    int i = 0,j = 0;    for(j = 0; j < 3; ++j)    {        for(i = 0; i < 5; ++i)            {                if(3 == i)                    {//                        break;                            continue;                    }                printf("i = %d\t",i);            }        printf("\n");        printf("j = %d\t",j);    }    printf("\n");    return 0;}

输出结果:

i = 0i = 1i = 2i = 4j = 0i = 0i = 1i = 2i = 4j = 1i = 0i = 1i = 2i = 4j = 2
可见,break是跳出本层循环;而continue是结束本轮循环重新继续下一轮循环.


11-2:在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放
在最外层,以减少 CPU 跨切循环层的次数.


11-3:建议 for 语句的循环控制变量的取值采用“半开半闭区间"写法.

        "半开半闭区间"的方式写法如下:

        for (n = 0; n < 10; n++)


11-4:goto关键字

        尽可能不要用goto关键字.


11-5:void关键字

        11-5-1.有容奶大

        在LINUX内核中,有时我们传入的数据是不定的,比如有可能是一个结构体的地址、有可能是一个int型

变量的地址.这时候我们定义的函数参数可以定义为void*类型.再根据具体的传入指针类型再把相应的数据

给拿出来操作.下面给出一个示例:

#include <stdio.h>#include <stdlib.h>typedef struct __sTestVal{    const char *name;    int age;}sTestVal,*psTestVal;void func_test(void *p){    psTestVal q;    q = p;    printf("q->name = %s\n",q->name);    printf("q->age = %d\n",q->age);}int main(int argc,char **argv){    sTestVal student;    student.name = "Seven";    student.age = 24;    func_test(&student);    return 0;}
输出结果:

q->name = Sevenq->age = 24
简析:

        上述代码中func_test()的参数是void*类型.我们可以根据传进来的具体类型再提取数据出来处理.


    11-5-2:如果函数没有返回值一定要加void声明,不能在函数前面什么都没有.比如:

    good_luck();

   要声明成:

    void good_luck();

    如果一个函数声明前面什么都没有修饰词,默认返回值是int型.


    11-5-3:void不能代表一个真实的变量.

    当我们定义一个变量时,编译器是需要为我们这个变量分配内存的.但是如果声明为void类型,那么编译器就不知道

应该为我们这个变量分配多大的内存.如下面的定义是错误的:

    void a;


12.return 关键字.

    这个没啥好说的,主要是返回指针时要特别注意是不是栈区的.如下面的语句是非常危险的:

    char * Func(void)
    {
       char str[30];
       ...
       return str;
    }

    str 属于局部变量,位于栈内存中,在 Func 结束的时候被释放,所以返回 str 将导致错误.


13.const关键字

    const意味着只读变量.它本质上还是变量而不是常量,只不过是只读的.因此,case语句后面不能带

被const修饰的变量,因为case后面只能跟常量.必须在定义的时候初始化,如下面的示例:

#include <stdio.h>#include <stdlib.h>int main(int argc,char **argv){    const int iTestVal;    iTestVal = 100;    printf("iTestVal = %d\n",iTestVal);    return 0;}
编译出错:

root@seven-laptop:~/learn/C_Program# gcc const.c -o constconst.c: In function ‘main’:const.c:8: error: assignment of read-only variable ‘iTestVal’root@seven-laptop:~/learn/C_Program# 

   

    13-1.const与define的区别:

        1).const只有一份拷贝,而define有多份拷贝;

        2).define在预编译阶段进行替换,const修饰的只读变量是在编译的时候确定其值;

        3).define宏没有类型,const修饰的只读变量是有类型的.


    13-2.const到底修饰谁?

    下面给出一个容易的记忆方法:

      先忽略类型名, const离谁近就修饰谁.例如:

const int *p;      //去掉"int",const 修饰*p,p 是指针,*p 是指针指向的对象,不可变int const *p;      //去掉"int",const 修饰*p,p 是指针,*p 是指针指向的对象,不可变int *const p;      //去掉"int",const 修饰 p,p 不可变,p 指向的对象可变const int *const p;//去掉"int",前一个 const 修饰*p,后一个const修饰p,指针p和p指向的对象都不可变

   

    13-3.const修饰函数参数.

    const 修饰符也可以修饰函数的参数,当不希望这个参数值被函数体内意外改变时使用.例如:
    void Fun(const int i);
    告诉编译器 i 在函数体中的不能改变,从而防止了使用者的一些无意的或错误的修改.


    13-4.const 修饰符也可以修饰函数的返回值,返回值不可被改变.


    小结:

        在代码中灵活运用const关键字可以提高代码的效率和可读性.


14.volatile关键字

    volatile 关键字和 const 一样是一种类型修饰符,用它修饰的变量表示可以被某些编译器
未知的因素更改,比如操作系统、硬件或者其它线程等.遇到这个关键字声明的变量,编译器

对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问.

    通过下面两个示例对比volatile关键字的作用.

    示例一:

  int i=10;  int j = i;//(1)语句  int k = i;//(2)语句  这时候编译器对代码进行优化,因为在(1)(2)两条语句中,i没有被用作左值.这时候编译器认为i的值没有发生改变,所以在(1)语句时从内存中取出i的值赋给j之后,这个值并没有被丢掉,而是在(2)语句时继续用这个值给k赋值.编译器不会生成出汇编代码重新从内存里取i的值,这样提高了效率.但要注意:(1)(2)语句之间 i 没有被用作左                             、值才行.

    示例二:

     volatile int i=10;     int j = i;//(3)语句     int k = i;//(4)语句     volatile关键字告诉编译器i是随时可能发生变化的,每次使用它的时候必须从内存中取出 i的值,因而编译器生成的汇编代码会重新从 i 的地址处读取数据放在 k 中.这样看来,如果i是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数据,就容易出错,所以说volatile可以保证对特殊地址的稳定访问.


15.extern关键字

     extern 可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,下面的代码用到的这些

变量或函数是外来的,不是本文件定义的,提示编译器遇到此变量和函数时在其他模块中寻找其定义.


16.struct关键字

    当我们要传输的数据不是简单的字节,我们要用到struct关键字,比如网络协议、LINUX底层开发等;

    当我们函数的参数超过4个时,可以借用结构体来压缩参数个数.因为如果函数的参数多于 4 个使用

起来非常容易出错(包括每个参数的意义和顺序都容易弄错) 效率也会降低,(与具体 CPU 有关,ARM
芯片对于超过 4 个参数的处理就有讲究,具体请参考相关资料).


17.union关键字

      union 维护足够的空间来置放多个数据成员中的“一种”,而不是为每一个数据成员配置
空间.


    17-1.在 union 中所有的数据成员共用一个空间,同一时间只能储存其中一个数据成员,所
有的数据成员具有相同的起始地址.如果同时对union成员赋值,则只有最后的成员有效.例如下面两个

对比示例:

    示例一:

#include <stdio.h>#include <stdlib.h>typedef union __uniontest{    unsigned char ca;    unsigned short sb;    unsigned int ic;}uniontest,*puiontest;int main(int argc,char **argv){    char cTestVal1 = 'I';    char *sTestVal2 = "Love";    char *sTestVal3 = "You";    uniontest unionVal;    unionVal.sb = 520;    unionVal.ic = 20131314;    unionVal.ca = 'c';    printf("%c\n",cTestVal1);    printf("%s\n",sTestVal2);    printf("%s\n",sTestVal3);    printf("Value of unionVal.ca = %c\nAddress of unionVal.ca = %p\n",unionVal.ca,&unionVal.ca);    printf("Value of unionVal.sb = %d\nAddress of unionVal.sb = %p\n",unionVal.sb,&unionVal.sb);    printf("Value of unionVal.ic = %d\nAddress of unionVal.ic = %p\n",unionVal.ic,&unionVal.ic);    return 0;}
输出结果:

root@seven-laptop:~/learn/C_Program# ./union ILoveYouValue of unionVal.ca = cAddress of unionVal.ca = 0xbfab0ac4Value of unionVal.sb = 11619Address of unionVal.sb = 0xbfab0ac4Value of unionVal.ic = 20131171Address of unionVal.ic = 0xbfab0ac4root@seven-laptop:~/learn/C_Program# 

示例二:

#include <stdio.h>#include <stdlib.h>typedef union __uniontest{    unsigned char ca;    unsigned short sb;    unsigned int ic;}uniontest,*puiontest;int main(int argc,char **argv){    char cTestVal1 = 'I';    char *sTestVal2 = "Love";    char *sTestVal3 = "You";    uniontest unionVal;    unionVal.ca = 'c';    unionVal.sb = 520;    unionVal.ic = 20131314;    printf("%c\n",cTestVal1);    printf("%s\n",sTestVal2);    printf("%s\n",sTestVal3);    printf("Value of unionVal.ca = %c\nAddress of unionVal.ca = %p\n",unionVal.ca,&unionVal.ca);    printf("Value of unionVal.sb = %d\nAddress of unionVal.sb = %p\n",unionVal.sb,&unionVal.sb);    printf("Value of unionVal.ic = %d\nAddress of unionVal.ic = %p\n",unionVal.ic,&unionVal.ic);    return 0;}
输出结果:

root@seven-laptop:~/learn/C_Program# ./union ILoveYouValue of unionVal.ca = Address of unionVal.ca = 0xbfe1e624Value of unionVal.sb = 11762Address of unionVal.sb = 0xbfe1e624Value of unionVal.ic = 20131314Address of unionVal.ic = 0xbfe1e624root@seven-laptop:~/learn/C_Program# 
上述两个示例中只是把语句"unionVal.ca = 'c';"换一下位置,输出结果截然不同.
  

    17-2.巧用union来判断系统的大小端

    大端:是指字数据的高字节存放在低地址中,而字数据的低字节则存放在高地址中;

    小端:是指字数据的高字节存放在高地址中,而字数据的低字节则存放在低地址中.

    union 型数据所占的空间等于其最大的成员所占的空间.对 union 型的成员的存取

都是相对于该联合体基地址的偏移量为 0 处开始, 也就是联合体的访问不论对哪个变

量的存取都是从 union 的首地址位置开始.

    也就是说,如果我们定义一个字类型数据,往这个字类型数据的内存地址空间的一个

字节处写入一个值.然后根据这个字节存放在这个字地址的高低地址来判断这个系统

是大端还是小端.代码如下:

#include <stdio.h>#include <stdlib.h>int checkSystem(void){   union check   {    int i;    char ch;    }c;    c.i = 1;    return (1 == c.ch);}int main(int argc,char **argv){    int ret = -1;    ret = checkSystem();    if(1 == ret)    {        printf("Your System Is Little_Endian.\n");    }    else    {        printf("Your System Is Big_Endian.\n");    }    return 0;}

    代码简析:

    变量 i 占 4 个字节,但只有一个字节的值为 1,另外三个字节的值都为 0.如果取出低
地址上的值为 0,毫无疑问,这是大端模式;如果取出低地址上的值为 1,毫无疑问,这是
小端模式.

18.enum关键字

    一般的定义方式如下:

enum enum_type_name{    ENUM_CONST_1,    ENUM_CONST_2,    ...    ENUM_CONST_n} enum_variable_name;

    1).enum_type_name可以看作和int性质一样,是一种数据类型;

    2).enum_type_name是enum_type_name类型的一个变量,只是这个变量的取值作了限定,只能是"{}"括号里面的某个常量;

    3).enum类型中"{}"里面是元素是常量,不能在程序过程在再次赋值;

    4).总的来说,enum枚举类型是针对一些编程中需要处理一些有取值范围的情况,如一个星期有七天,一年有12个月等.对编程

规范起了很大的作用.

    示例:

#include <stdio.h>#include <stdlib.h>typedef enum Week{    SUN,    MON,    TUE,    WED,    THU,    FRI,    STA}WeekVal;int main(int argc,char **argv){    WeekVal enumVal0,enumVal1,enumVal2,enumVal3,enumVal4,enumVal5,enumVal6;    enumVal0 = SUN;    enumVal1 = MON;    enumVal2 = TUE;    enumVal3 = WED;    enumVal4 = THU;    enumVal5 = FRI;    enumVal6 = STA;    printf("WeekVa0 = %d\n",enumVal0);    printf("WeekVa1 = %d\n",enumVal1);    printf("WeekVa2 = %d\n",enumVal2);    printf("WeekVa3 = %d\n",enumVal3);    printf("WeekVa4 = %d\n",enumVal4);    printf("WeekVa5 = %d\n",enumVal5);    printf("WeekVa6 = %d\n",enumVal6);    return 0;}
输出结果:

root@seven-laptop:~/learn/C_Program# ./enum WeekVa0 = 0WeekVa1 = 1WeekVa2 = 2WeekVa3 = 3WeekVa4 = 4WeekVa5 = 5WeekVa6 = 6root@seven-laptop:~/learn/C_Program# vim enum.c 
    [注:]enum是基本数据类型,相当于UNIT,不是结构.enum只是定义了一个常量集合,里面没有"元素".因此,上述代码中

sizeof(WeekVal)是等于4的.


19.typedef关键字

    给一个已经存在的数据类型(注意:是类型不是变量)取一个别名,而非定义一个新的数据类型.语法如下:

typedef struct student{   //code}Stu_st,*Stu_pst;
    Stu_st,*Stu_pst可以理解为一种和int性质一样,都是数据类型.因此,当判断const修饰被typedef修饰的数据类型别名时,

可以像int型一样"抹掉"就可以了.如下面两个语句:

const Stu_pst stu3;Stu_pst const stu4;

均可把Stu_pst"抹掉",显而易见,const均修饰stu3和stu4的地址.

如下面代码是编译不过的:

  1 #include <stdio.h>  2 #include <stdlib.h>  3   4 typedef struct __student  5 {  6     char *name;  7     unsigned int age;  8 }student,*pstudent;  9  10 int main(int argc,char **argv) 11 { 12     pstudent stu1 = (pstudent)malloc(sizeof(student)); 13     pstudent stu2 = (pstudent)malloc(sizeof(student)); 14  15     stu1->name = "Seven"; 16     stu1->age = 24; 17  18     stu2->name = "Melin"; 19     stu2->age = 22; 20  21     const pstudent stutmp1 = stu1; 22     pstudent const stutmp2 = stu2; 23  24     stutmp1->name = "Melin"; 25     stutmp1->age = 22; 26  27     stutmp2->name = "Seven"; 28     stutmp2->age = 24; 29  30     stutmp1 = stu2; 31     stutmp2 = stu1; 32  33     free(stu1); 34     free(stu2); 35     stu1 = NULL; 36     stu2 = NULL; 37  38     return 0; 39 }
编译报错:

root@seven-laptop:~/learn/C_Program# gcc typedef.c -o typedeftypedef.c: In function ‘main’:typedef.c:30: error: assignment of read-only variable ‘stutmp1’typedef.c:31: error: assignment of read-only variable ‘stutmp2’root@seven-laptop:~/learn/C_Program# 

[注:]当计算一个指针指向类型的大小时,这个指针必须有指向对象.如上面语句

"pstudent stu1 = (pstudent)malloc(sizeof(student));"

修改为

"pstudent stu1 = (pstudent)malloc(sizeof(*pstudent));"

编译报错:

typedef.c:12: error: expected expression before ‘pstudent’
其实只要深刻明白sizeof()的定义即可:sizeof是计算一个内存存在对象占用内存的大小.*pstudent指向

的内存存在对象是未知的,自然为难sizeof了.


    19-1.typedef与define的区别:

        1).声明形式不一样,define后面没有分号,而typedef后面要加分号.如下:

#define INT32 inttypedef int int32;
        2).修饰指针不一样,define只是简单的替换,不能像int i,j;那样定义一系列变量.如下:

#define PCHAR char*   PCHAR p3,p4;//p3是指针,p4只是一个普通的字符typedef char* pchar;pchar p1,p2;//p3、p4都是一个指针

原创粉丝点击