概述

一、概述

简介

语言历史:

    机器语言:最早期的低级语言,直接通过0和1来编写程序,可被计算机直接识别

    汇编语言:使用一些符号来替代0和1,同样不容易理解,且依赖于硬件体系

    高级语言:通常使用英文来作为程序设计语言,更加人类思维,且屏蔽了底层复杂的硬件体系结果

C语言:一种面向过程的高级语言,70年代初期由美国的程序员丹尼斯·里奇改写,其程序标准由ANSI定义,最开始是用于替代汇编,来重写unix;

优点:高效率、跨平台、灵活、可操作内存、访问硬件、强大函数库等;且由于c的出现,此后诞生的程序语言大致都参照与c语言的风格;

缺点:指针的风险,面向过程性等.

Hello World

#include<stdio.h>        /* 头文件包含命令、预处理操作 */

int main(int argc, char *argv[]){               /* main函数、程序入口 */

  printf("hello world");      /* 输出语句 */

  getchar();                  /* 缓冲,避免一闪而过 */

  return 0;                  /* 返回值 */

}

 

c语言命名规范:常量命名大写、变量命名有意义且首字母小写、函数首字母大写.

注释:  //     /*  */

标识符:必须以字母或下划线开头、且c语言严格区分大小写. 标识符长度根据不同的编译器也不同.

关键字:共32个、如常见的if  else  return while 都是.

编译过程

编译流程图:

    

 

1,预处理:读取.c源程序,对其中的伪指令(#开头)和特殊符号进行处理变成.i文件,主要包括:

  1. 宏定义指令,宏替换,将程序中所有使用的宏定义变量,替换成对应字符串
  2. 条件编译指令,如#ifdef #elif(用于处理释放编译代码)等进行过滤.
  3. 头文件包含指令,如#include. 可将包含的.h头文件内容加载. 
  4. 特殊符号,如源程序的LINE会被解释成当前行号,FILE则是当前编译的c源程序名称

2,编译:经过预处理得到的.i文件,将只有常量、变量、c语言关键字等定义,在确认语法规则后,将其翻译成汇编代码;

3,汇编:将汇编代码翻译成目标机器指令的过程,通过这一处理得到相应的目标文件;扩展名通常为.o、.obj;

4,链接:即关联所有的目标文件(机器指令)、引用的函数库,并链接,最终形成可执行的文件     

    静态链接:将关联的函数代码,从所在的静态库中拷贝到最终的程序;

    动态链接:关联的函数代码被存放在某个位置中,通过映射地址找到关联的函数库;

 

C编程中文件后缀介绍:

    .c:源代码

    .h:头文件,本质上就是.c源程序,用于为其它c程序提供服务. 避免重复代码编写.

    .i:经过预处理的c源码

    .s .asm:汇编代码

    .o .obj:目标文件,存放了机器代码.

 

GCC编译器

编译器:将高级语言解释成计算机所识别的机器语言指令,最常用为gcc,windows可下载mingw-w64,并将mingw64\bin注册到环境变量中;

 

简单编译(-o):最常用,可直接生成可执行文件,内部包含了预处理、编译、汇编、链接四个阶段

gcc demo.c -o demo

1,预处理:进行预处理,并生成.i文件

gcc -E  demo.c  或者  gcc -E  demo.c  -o  demo.i

 

2,编译:即将预处理后的.i文件,翻译成汇编代码,为.s文件

gcc -S demo.i -o demo.s

 

3,汇编:将汇编代码翻译成最终的目标文件,即机器指令(二进制),通常为.o、.obj

gcc -c demo.s -o demo.o

4,链接:将程序的目标文件和所有关联的文件,相关联,并最终生成可执行文件

gcc demo.o -o demo

 

多个程序文件的编译:开发时通常会直接生成可执行文件

gcc demo1.c demo2.c -o demo

 

检查错误:-pedantic选项可以查看不符合ANSI/ISO C标准的代码      -Werror可以查看错误信息 

    gcc  -pedantic   demo.c  -o  demo

    gcc  -Werror  demo.c  -o demo    

 

库连接:有时候可能会使用除了标准c之外的函数库,那么就需要进行库连接,通常是.so、.lib、.dll

gcc -c -I /usr/dev/mysql/include demo.c -o demo.o

 

强制链接时使用静态链接库:默认情况下,gcc在链接时优先使用动态连接库(通常以.so、.dll结尾),只有动态链接库不存在时,才会考虑静态链接库,可通过-static强制进行

gcc –L /usr/dev/mysql/lib –static –lmysqlclient demo.o –o demo

 

unix/linux静态库链接时搜索路径顺序:

1. ld会去找GCC命令中的参数-L

2. 再找gcc的环境变量LIBRARY_PATH

3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的

 

动态链接时、执行时搜索路径顺序:

1. 编译目标代码时指定的动态库搜索路径

2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径

3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径

4. 默认的动态库搜索路径/lib

5. 默认的动态库搜索路径/usr/lib

有关环境变量:

LIBRARY_PATH:指定程序静态链接库文件搜索路径

LD_LIBRARY_PATH:指定程序动态链接库文件搜索路径

 

二、数据类型/运算符

数据类型

数据类型:规定在内存中开辟空间存储数据的类型. 对存储、获取、解释都有作用.

  • 基本类型: C语言中的基本数据类型、包括所有数值类型、枚举.
  • 构造类型: 就是使用基本类型的数据、进行添加、设计构造出新的数据类型来解决问题. 如struct
  • 指针类型: C语言的精华就是指针、其值代表某个内存地址;
  • 空类型: 关键字是void、主要用于对函数返回的限定、对函数参数的限定.

 

变量:  标识符,用于存储数据  . 指向数据的空间,其值在程序间可被修改  .  
常量:  在程序运行期间其值不可被修改,可使用  const  或  # define  宏定义来实现  .

基本数据类型:

    整型:short   int  long   long-long 

    浮点型(实型):float        double   long-double 

    字符:char 

    布尔:_Bool(在stdboo.h头文件或使用bool)

unsigned:代表无符号,只可代表正数,省略空间. 可表示更大的正数.

signed:带符号,可表示正负数,默认情况下所有基本数据类型都带符号;

说明:

  1. 在c99标准后,可直接使用变量,而无需先声明所有变量、再使用;
  2. int num = 018;   八进制  int num = 0x18;  十六进制
  3. 可对数值常量指定存储类型,例如422L:代表long      232.21f:代表float

e计数法:3.15e7 代表3.15乘以10的7次方.  如为e-7则代表除以10的7次方. 7称为指数.

char:用于存储单个字符,但实际存储的是int字符编码,长度为8位;将'FILE'赋给char,只能存储最后8位,最终变量的值为'E';

字符和字符串:使用单引号''代表一个字符,而使用双引号” ”代表一个字符串,c语言存储字符串时会自动加上\0作为结束标志;c语言中使用字符串是通过char指针和char数组;

typedef:可为任意数据类型起别名,例如:typedef int myint;  通常用于block、struct等

    自动数据类型转换:

        int num1 = 20;           //需要级别兼容性、如:int 转 double

        double  num2 = num1;

    强制数据类型转换:

       float f = 20.3f;     //目标类型必须大于被转换类型,否则可能数据丢失;

       int num = (int) f;

自动类型提升机制:float在传参时被提升为double. char和short会被提升为int类型;

运算符

赋值运算符:=  +=  -=  *=  /=  %=;    //可多重赋值,从右往左开始

 

算术运算符: + - * / % ++  —

    num++:先运算再自增,例如:num++ * 2 == num + 1 * 2

    ++num:先自增后运算,例如:++num * 2 == (num + 1) * 2

 

关系运算符: >   >=       < <=       ==  !=

 

逻辑运算符: &&:与     ||:或      !:非

    双个具有断电功能,即一边不符合条件,则另外一边不会再判断;

 

在c语言中、可用逗号运算符将多个表达式分隔、例如:

    int  value =  2+5,1+2,5+7;      //赋值运算符的优先级比逗号高,2+5赋给了value

 

 

位运算:直接对二进制进行操作的运算符,高效率,在c程序中1:true   0:false

说明:在计算机中,通常0代表false,1代表true;

 

逻辑运算符主要有:&(位与)、|(位或)、~(非)、^(异或)

 

& :位与(AND),两个都是1,返回1,其它返回0;

    

 

|:位或(OR),有一个为1则返回1,2个都为0返回0

    

 

^:异或,即两个比较的位数不同为1,相同为0;(0和1返回1;0和0,1和1返回0)

        

 

~:取反,单目运算符,0变成1,1变成0;

      

 

位移运算:将数值的二进制形式,左移或右移n位. 不足补0.

    <<:左移变大       高位舍弃,低位补0;

    >>:右移变小       低位舍弃,高位补0;

 



快捷算法:

         3  <<  2  =   3 * 2②

         6  >>  2  =   6 / 2②

 

定点数表示法:计算机中有原码、反码、补码来表示;

    原码:使用数值的最高位来代表符号位,0代表正数;1代表负数;

    反码:正数的反码与原码相同;负数的反码等于除了最高位符号外,其它位取反;

    补码:正数的补码与原码相同;负数的补码等于其反码+1;

示例:

       1100110011 原

       1011001100 反,除符号位外,其它按位取反

       1011001101 补,反码+1

 

三、分支/循环

分支:主要用于流程控制,判断用,常见主要为if、switch结构;

   if(表达式){
 
            
   }else if(表达式){
 
            
   }else{
 
   }
 
 
   switch(表达式/变量){ 
            
        case 情况1:
            
            语句块1;
            
                break  ;  
            
            default  ;  
              
            默认情况语句块;  
              
            break;
            
  }

 

三目运算符:表达式 ?结果1:结果2

循环:通常有for、while、do-while三种形式;

   for(初始化;表达式;循环变量){
        
   }
 
 
     while(表达式){
        
     }
 
     do{
       
     }while(表达式);

 

break:退出循环    

continue:跳出本次循环、进入下一次

goto 标识符:无条件转移语句、可使程序立即跳转到函数内部的任意一条可执行语句.可用于退出循环.

    goto show;        //将转到sayhi函数中的show之后代码块执行.

    void  sayhi(){ show:  代码块}



注意:在c语言中1代表true,0代表false. 因此可将数字或变量作为条件表达式.

 

四、数组

一维数组:

数组:主要用于存储一组相同数据类型的数据集合;

声明:

     int arr[5];        //可不指定长度,内存首地址就是数组名或&arr[0]
     char ch[];    

 

初始化:

    ch[0] = 'a';                            //逐个赋值a,ch[1]...
    char ch[5] = {'a','b','c','d','e'}; 
    int  numAttr[] = {5,3,21,12,32};          //静态初始化,自动长度

字符数组:

字符数组:在c语言中,通常使用char[],或char*来存储字符串,且会在末尾追加\0作为结束标记,长度需要+1; 

    char str[] = "hello world";     //可采用字符串方式赋值

    char str[12] = "hello world";      //若指定长度则需要12,因为还有\0

    char str[11] = {'h','e','l','l','o',' ','w','o','r','l','d’};   //不是字符串,不会有\0



字符数组的输入:

    scanf("%s",str);  //读取字符串到数组,首地址就是数组名



字符数组的输出:

   printf("%s",str);     //输出字符数组内容,也可通过&str[0]

   printf("%c",str[0]);  //输出指定字符 



二维数组:

可在声明式进行初始化:

    int  arr[2][3] = {{1,2,3},{4,5,6}};  //使用多个括号分割、建议使用.

    int  arr[2][2] = {1,2,3,4};           //2行2列、自动按照行列顺序进行排列

    赋值: arr[rowIndex][colIndex] = 值;

 

五、函数

自定义函数

说明:是完成特点任务的代码块,将程序分为若干个模块,实现代码复用、可扩展性等.

标准做法:先提供函数原型再实现,但如果函数位于调用者之前,则可以省略声明函数;

//声明函数原型,标准做法需要void
void Eat(void);   
   
//实现Eat函数
void Eat(){          
    printf("this function is eat");
}

 

形参/实参:

形参:形式上的参数、没有值、声明.由外界传入实参赋给形参. 如果不需要参数在函数体内被修改,可加上const修饰.

实参:有实际值的参数、传入方法、赋给形参.

 

值传递/引用传递:

值传递:对于基本数据类型int double等,传参时会将值赋值一份传入. 因此在函数体无论如何修改,都不会影响到原值.

引用传递:如数组等作为参数,则传递的是地址,因此函数体内进行的操作,会对原来数组产生影响.

返回值:函数返回给外界使用的一个值. 

 

局部变量:作用域为定义它的代码块内部.   即{};

全局变量:不属于函数、而属于整个源文件. 如果外部文件需要引用则需要extern修饰

 

内部函数:指函数只在其所在源文件中使用, 又称为静态函数.

    static  返回值  函数名 ([参数列表]);      //加上此关键字即可.

外部函数:可被外部文件调用的函数.

    extern  返回值  函数名([参数列表]);       //默认的、因此可省略extern关键字.

 

递归:函数自己调用本身. 注意每次调用完本身后会返回接着执行;

 

常见系统函数

通常引用c函数库的方式有三种:

  1. 自动访问,常见的函数     

  2. #include文件包含  

  3. 库包含如.so、.dll程序集等

math.h(数学类):

    int abs(int i);      long labs(long l);   double  fabs(double d);

    sin(double d);       cos(double  d);      tan(double  d);  

 

ctype.h(字符类):

    int  isalpha(int  ch);       //检测是否为字母. 不是返回0false、是返回非0-》true
    int  isdigit(int  ch);       //检测是否为数字、是则返回非0。不是返回0
    int  isalnum(int  ch);       //检测是否为字母或数字.
    int  islower(int  ch);       //小写字母
    int  isupper(int  ch);       //大写字母
    int  tolower(int  ch);              //转小写 
    int  toupper(int  ch);              //转大写
    bool isspace(int  ch);      //是否为空白字符

stdlib.h(系统类):

    exit(1);             //用于终止程序

string.h(字符串处理类):

char *strcpy(char* p1,char* p2);   //将p2复制到p1指向的位置(前面). 返回值是p1;
 
char * strcat(char* p1,char*p2);   //将p2追加到p1字符串后面。返回p1
 
//两个字符串从首字母按照编码表开始比较. 0:相等    正数:p1>p2   负数:p1<p2
int  strcmp(char* p1,char* p2);   
 
char *strupr(char* str);      //转大写
 
char *strlwr(char* str);      //转小写
 
int  sizeOf(void * str);      //获取空间(数组,数据类型,指针等)的字节大小.
 
int  strlen(char* str);      //获取字符串长度int  len = strlen(“hello world”);

 

输入/输出函数

简介:主要用于接收用户输入、并返回运算结果的函数

常见转义符:在c、java、php中皆以\反斜杠为转义符,在.net中通常使用@符号;

    \n:换行              \t:制表位         \b:退格              \r:回车

    \\:反斜杠         \’:单引号            \a:警报

字符类:

int putchar(int ch);         //输出单个字符并将其返回;


int getchar();               //从输入队列中获取一个字符. 可读取空白字符;


int  puts(char * str);      //输出字符串并自动换行


char* gets(char * str);     //从键盘录入一行字符串,返回读取后的字符指针.   

 

格式化类:

  • int printf(格式控制,输出列表);   //返回输出的字符个数.
  • int scanf(格式控制,地址列表);    //成功返回读入的字段个数. 错误则返回0.

 

注意:

   1,格式说明符和参数类型必须正确,否则会造成数据错乱

   2,为避免内存溢出,scaf将自动忽略空白字符,并用其分隔相邻的输入项,因此存储字符串时建议使用gets函数;

 

常见格式说明符:

%d:十进制         %0:八进制            %x:十六进制

%hd:短整型        %ld:长整型           %hu:无符号短整型

%f:单/双精度      %lf:长双精度         %.mf:精确到小数点m位

%e:指数          %c:字符              %s:字符串

%p:指针、地址.    %%:输出百分号

指定宽度,并补齐空格:

%m.nf:浮点数长度为m,并精确到小数点n位.

%ms:  长度不足m,则右对齐,左补空格,否则全部输出例:

   printf("this is %10s","love");结果为[     love]

%-ms:同上,但是左对齐,右补空格

%m.ns:表示输出字符占m列、但是只取字符串左侧n个字符、且输出在右侧、左补空格例如:

   printf("%10.3s","love");        结果:      lov

%-m.ns:同上左对其,右补空格. 

printf("asdas"    "zxczx");  //解决一行数据过多的办法. 使用2个双引号即可.

常见标志:

    -:左对齐,可和格式说明符结合使用,左右补空格等. 例如:printf("%-s","isfosdjfo");

    +:正数则显示+号,负数显示-号. 如:printf("%+d",32);    printf("%+d",-32);

    空格:正数显示空格,负数显示-;  如:printf("% d",-32);

    #:转换可选形式.   %0  %x  用于输出八进制或十六进制.

    *:如果不事先指定字段宽度而希望是由程序来决定,可在字符宽度部分使用*来代替. 

数据重定向:可在命令行使用,在unix、linux、ds下还可使用更复杂的运算符;

输入重定向:< 代表输入来自文件,而不是键盘. 作为一个字符流传入. 

输出重定向:> 将其程序的输出到指定文件中.  .

 

缓冲区:getchar、gets、printf等IO函数时,会先存储到缓冲区,当按下回车键后才对程序变为可用,也可\n强制刷新缓冲区;

缓冲分为两种:

    完全缓冲:缓冲区装满时被清空,此种类型通常出现在文件输入输出中. 缓冲区大小取决于系统.

    行缓冲:遇到一个换行字符时清空缓冲区(发送到目的地),例如键盘输入,按下回车键将清空.

        

 

总结:

    1,作为标准i/o包下的getchar scanf函数检测到文件末时候都返回EOF

    2,如果混合使用getchar和scanf,则在调用getchar之前,恰好有scanf在输入队列中留下换行符时,可能产生对程序错误的问题,需要进行判断

main函数解析

main函数是c程序的入口,其可不带参数或带2个参数. 参数个数和字符串数组. 由在古老的命令行系统中传入. 如linux dos下.执行格式:    

文件名字符参数;   多个字符参数使用空格隔开

void main(intargc,char *argv[]){  //默认传入编译后可执行文件路径

       printf("参数个数:%d\n",argc);

       printf("参数名称:%s",*argv);

}

结果:

    

 

 

六、指针

指针入门

指针:就是一种变量类型,专门用于存储内存单元的地址,可通过其访问内存单元,操作数据;

语法:数据类型  *指针名;

赋值:通过&赋予地址,注意指针本身也有地址,同样通过&;

示例:

int num = 99;  
 
int *p;                    //定义一个int类型的指针变量
 
p = &num;                   //指针变量指向了num的地址.
 
printf("%d ",*p);           //输出值. 加上*号代表输出指针指向的值.

 

&:地址运算符,返回数据变量的地址.

*:指针运算符,返回指针变量指向地址的内存单元中存储的值(数据). 

   *p = 123;    //可为指针指向的地址内存中存储的值进行操作.

   *p = *p2;     

&和*优先级相同,因此从右往左执行:

    printf("%d ",&*p);       //*获得指针p指向的值,在通过&获得指向值的地址.

指针自增自减运算:指向下一个内存单元,对于数组而已,会指向下一个元素地址,int(4字节)、short(2字节)、short(直接输出值,而不是地址)

 

指针和数组

简介:数组本质就是一个连续的存储空间,且数组名称就是数组在内存的首地址,即arr = &arr[0],因此可对指针就行自增/自减,来完成迭代数组;

int arr[5] = {1,2,3,4,5};
 
int *p;        //指针变量
 
p = arr;       //获取数组的首地址,也可通过&arr[0]
 
for ( i = 0; i < 5; i++){
   printf("%d",*(p+i));      //*p++ == *(arr+i)  == *(&arr[0]+i) 皆可指向下一个内存单元
}

 

通过两个指针来完成迭代:

   while(start <= end)   或  while(start != end)

 

const和指针:

     1,const int* p1;    //const放在前面,可改变指向的地址,但不可改变指针执行的值

          *p1 = 18;          //错误

          *p1 = &age;         //正确 

    2,int const* p2;    //const放在后面,可改变指向值,但不可改变指针执行的地址

          *p2 = 18;          //正确

          *p1 = &age;         //错误 

    3,const int* const p3 = &age;  //两个const,则即不可改变指针执行地址、也不可改变指针执行的值

指针数组:即数组中存放的元素都是指针,最常见为char字符指针数组,分别存储一个字符串

    int *p_arr[3];      int (*p_arr)[3];
 
    //通过二位字符数组,来存储字符串,不建议使用,大小固定
   char ch[4][10] = {"hello","world","servlet","asp.net”};
 
    //建议使用,每个char*来存储字符串
   char *month[] = {"one","two","three","four"};  

 

指针与字符串

字符串与指针:c语言中没有对字符串的封装,可通过字符数组、字符指针来完成;会在末尾加上\0标识;

    char  str[] = "hello";      //字符数组,可指定长度,用于存储字符串

    char  *p = "world";         //字符指针,指向字符串首个字符地址.

    char * month[] = {"one","two","three","four”};   //字符指针数组

使用:两者使用基本相同,可使用下标,也可当做指针进行操纵:str[1];  p[1]; str++; p++;

函数指针

函数指针:通常作为参数使用,并作为回调函数,需提供函数原型、函数名为首地址;

//1,声明函数原型
void sayHi(string str);    
   
//2,函数指针,将函数名赋予即可,格式:返回值 (*指针名) (参数类型) = 函数名;
void (*p) (string) = sayHi; 
 
//3,调用
p(参数) == (*p) (参数);

 

通常是作为参数,并进回调,格式如下:

     void show(void (*p) (string));

函数指针数组:

    void(*p_arr[5]) (string);

    (*p_arr[index]) (arg);               

示例

示例1:指向指针的指针:

    int num = 0;    
    int* p1 = &num;     
    int** p2 = &p1; //指向一个指针

 

示例2 - 指针变量做函数参数-交换值:

void swap(int *a,int *b){
  int temp = *a;       //交换值操作
  *a = *b;            //*:指针指向地址中存放的数据. 不是操作地址.
  *b = temp;
}
 
void main(){
  int x = 80,y = 90;
  int *p_x = &x, *p_y = &y;       //两个指针分别指向x y的地址
  swap(p_x,p_y);       //传入指针、内部对指向地址的数据进行了交换. 直接影响到了变量 x y
  printf("x = %d\n",x);       //输出
  printf("y = %d\n",y);

 

示例3 - 指针完成冒泡排序:

//通过指针、实现冒泡排序;指针p代表数组的首地址
void orderby(int *p,intlen){
 
    int i,j,temp;
 
    for ( i = 0; i < len-1; i++){
       for ( j = 0; j < len - i -1; j++){
           if(*(p+j) > *(p+j+1)){      //交换值操作、*代表指针地址对应的数据
              temp = *(p+j);
              *(p+j) = *(p+j+1);
              *(p+j+1)  = temp;
           }
       }
    }
 
    for ( i = 0; i < len; i++){
       printf("%d ",*(p+i));    //打印、从首地址开始.
    }
 
}
void main(){
 
    int arr[10] = {5,3,12,54,23,43,15,32,54,67};
    int *p;
    p = arr;          //将首地址赋给指针。等价于:p = &arr[0];
    orderby(p,10);    //排序
 
}

 

七、结构体、共用体、枚举

结构体

结构体:构造类型,用于存储一组不同数据类型的结构;(可理解面向对象中,为轻量级的类)

语法:

struct MyDate{           //定义一个结构
 int year;
 int month; 
 int day;          
}
 
struct MyDate d1;     //创建一个结构变量,并使用
d1.year = 2010;

 

特殊语法:

   1,可在声明结构体时,创建结构体变量

struct MyDate{
 int year;
 int month; 
 int day;          
} d1,d2,d3

 

   2,结构体变量的初始化

struct MyDate d1 = {2010,10,10};  
d1.year = 2010;

 

   3,结构体也可作为数组,例如

  struct MyDate days[3] = {{2010,10,10},{2010,10,10},{2010,10,10}};
  day3[0].year;   //访问方式

 

结构体指针:

     struct MyDate today;     //创建一个结构变量,并赋值
 
     today.year = 2010; 
     today.month = 10; 
     today.day = 1; 
  struct MyDate *p = &today;  //创建一个结构指针,并通过&指向today
 
  p->day;      //通过->指针运算符直接调用
  (*p).day;    //通过*寻址运算符,再通过.调用

 

共用体

说明:使用类似于struct,其关键字为union,为所有共用体变量所共享,当某个共用体变量的成员改变,其它的成员也会改变;

union Person{
  int id;           //注意共用体中成员数据是相同的. 二个进制位
  char name;
};
 
void main(){
    union Person person = {97};       //创建共用体变量,并初始化为id赋值97
 
    printf("%c ",person.name);       //输出a,共用体内成员数据共享的. name == id == 97;
 
    person.name='A';            //将name改成’A’,则id也会变成’A’的ascii码
 4
    printf("%d ",person.id);     //输出65
}

枚举

enum:用于存放一组相关的数据,且每个标识符都对应一个整数,称为枚举常量,使用与struct、union类似,需定义枚举变量

//定义一个枚举
enum  Colors{
    Red,Green,Blue
} c1,c2;         
 
//枚举常量
enum  Colors{
    Red=1,Green=2,Blue=3
};   
 
enum Colors c = Red;         //定义一个枚举变量,其值只能是Red、Green、Blue

 

位段:特殊的结构类型,用于一些特殊场合,其所有成员的长度均是以二进制位为单元定义的,结构中的成员称为位段.

struct CpuStatus{
    unsigned sign:1;     //符号标志
    unsigned zero:1;     //零标志
    unsigned carry:1;    //进位
};



八、预处理

简介:C程序在编译前会先经过一个预处理的过程,用于#inclde包含文件,宏替换,特殊符号的处理等,再变成一个.i文件,预处理指令以#号开头;

#include(包含指令)

简介:包含指令,用于包含另外一个源文件的内容;

< >:系统到存放c函数库头文件所在的目录中查找,标准方式.

" ":先在用户指定目录中寻找,若找不到,再到存放c函数库头文件所在的目录中查找.



.h头文件:本质就是.c源程序,通常存储宏定义、函数原型、常量、typeof、结构、枚举、外部函数等. 之后在其它程序通过#include指令引入使用即可.

#define(宏定义)

简介:宏定义,即字符串常量,仅作替换,不分配内存空间;

语法:  #define  宏名[(参数列表)]  字符串;    //可使用”” ‘’

#define PI 3.141592;        //不带参数的宏
.
#define SUM(a,b) (a+b);         //定义一个带参数的宏,本质就是一个函数
    int x = 10 ,y = 20;       
    printf("%d \n",SUM(x,y))
 
#undef PI;     //终止宏定义

 

ANSI标准说明了5个预定义宏替换名:

    _LINE_:当前被编译代码的行号

    _FILE_:当前源程序的文件名称

    _DATE_:当前源程序的创建日期

    _TIME_:创建时间

    _STDC_:判断当前编译器是否为标准c.    1 = 符合标准c;

 

#if、#elif、#else(条件编译)

条件编译:当某些代码在满足条件下,才进行编译,否则跳过;

#if  表达式1                //此种类似于if else if结构.
  代码块
#elif  表达式2
   代码块
#else
   代码块
#endif
 
#ifdef 宏替换名       //如果定义此宏、则编译。否则不编译
 
    代码块.
 
#endif

#line #error

#line:用于重置由__LINE__和__FILE__报告的行号和文件名. 

#line 1000;             //将当前行号设置为1000

#iine 10 “cool.c”;       //将行号重置为10,文件名称重置为cool.c

    

#error:使预处理发出一个错误消息(异常),之后编译过程中断.

    #error “不支持c99标准”; 

#pragma

#pragma  参数;     //设定编译器状态,完成一些特定操作;

#pragmacomment(lib,"ws2_32.lib")     //在socket编程中需要ws2_32.dll程序集.

 

常见参数:message,能够在编译信息输出窗口中输出相应的信息

         Code_Seg:设置程序中函数代码存放的代码段

         Once:保证头文件被编译一次

         Comment:可导入程序集,动态链接库德国.

 

九、文件操作

FILE

标准I/O包:stdio.h头文件,提供了大量函数用于输入/输出

简介:C程序自动打开3个文件,标准输入,标准输出,标准错误输出.

 

FILE:结构体变量,包含了文件名,状态,位置,大小等信息,通常使用此指针来描述文件信息

typedef struct  {
  short           level;          /* fill/empty level of buffer */
  unsigned        flags;          /* File status flags    */
  char            fd;             /* File descriptor      */
  unsigned char   hold;           /* Ungetc char if no buffer */
  short           bsize;          /* Buffer size          */
  unsigned char   *buffer;        /* Data transfer buffer */
  unsigned char   *curp;          /* Current active pointer */
  unsigned        istemp;         /* Temporary file indicator */
  short           token;          /* Used for validity checking */
} FILE;                           /* This is the FILE object */


FILE  * fp;       //文件指针指向了FILE(结构体变量),可通过其操作文件.


 
文件的打开模式:
  
 
 
EOF:通常用于判断文件末尾,其值为-1;#define定义

打开/关闭文件

   FILE * fp = fopen("c:\\a.txt","r”);   //成功返回FILE指针,失败返回NULL

   fclose(文件指针);                     //关闭成功返回0,失败返回EOF.

文件的读写:

字符:

    fputc(int i,FILE *fp);      //写入字符,成功返回写入字符数,失败返回EOF

    fgetc(FILE *fp);             //读取一个字符,当读到末尾时返回结束标志EOF

    getc(FILE *fp);              //也可实现从指定文件读取一个字符、末尾返回EOF

字符串:

    fputs(char * str,FILE *fp);     //写入字符串,成功返回写入的字符数,失败返回EOF

    fgets(char * str,int len,FILE *fp);  //读取len长度的字符串,到str中. 末尾返回EOF

格式化读写文件

fprintf(文件指针,格式字符串,输出列表);

    fprintf(fp,"%d",i);    //将整型变量i,输出到fp指向文件中.

fscanf(文件指针,格式字符串,输入列表);

    fscanf(fp,"%d",&i);    //从fp中读取一个字符,存到变量i

 

//从fp指向文件读入count次,每次读size字节,存储到buffer地址中.

fread(buffer,size,count,fp);   

//将buffer开始写入count次,每次写size个字节到fp所指文件中.

fwrite(buffer,size,count,fp); 

 

参数说明:

   buffer:一个指针,输出/输入地址

   size:每次读取字节数

   count:读取多少次

   fp:文件指针

 

示例-读取:

    int i;

    char arr[20];

    FILE * fp = fopen("E:\\遇见未知的自己.txt","r++");

    if(fp != NULL){

       for (i = 0; i < 20; i++){

           fread(&arr[i],1,20,fp);//从fp指向文件中读取20次,每次读取1个字节存到字符数组

       }

       fclose(fp);       //关闭资源

    }

    printf("%s",arr);



示例-写入:

 for ( i = 0; i < 20; i++){

     fwrite(&arr[i],20,1,fp); //从fp指向文件中写入20次,每次将arr数组的数据,写入1个字节.

 }

文件定位

fseek(文件指针,位移量,起始点);   //0:文件头   1:当前位置  2:文件末



示例:

    char * str = "hello world";

    fseek(fp,5l,0);             //将文件指针指向距文件头后5个字节的位置. 负数则之前.

    fgets(str,sizeof(str),fp);  //再次进行读取字符串

结果:world

 

int rewind(文件类型指针);     //使位置指针重新指向文件头

long ftell(文件类型指针);    //得到流式文件中的当前位置,相对于文件头的位移量. 当返回-1l则出错



对于文件复制,可有多种方式如fread  fwrite  fseek等皆可.



 

 

十、存储管理  

存储管理

变量的存储类别:从变量的产生时间可分为静态存储和动态存储.

  静态存储:指程序运行前分配的固定存储方式.

  动态存储:指程序运行期间根据需要动态的分配存储空间.



可通过存储修饰符来指定存储类型变量:

  1. auto: 修饰一个局部变量为自动,默认的,每次只需到定义变量的代码行时,会产生并初始化
  2. register:存储在计算器的某个硬件寄存器而不是内存,可提高程序的运行速度,程序员无法获取寄存器变量的地址,不常用;
  3. static:在程序运行期间,其值始终有效,但是限定该全局变量只能在定义它的文件中使用;
  4. extern:外部存储变量、声明此变量由外部定义的,由于一个c程序可能有多个文件、因此可使用extern来提供给其它文件使用.

     extern int numExtern = 888;        //文件1

      //文件2

#include<stdio.h>   

int main(){

    extern int numExtern;        //使用extern来引用外部变量

    printf("%d\n",numExtern);   //此处将输出888

}



简介:static和extern也可作用于函数,且注意c语言中全局变量的定义是有顺序关系的;



外部函数:当前文件的函数允许被其它文件的函数访问、调用,即称为外部函数,默认就是extern

静态函数:又称内部函数,使用static修饰之后,则可定义该函数的文件中使用;

 

程序通常要先装载到计算机的内核与半导体内存中,再运行程序. 程序被组织成4个逻辑段.

   1,可执行代码和静态数据:被存储在固定的内存位置

   2. 动态数据(堆):程序请求动态分配的内存来自内存池,也就是堆.

   3. 栈:程序不会像堆那样动态分配内存,当程序调用函数,或声明变量时,系统会自动分配内存. 是一个先进后出的数据结构;



类型限定词:const

 volatile:告诉编译器该变量除可被程序改变之外,还可被其它代理改变,如硬件等.

 restrict:只可用于指针,表明指针是访问一个数据对象的唯一方式.



动态内存分配

动态内存分配:malloc、calloc、free,需引入 <stdlib.h> ,返回值为 void * 代表通用指针

    //分配一个size大小的空间,返回该内存地址,可赋予指针

    void * malloc(unsignedint size);



    //重新改变一个指针的内存大小,并返回该指针(本质是先释放、再分配)

    void * malloc(void *p , unsignedint size);



    //动态分配n个长度为size的连续内存空间数组,并返回指针,为数组的首地址,错误返回NULL

    void * calloc(unsigned n,unsigned size);

 

  //释放由指针p指向的内存空间,使其内存区域可用,是通过calloc或maloc函数时返回的值

    void free(void *p);



sizeof:返回类型的大小,例如int占4个字节

    size (int);

    size (num); 



示例:

void main(){

     int * pArray;

     int i;

     pArray = (int *) calloc(3,sizeof(int));    //分配3个连续存储的int空间,并且指向首地址

     for ( i = 1; i < 4; i++){

        *pArray = 10*i;             //使用指针对数组赋值

        printf("%d == %d \n",i,*pArray);   //输出

        pArray++;            //移动指针指向下一个存储的数据的位置

      }

}



内存泄露:在使用了malloc、calloc等函数分配内存后,必须使用free函数进行释放,否则可能会因为内存没释放而造成内存泄露,从而导致系统奔溃.

 

posted on 2017-07-19 17:30  janino  阅读(1175)  评论(0)    收藏  举报

导航