一、C语言的结构

1、Hello world

简单来说,一个C程序就是由若干头文件和函数组成。

#include<stdio.h>    //包含头文件
/*
 *主函数
*/  
int  main(){
    printf("Hello World\n");  
    return 0;
}

#include <stdio.h>就是一条预处理命令,它的作用是通知C语言编译系统在对C程序进行正式编译之前需做一些预处理工作。

函数就是实现代码逻辑的一个小的单元。

注:在最新的C标准中,main函数前的类型为int而不是void。

2、主函数

一个C程序有且只有一个主函数,即main函数。主函数就是C语言中的唯一入口。

3、编写规范

1.一个说明或一个语句占一行,例如:包含头文件、一个可执行语句结束都需要换行;

2.函数体内的语句要有明显缩进,通常以按一下Tab键为一个缩进;

3.括号要成对写,如果需要删除的话也要成对删除;

4.当一句可执行语句结束的时候末尾需要有分号;

5.代码中所有符号均为英文半角符号。

4、注释

如两种注释//单行和 /**/多行。

5、标识符

编程时给变量或者函数起的名字就是标识符,就好比我们慕课网的每一位童鞋都有姓名,姓名就是这位童鞋的标识符。C语言的标识符是不可以随便起名字的,必须遵守一定的规则。

C 语言规定,标识符可以是字母(A~Z,a~z)、数字(0~9)、下划线_组成的字符串,并且第一个字符必须是字母或下划线。在使用标识符时还有注意以下几点:

1.标识符的长度最好不要超过8位,因为在某些版本的C中规定标识符前8位有效,当两个标识符前8位相同时,则被认为是同一个标识符。

2.标识符是严格区分大小写的。例如Imooc和imooc 是两个不同的标识符。

3.标识符最好选择有意义的英文单词组成做到"见名知意",不要使用中文。

4.标识符不能是C语言的关键字。

6、变量输入

#include<stdio.h> 
 
int  main()
{   int price = 0
    printf("金额(元)")
    scanf("%d,price")
    int change = 100-price
    printf("找零%d元\n,change");  
    return 0;
}

7、基本数据类型

数据类型
基本数据类型
整型:char、short、int、long、long long
字符型:char
实型(浮点型)
单精度型:float
双精度型:double、long double
构造数据类型
枚举类型
数组类型
结构类型
共用体类型
指针类型,
空类型
逻辑:bool

表达范围:

sizeof()

类型 16位 32位 64位
char 1 1 1
unsigned char 1
signed char 1
int 2 4 4
unsigned int 2
signed int 2
short int 2 2 2
unsigned short int 2
signed short int 2
long int 4 4 8
signed long int 4
long long int 8 8 8
float 4 4 4
double 8 8 8
long double 8

当运算符两边出现不一致的类型时,会自动转换成较大的类型
无特殊需要:整数选int 浮点选double
sizeof:是静态运算符(结果在编译时刻已确定)——不要在sizeof括号里做运算,这些运算不会进行

类型 字长 范围 有效数字(二进制)
float 32 ±(1.201038——3.401038),0,±inf,nan\pm(1.20*10^{-38} ——3.40*10^{38}),0,\pm inf,nan 7
double 64 ±(1.2010308——3.4010308),0,±inf,nan\pm(1.20*10^{-308} ——3.40*10^{308}),0,\pm inf,nan 15

8、运算符

优先级 运算符
1 ()
2 !、++、+-、--(单目+、-)
3 *、/、%
4 + -
5 <、<=、>、>=
6 == !=
7 &&
8 ||
9 = += -= *= /= %=

逗号运算符是优先级最低的运算符,先算逗号左,逗号右作为运算结果

9、逃逸字符

字符 意义
\b 回退一格
\t 到下一个表格位
\n 换行
\r 回车
" 双引号
' 单引号
\ 反斜杠

10、逻辑运算
结果只有0或1

运算符
!
&&
||

禁止"嵌入式赋值"—— 不易阅读
11、条件运算符
条件?满足:不满足

二、语句

1、判断

#include<stdio.h> 
 
int  main()
{   
    if()
    {
    }
    else
    return 0;
}

例 计算时间差

#include<stdio.h> 
 
int  main()
{   
    int hour1,minute1;
    int hour2,minute2;
    scanf("%d %d",&hour1,&minute1);
    scanf("%d %d",&hour1,&minute2);
    int ih = hour2 - hour1;
    int im = minute2 - minute1;
    if(im<0)
    {im = 6- +im;
    ih --;
    }
    printf("时差%d小时%d分。\n",ih,im);
    return 0;
}

例 找零

#include<stdio.h> 
 
int  main()
{   
    // 初始化
    int price = 0;
    int bill = 0;
    // 输入金额和票面
    printf("输入金额");
    scanf("%d",&price);
    printf("输入票面";
    scanf("%d",&bill);
    if(bill>=price)
    {
        printf("应找%d元", bill-price);
    }
    else
        printf("金额不足");
    return 0;
}

2、嵌套 if else

#include<stdio.h> 
 
int  main()
{   
    if()
    {
    }
    else if()
    {
    }
    ...
    else if()
    {
    }
    else
    
    return 0;
}

3、while 循环

#include<stdio.h> 
 
int  main()
{  
    while()
    {
    }
    return 0;
}

4、do while(必须执行一次) 循环

#include<stdio.h> 
 
int  main()
{  
    do
    {
    }
    while()
    return 0;
}

5、for(固定次数) 循环

#include<stdio.h> 
 
int  main()
{  
    for(;;)
    {
    }
    return 0;
}

6、switch 多路分支

#include<stdio.h> 
 
int  main()
{  
    switch(type)
    {
        case 1:
        break
        case 2:
        break
        
    }
    return 0;
}

7、break continue 跳循环
break 结束所在的一个循环
continue 跳到下一次循环
goto 跳到指定位置

#include<stdio.h>

int main()
{
    int x;
    int one,tow,five;
    int exit = 0
    x = 2;
    for(one = 1;one < x*10;one++)
    {
        for(tow = 1;tow < x*5;tow++)
        {
            for(five = 1;five < x*2;five++)
            {
                if(one + tow*2 + five*5 = x)
                {
                    printf("可以用%d个一角%d个两角%d个五角",one,tow,five);
                    exit = 1;
                    break;                             
                }
            }
            if(exit)
            {
            break
            }
        }
        if(exit)
        {
        break
        }
    }
    return 0;
}

#include<stdio.h>

int main()
{
    int x;
    int one,tow,five;
    int exit = 0
    x = 2;
    for(one = 1;one < x*10;one++)
    {
        for(tow = 1;tow < x*5;tow++)
        {
            for(five = 1;five < x*2;five++)
            {
                if(one + tow*2 + five*5 = x)
                {
                    printf("可以用%d个一角%d个两角%d个五角",one,tow,five);
                    exit = 1;
                    goto out;                             
                }
            }
        }
    }
    out:
    return 0;
}

三、函数

1、函数组成

注:即使没有参数也需要()
void返回类型可以没有return,但不能使用带值returen
可以将函数放到所用函数处的下方,但是使用前必须声明函数原型(函数头+;)
参数:

  1. 字面量
  2. 变量
  3. 函数的返回值
  4. 计算的结果

应注意在类型不匹配时编译器会自动转换,不会报错
传值:传入的实际是参数值,所以函数运行过程中不会改变函数外的参数值,只会return一个值
若需要改变函数外的参数值需要用四、指针
2、本地变量
在函数的独立变量空间中

  1. 函数内部的变量
  2. 参数

规则:

  1. 定义在块内(函数的块内、语句的块内、随便一个大括号的块内)
  2. 进入块前变量不存在,离开块后变量消失
  3. 块外定义的变量在块内依然有效
  4. 内部定义块外同名变量时忽略块外变量
  5. 不能在同一块内定义同名变量
  6. 本地变量不会被默认初始
  7. 参数在进入函数的时候被初始化了
  8. 生存期与作用域都是块

在传统的C中

void f(void); //表示无参数
void f();     //表示未知参数

3、malloc(申请变量空间)

#include<stdilb.h>
void*malloc(size_t size);
(int8)malloc(n*sizeof(int));    //n个int空间
  1. 向malloc申请的空间以字节为单位
  2. 返回的结果是void*,使用时转换为需要类型
  3. 必须free不能重复free
  4. 不要free错地址

4、main函数

#include<stdio.h>

int main(int argc,char const *argv[])
{
    return 0;
}

int argc在命令行所输字符串个数 *argv[]分别指向所有命令行字符串
5、printf:%[flags][width][.prec][hlL]type

flag 含义
- 左对齐
+ 前面放+或-
(space) 正数留空
0 0填充
width或prec 含义
number 最小字符数
* 下一个参数是字符数
.number 小数点后的位数
.* 下一个参数是小数点后的位数
类型修饰 含义
hh 单个字节
h short
l long
ll long long
L long double
类型名称 type
字符串 s
char c
int i,d
unsigned int u
float g,G
float,6 f,F
long ld
double f,e
unsigned u
long long ld
unsigned long long lu
指针 p
八进制 o
十六进制 x
字母大写的十六进制 X
十六进制浮点 a,A
指数 e,E
读入写出个数 n

6、scanf:%[flag]type

表达 flag
跳过 *
最大字符数 数字
char hh
short h
long double l
long long ll
long double L

7、printf和scanf的返回值

  1. 读入的项目数
  2. 输出的字符数

[^.]

四、指针

1、左值

scanf(%p,i);   //由于无&所以是将输入值存到地址i而不是i所代表的地址
int *p;

变量p的值是一个地址
*p = 2;是将2存到 *p(地址)
2、应用
指针一般用在函数,没有指针函数只能输出一个返回值,而利用指针,&i(i的地址)给 *p,可在函数内直接改变%i处(即i存放变量的地址)存的值。即函数需要返回多个值时用指针

  1. 函数直接返回多个值
  2. 函数返回一个任意值加一个判断(相当于函数返回两个值)
  3. 需要传入较大数据时用做参数
  4. 传入数组后对数组操作
    3、const
int *const p1 = &i; // *p 只能指向i(无法修改指针指向的地址)
int const *p2 = &i; // *p 只能指向i(无法修改指针指向的地址)
const int *p3 = &i; //不能用 *p 改变i( *p 指向的地址)的值

4、运算
指针进行运算时每个1代表一个单位(sizeof(指针所指类型))
5、0地址

  1. 不能随意改变0值所以指针不应具有0值
  2. 返回0值的指针为无效指针
  3. 先初始化指针值为0,若最终返回指针值为0,证明未初始到正确地址
  4. NULL表示0地址(存在编译器拒绝用0表示0地址)

6、类型转换
指针也可以强制类型转换

五、数组

1、定义
数组可以看作地址不能被改变的指针const *p
所以当数组传入函数时传入的的是地址,为了保护数组值可以设置参数为const

int sun(const int a[],int length

当数组作为函数的参数时就是指针,此时不能在[]中标识,不能再利用sizeof计算数组元素个数
2、元素个数
数组的每个单元就是数组类型的一个变量
[]内的数字叫做下表或索引
只能使用有效下表值[0,数组的大小-1]

sizeof(a);                 //数组所占字节
sizeof(a[0]);              //a[0](单个元素所占字节)
sizeof(a)/sizeof(a[0]);    //数组元素个数

数组本身不能被赋值,若要赋值必须遍历
3、二维数组
初始化:

  1. 列数必须给出
  2. 每行一个{},逗号分隔
  3. 最后的逗号可以存在,有古老传统(装逼)
  4. 如果省略自动补零
  5. 也可以用定位(* C99 ONLY二级不能用)

六、字符串

1、特征

3、字符串函数string.h
strlen

size_t strlen(const char *s);      //返回s的字符串长度(不包括结尾的0)

strcmp

strcpy

char * strcpy(char *restrict dst, const char *restrict src);    //把src的字符串拷贝给dst

restrict表明dst与src不重叠(C99)
返回dst——为了能链起代码
复制一个字符串

strcpy(dst,src);    //将src所指内存的数据(字符串+\0)给dst

结构

1、枚举
枚举是一种用户自定义的数据类型,他用关键字enum以如下语法声明:

enum 枚举类型名字 {名字0,名字1,。。。,名字n};
enum colors {red,yellow,green};    //red值是0,yellow值是1,green是2

枚举类型名字通常不真正使用,要用的是大括号里的名字,因为它们就是常量符号,他们类型是int,值依次从0到n
当需要一些排列起来的常量值时,定义枚举的意义就是给这些常量名字。

enum COLOR {RED = 1,YELLOW,GREEN = 5};

2、声明结构类型

#include <stdio.h>

int main(int argc, const *argv[])
{
    struct date
    {
        int month;
        int day;
        int year;
    };
    struct date today;

    today.month =   07;
    today.day   =   31;
    today.year  = 2014;
    
    printf("Today's date is %i-%i-%i.\n,
            today.year,today.month,today.day);
    return 0;
}

和本地变量一样,在函数内部声明的结构类型只能在函数内部使用,所以通常在函数外部声明结构类型,这样就可以被多个函数使用
3、声明结构的形式

//1. p1 p2都是point(类型)的结构
struct point
{
    int x;
    int y;
};
struct point p1,p2;
//2. p1 p2都是无名结构
struct
{
int x;
int y;
}p1,p2;
//3. p1 p2都是point结构
struct point 
{
    int x;
    int y;
}p1,p2;
//里面都有p1.x,p1.y两个值

point是一种自建类型,类比int
p1,p2是一种结构,类比变量
4、结构运算
要访问整个结构,直接使用结构变量的名字
对于整个结构,可以做赋值、取地址,也可以传递给函数参数

p1 = (struct point){5,10}. //相当于 p1.x = 5;p1.y = 10
p1 = p2.                   //相当于 p1.x = p2.x p1.y = p2.y

5、结构指针
和数组不同,结构变量的名字并不是结构变量的地址,取地址必须用&运算符

struct data
{
int month;
int day;
int year;
}today;
struct date *pDate = &today.  //*pDate指向struct date类型的结构today
(*p.)month = 12;   //法1
p->month = 12;     //法2

6、嵌套

struct point
{
    int x;
    int y;
};
struct ractangule
{
    struct ponit pt1;
    struct ponit pt2;
};
//若有变量:
struct rectangule r;
//就可以有:
r.pt1.x;
r.pt1.y;
r.pt2.x;
r.pt2.y;

//若有变量定义:
struct rectangle r.*rp
rp = &r
//则下列四种形式等价
r.pt1.x;
rp->pt1.x
(r.pt1).x
(rp->pt1).x

7、Typedef
Typedef语句的最后一个字符串是新名字
typedef与最后一个字符串中间是原先的名字与其他信息
8、联合union

union AnElt
{
    int  i;
    char c;
}elt1,elt2;
elt1.i = 4;
elt2.c = 'a';
elt2.i = 0xDEADBEEF;

9、全局变量与静态本地变量

编译

1、#define(宏)

include是一个编译预处理指令,和宏一样,在编译之前就处理了

把那个文件的全部文本内容原封不动的插入它所在的地方
所以也不是一定要在.c文件的最前面#include

include有两种形式来指出要插入的文件

""要求编译器首先在当前目录(.c所在的目录)寻找这个(""里)文件,若没有,到编译器指定目录寻找
<>让编译器只在指定目录寻找
编译器知道自己的标准库头文件在哪
环境变量和编译器命令行参数也可以指定寻找头文件的目录
在使用和定义这个函数的地方都应该#include这个头文件
一般的做法是任何.c都对应同名.h,将所有对外公开的函数的原型和全局变量的声明都放进去
只有声明可以被放在头文件中,否则会造成一个项目中多个编译单元有重名的实体(某些编译器允许几个编译单元中存在同名的函数,或者用weak修饰符来强调这种存在
重复声明:
一个编译单元中同名的结构不能被重复声明
如果你的头文件里有结构的声明,很难这个头文件不会在编译单元里被#include多次
所以需要"标准头文件结构"
标准头文件结构

#ifndef _LIST_HEAD_
#define _LISH_HEAD_




#endif

运用条件编译和宏保证这个头文件在一个编译单元只会被#include一次

pragme once也能起到相同作用,但有编译器不支持

误区:

int i;          //变量的定义
extern int i;   //变量的声明

4、声明和定义
声明不产生代码

  1. 函数的原型
  2. 变量声明
  3. 结构声明
  4. 宏声明
  5. 枚举声明
  6. 类型声明
  7. inline函数声明

定义产生代码

5、文件输入输出
用 > 和 < 重定向
命令行:> 输出 < 输入
FILE

FILE* fopen(const char *restrict path,const char *restrict mode);   //在stdio.h中已经声明
fscanf(FILE*,...)
fprintf(FILE,...)

//标准代码
FILE* fp = fopen("file","r");
if(fp)
{
    fscanf(fp,...);              //
    fclose(fp);
}
else
{
    printf("无法打开文件");
}

fopen

填入 含义
r 打开只读
r+ 打开读写,从文件头开始
w 打开只写,若不存在新建,存在清空
w+ 打开读写,不存在则新建,存在清空
a 打开追加,不存在则新建,存在则从文件尾开始
..x 只新建,存在不能打开

restrict:

6、二进制读写

size_t fread(void *restrict ptr,size_t size,size_t nitems,FILE *restrict stream);
size_t fwrite(const void *restrict ptr,size_t size,size_t nitems,FILE *restrict stream);

long ftell(FILE *stream);
int fseek(FILE *stream,long offset,int whence);

SEEK_SET  //从头开始
SEEK_CUR  //从当前位置开始
SEEK_END  //从尾开始(倒过来)

注意FILE指针是最后一个参数
返回的是成功读写的字数

底层

1、按位运算、左移右移

符号 名称 含义
& 按位与 全1为1
按位或 全0为0
~ 按位取反 01互变
^ 按位异或 相等为0不等为1
<</>> 左移/右移 不足int以int,忽略正负号bite,不能移负位
右移

2、位段

链表

1、可扩充的数组

/*
* 声明array.h
*/
Array array_create(int init_size);                 //创建数组
void array_free(Array *a);                         //回收空间
int array_size(const Array *a);                    //得到单元数
int *array_at(Array *a,int index);                 //访问某单元
void array_inflate(Array *a,int more_size);        //让数组变大

typedef struct                    //Array结构声明
{
    int *array;                  //指向整个int空间
    int size                     //int空间数量
}Array;

#include"array.h"
#include<stdio.h>
#include<stdlib.h>               //内有malloc函数声明

const BLOCK_SIZE = 20             //每次给一块空间=20

//typedef struct                    //Array结构声明
//{
//    int *array;                  //指向整个int空间
//    int size                     //int空间数量
//}Array;


int main(int argc,char const *argv[])
{
    array a =array_create(100);           //a是有一个指向一个100个int空间的指针一个int=100的结构
    printf("%d\n",array_size(&a));         //输出a指向int空间的数量(int类型)
    *array_at(&a,0) = 10;                   //将10写到a所指的数组的第0个单元
    printf("%d\n",*array_at(&a.0));        //输出a所指的数组的第0个单元的的数字(以int类型)
    int cut = 0;
   
    do{
        scanf("%d",&number);               //输入数据
        if(number != -1)
        {
            *array_at(&a,cnt++) = number   //数据录入数组
        }
        
    }while(number != -1)                   //输入-1时结束录入
    
    array_free(&a);                       //释放a的空间
    return 0;
}

/*
*构建的函数,也可放在array.c文件
*/
Array array_create(int init_size)
{
    Array a;
    a.size  = init_size;
    a.array = (int*)malloc(sizeof(int)*a.size)
}

void array_free(Array *a)
{
    free(a->array);
    a->array = NULL;
    a->size = 0;
}

int array_size(const Array *a)
{
    return a->size;
}

int *array_at(Array *a,int index)
{
    if(index >=  a->size)
    {
        array_inflate(a,(index/BLOCK_SIZE+1)*BLOCK_SIZE-index);
    }
    return &(a->array[index]);             //返回a所指的数组的第index个单元的地址
}

void array_inflate(Array *a,int more_size)
{
    int *p =  (int*)malloc(sizeof(int)*(a->size+more_size))
    int i;
    for(i=0;i<a->size;i++)
    {
        p[i]=a->array[i];
        free(a->array);
        a->array = p;
        a->size += more_size;
                
    }     
}

2、链表

#include"node.h"
#include<stdio.h>
#include<stdlib.h>               //内有malloc函数声明

typedof struct _node
{
    int value;
    struct _node *next;
}Node;
typeof struct _list
{
    Node *head;

}List;    

voide add(List * pList,numer);
void print(List * pList);
void search(List * pList,int number);
void delete(List * pList,int number);
void clean(List * pList);

int main(int argc,char const argv[])
{
    List list;
    int number;
    list->head = NULL;
    do{
        scanf("%d",&number);                         //输入数据
        if(number != -1)
        {
            add(&list,number);
        }
    }while(number != -1)    
    print(&list);
    search(&list,number);
    delete(&list,number);
    clean(&list);
    return 0;
}

//add to linked-list(添加链表新节点)
voide add(List * pList,numer)
{
    Node *p  = (Node*)malloc(sizeof(Node));       //p指向一个新的节点(Node结构)
    p->value = number                         //数据录入到新节点(Node结构的)
    p->next  = NULL;                          //新节点(Node结构)的next(指针)指向0(空)
    //fined the last
    Node last = pList->head;                         //初始化last
    if(last)
    {
        while(last->next)                    //链表中,last指向节点后还有节点,                         
        {
                last = last->next;           //让last指向后一个节点     
        }                                    //last指向的节点内的指针(next)没有指向节点
        //attach
        last->next = p;                                //last指向的节点内的指针(next)指向新节点p(Node结构)
    }
    else
    {
        pList->head = p;
    }
}


void print(List * pList)
{
    Node *p;
    for(p = pList->head;p;p = p->next)
    {
        printf("%d\t",p->value);
    } 
}
void search(List * pList,int number)
{
    Node *p;
    int isFound = 0;
    for(p = pList->head;p;p = p->next)
    {
        if(p->value = number)
        {
            isFound = 1;
            break
        }
    }
    if(isFound)
    {
        printf("找到了")
    }
    else
    {
        printf("没找到")
    }
    
}

void delete(List * pList,int number)
{
    Node *p = NULL;
    Node *q = NULL;
    int isFound = 0;
    for(p = pList->head;p;q = p,p = p->next)
    {
        
        if(p->value = number)
        {
            isFound = 1;
            break
        }
    }
    if(isFound)
    {
        if(q)
        {
            q->next = p->next;
        }
        else
        {
            pList.head = p->next;
        }
        free(p);
        printf("已删除"}
    else
    {
        printf("不存在")
    }
    
}
void clean(List * pList)
{
    Node p = NULL;
    Node q = NULL;
    for(p = pList.head;p;p = q)
    {
        q = p->next;
        free(p);
    }
    
}

2

#include"node.h"
#include<stdio.h>
#include<stdlib.h>               //内有malloc函数声明

typedof struct _node
{
    int value;
    struct _node *next;
}Node;
typeof struct _list
{
    Node *head;
    Node *tail;    
}List;    

void add(List * pList,numer);

int main(int argc,char const argv[])
{
    List list;
    int number;
    list->head = NULL;
    list->tail = NULL;
    do{
        scanf("%d",&number);                         //输入数据
        if(number != -1)
        {
            Add(&list,number);
        }
    }while(number != -1)

}

//add to linked-list(添加链表新节点)
void Add(List * pList,number)
{
    Node *p  = (Node*)malloc(sizeof(Node));   //p指向一个新的节点(Node结构)
    p->value = number                         //数据录入到新节点(Node结构的)
    p->next  = NULL;                          //新节点(Node结构)的next(指针)指向0(空)
    if(list->tail)
    {
        list->tail->next = p;
        list->tail       = p;
    }        
    else
    {
        list->head       = p;
        list->tail       = p;
    }
}