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 | 7 | |
double | 64 | 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
可以将函数放到所用函数处的下方,但是使用前必须声明函数原型(函数头+;)
参数:
应注意在类型不匹配时编译器会自动转换,不会报错
传值:传入的实际是参数值,所以函数运行过程中不会改变函数外的参数值,只会return一个值
若需要改变函数外的参数值需要用四、指针
2、本地变量
在函数的独立变量空间中
规则:
在传统的C中
void f(void); //表示无参数 void f(); //表示未知参数
3、malloc(申请变量空间)
#include<stdilb.h> void*malloc(size_t size); (int8)malloc(n*sizeof(int)); //n个int空间
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、左值
scanf(%p,i); //由于无&所以是将输入值存到地址i而不是i所代表的地址 int *p;
变量p的值是一个地址
*p = 2;是将2存到 *p(地址)
2、应用
指针一般用在函数,没有指针函数只能输出一个返回值,而利用指针,&i(i的地址)给 *p,可在函数内直接改变%i处(即i存放变量的地址)存的值。即函数需要返回多个值时用指针
int *const p1 = &i; // *p 只能指向i(无法修改指针指向的地址) int const *p2 = &i; // *p 只能指向i(无法修改指针指向的地址) const int *p3 = &i; //不能用 *p 改变i( *p 指向的地址)的值
4、运算
指针进行运算时每个1代表一个单位(sizeof(指针所指类型))
5、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、特征
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(宏)
把那个文件的全部文本内容原封不动的插入它所在的地方
所以也不是一定要在.c文件的最前面#include
""要求编译器首先在当前目录(.c所在的目录)寻找这个(""里)文件,若没有,到编译器指定目录寻找
<>让编译器只在指定目录寻找
编译器知道自己的标准库头文件在哪
环境变量和编译器命令行参数也可以指定寻找头文件的目录
在使用和定义这个函数的地方都应该#include这个头文件
一般的做法是任何.c都对应同名.h,将所有对外公开的函数的原型和全局变量的声明都放进去
只有声明可以被放在头文件中,否则会造成一个项目中多个编译单元有重名的实体(某些编译器允许几个编译单元中存在同名的函数,或者用weak修饰符来强调这种存在
重复声明:
一个编译单元中同名的结构不能被重复声明
如果你的头文件里有结构的声明,很难这个头文件不会在编译单元里被#include多次
所以需要"标准头文件结构"
标准头文件结构
#ifndef _LIST_HEAD_ #define _LISH_HEAD_ #endif
运用条件编译和宏保证这个头文件在一个编译单元只会被#include一次
误区:
int i; //变量的定义
extern int i; //变量的声明
4、声明和定义
声明不产生代码
定义产生代码
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); } }
#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; } }