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

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# ./sizeof
sizeof(p) = 4
sizeof(*p) = 4
sizeof(a) = 400
sizeof(a[100]) = 4
sizeof(&a) = 4
sizeof(&a[0]) = 4
sizeof(b) = 4
root@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# ./sizeof
sizeof(p) = 4
sizeof(*p) = 1
sizeof(a) = 100
sizeof(a[100]) = 1
sizeof(&a) = 4
sizeof(&a[0]) = 4
sizeof(b) = 4
root@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.001000
fTestVal != 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 = 0	i = 1	i = 2	
j = 0	i = 0	i = 1	i = 2	
j = 1	i = 0	i = 1	i = 2	
j = 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 = 0	i = 1	i = 2	i = 4	
j = 0	i = 0	i = 1	i = 2	i = 4	
j = 1	i = 0	i = 1	i = 2	i = 4	
j = 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 = Seven
q->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 const
const.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 
I
Love
You
Value of unionVal.ca = c
Address of unionVal.ca = 0xbfab0ac4
Value of unionVal.sb = 11619
Address of unionVal.sb = 0xbfab0ac4
Value of unionVal.ic = 20131171
Address of unionVal.ic = 0xbfab0ac4
root@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 
I
Love
You
Value of unionVal.ca = 
Address of unionVal.ca = 0xbfe1e624
Value of unionVal.sb = 11762
Address of unionVal.sb = 0xbfe1e624
Value of unionVal.ic = 20131314
Address of unionVal.ic = 0xbfe1e624
root@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 = 0
WeekVa1 = 1
WeekVa2 = 2
WeekVa3 = 3
WeekVa4 = 4
WeekVa5 = 5
WeekVa6 = 6
root@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 typedef
typedef.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 int
typedef int int32;
        2).修饰指针不一样,define只是简单的替换,不能像int i,j;那样定义一系列变量.如下:

#define PCHAR char*
   PCHAR p3,p4;//p3是指针,p4只是一个普通的字符

typedef char* pchar;
pchar p1,p2;//p3、p4都是一个指针

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值