C语言写代码步骤:
编辑,编译,执行,调试
关键字:
Include(包含)头文件包含
Int: 整形,整数类型 %d short 短整形 long 长整形 范围不一样
Char 字符 ASCII字符 %c 数字编码表示一个符号 一般8位2进制数 无符号0~255
Float 浮点型 小数 %f %2.10 前2为后10位 float double 范围不一样
return 返回
编译 gcc hello.c -o hello 执行./hello
Printf(); 打印输出 \n 换行 \ 接续符
有符号数和无符号数
有符号
Signed int signed long int signed short int
无符号
Unsigned int unsigned long int unsigned short int
小数没有无符号的
Signed float signed double
Signed char -128-127
Unsigned char 0 ~ 255
常用运算符: + - * / %
复合运算:(最高级,优先运算)
= 赋值运算,与数学完全不同,把右边里面的东西传到左边部分
判断运算符:== 等于 != 不等于 >大于 <小于 >= 大于等于 <=小于等于
A+=b a+b = a
逗号运算符,主要作用是用来分割
++ -- a++ 等于 ++a 等于 a= a+1 等于 a += 1
a++后置,先运算在加一 ++a,前置,先加一在运算
代码风格:空格 空行 注释 文件头 函数头
程序结构一共有三种
顺序结构:按照实物本身特性,必须一个一个接着一个来完成
选择结构:到某个节点后,会根据一次判断结果来决定之后是走哪一个分支
循环结构:循环体,循环一段代码,关键判断循环体执行多少次
If else bool类型,真假
If(bool值1)
{
}else if(bool值2) //开头和结尾只能有一个,中间可以有很多个
Else
{
}
//执行到这一句,变量值已经知道了
Switch case语句执行时,会用该变量的值依次与各个
Case 后的常数去对比,试图找到第一个匹配项。
找到匹配项够,就去执行该case对应的代码段
如果没有找到则继续对比下一个case,直到default
如果前面的case都未匹配,则default匹配
Switch(变量)
{
Case 常数1:
代码段1;
Break;
Case 常数2:
代码段2;
Break;
..........
default:
代码段;
Break;
}
注意:case中必须是常数,而且必须是整形(不能是float double,可以是int char)
一般来说,每个case中代码段后必须有一个break,如果没有,结果可能会让你大吃一惊
Case之后一般都会有default,语法上允许没有default,但写代码时一定要写。
Switch case和if else对比
1.if else适合对比条件比较复杂,但是分支比较少的情况,switch
Case 适合那种对比条件不复杂,但是分支很多的情况
2.所有的选择结构,其实都可以用if else来实现。但是只有部分才可以
用switch case实现。
一般的做法是:在合适使用switch case的情况下会优先使用switch case 如果不适合使用switch case,则不得不使用if else。
循环结构三种:for循环,while循环,do while 循环
for(循环控制变量初始化;循环终止条件:循环控制变量) 三部分不可省略
{
循环体
}
For(a = 1;a<56,a++)
{
}
For循环的执行步骤
- 先进行循环体控制变量初始化
- 执行循环终止条件,如果判断结果为真,则进入第3步,如果为假则循环终止,退出
- 执行循环体
- 执行循环控制变量增量,转入第2步
基础知识:当我们定义了一个局部变量,但是没有初始化的时候,这个值是随机的。
标准的for循环,应该把循环控制变量的初始化,增量都放在()当中,并且在循环体中绝对不应该更改循环控制变量(可以引用他的值,但不应该改变它)。
For(i = 0,sum = 0,i < 10; i++)
{
不能改变i,可以引用
sum += i;
}
While循环
I = 0
While(i<100)
{
Printf(“hello world”);
}
While循环的执行步骤:
- 首先是循环初始化,这一部分其实不属于while循环本身
- 先判断终止条件是否满足,如果真,则进入第2步,否则直接退出
- 执行循环体,然后转入第1步。
I = 0
Do
{
Printf(“hello world”);
I++;
}while(i<20);
Do while循环的执行步骤
- 首先是循环初始化,这一部分其实不属于do while循环本身
- 执行循环体
- 判断终止条件,若成立,则转入2;若不成立立即退出
总结:while循环和do while循环哪里不同?
While循环是先判断后执行,do while循环是先执行后判断,等循环开始返转了,其实是一样的。
While循环有没有可能循环一次都不执行 有
Do while 循环有没有可能循环体一次都不执行 不可能,至少执行一次
总结:不管那种循环结构,都不能缺少一些要素
循环控制条件初始化,终止条件,循环控制变量增量,循环体
C语言基础大模块:数据类型,运算符,三种程序结构,函数,数组,指针,结构体,共用体,枚举
函数
函数是C语言代码的基本组成部分,它是一个小的模块,整个程序由很多个独立的模块(函数)组成,这就是程序设计的基本分化方法。
Main:C语言中所谓的主函数,主函数就是一种特别的函数,特别之处在于,一个C语言程序只能有且必须有一个main函数,C语言规定,一个C语言程序从主函数开始执行,到主函数执行完结束。
Printf:函数的作用是用来在标准输出中打印信息,这个函数不是程序员自己写的,是C语言标准库提供的一个库函数,在C语言中写代码时可以引用库函数,但是必须使用#include引用这个库函数所在的头文件。
写程序也得有目标
写一个计数器,每一个功能用一个函数封装,函数定义=函数实现,然后声明,才能被调用。
使用函数来写程序时关键部分:
函数定义:函数定义是关键,是这个函数的实现,函数定义中包含了函数体,函数体中的代码段决定了这个函数的功能。
函数声明:函数声明实际上是叫函数原型声明,什么叫原型?函数的原型包含三部分,函数名,返回类型,函数参数列表。通俗讲,函数原型就是这个函数叫什么,接收什么类型的几个参数,返回一个什么样的返回值。
函数声明的作用,在于告诉使用函数的人,这个函数使用时应该传递给他什么样的参数,它会返回什么类型的返回值,这些东西都是写函数的人在函数定义中规定好的,如果使用函数的人不参照这个原型来使用,就会出错,结果就会和你想的不一样。
函数调用:函数调用就是使用函数名来调用函数完成功能,调用时必须参照函数原型给函数传参,然后从函数得到适当的返回值作为结果。
函数的参数
形参:形式参数的简称。在函数定义和函数声明中的参数列表中的参数,都是形参
实参:实际参数的简称。函数调用中,实际传递的参数才是实参。
函数调用的过程,其实就是实参传递给形参的一个过程,这个过程传递实际是一次拷贝。实际参数的时候,实参(本质是一个变量)本身并没有进入到函数内,而是把自己的值复制了一份传给了函数中的形参,在函数中参与运算,这种传参方法,就叫传值调用。
返回值:(关键字 return)
当函数执行完成之后,会给调用该函数的地方返回一个值,这个值的类型就是函数声明中返回类型,这个值就是函数体中最后一句return xxx;返回的那个值。
函数名,变量名
第一点:起名字时候不能随意,要遵守规则,这个规则有两个层次,第一层是合法,第二层合理。合法就是符号C语言中变量名的命名规则,合理就是变量名起的好,人一看就知道什么意思,一看就知道这个函数是干嘛的,而且优美,好记。
第二点:C语言中,所有的符号都是区分大小写的。
数组
到目前为止,我们已经学习了C语言的基本数据类型:整形,浮点型,字符型,再往后就是复合数据类型。
所谓复合数据类型,是指由简单数据类型,经过一定的数据结构封装,组成而成的新的数据类型。譬如数组,譬如结构体,譬如共用体
为什么需要数组?
数组就是数组成一个组,数就是一个特定的数据类型的变量,组就是说好多个数放在一起。
怎么定义数组?
Int a[4] ; 数组中元素类型 数组名【数组元素个数】;
总结:数组中的所有元素必须是同一种数据类型,不可能在一个数组中存储两种数据类型的数
怎么使用数组?
数组定义的时候作为整体定义。但是使用的时候不能作为整体使用,使用时必须拆开使用数组中的各个元素。
譬如数组int a【4】,使用其中的四个元素,分别用a【0】...a【3】,其中【】是数组的标志,【】中的数字叫做数组下标(index,索引),下标是我们访问数组中各个元素的指引,下标是0代表数组中第一个元素,下标是1代表数组第二个元素。如果数组长度为n,下标中最后一个是n-1,访问数组时要特别注意下标,下标是从0开始的,如果下标超出n-1,会产生越界访问,结果是不可预期的。
数组的初始化问题
初始化(Init),是为了让对象有一个预定的初始化状态
譬如说:
- 变量的初始化
当一个局部变量定义时没有初始化,它的值是随机的。这个如果没有注意,可能会导致程序出错,怎么办,解决方案有两个;
第一个,在定义过后明确给他赋值,使用=运算符
第二个,定义该变量时,同时进行初始化。
总结:一般来讲,只要你记得显示赋值,则两种方式并无优劣差异。但是人会犯错的,会不小心,所以还是定义同时初始化好一点,因为这个定义的时候就有了固定值,即使之后忘记显示赋值也不会造成结果是随机的。
一般情况下,定义的同时都将变量初始化为0,局部变量定义同时初始化为0,这是一个写代码的好习惯。
- 数组的初始化
第一种:完全初始化,依次赋值 int a【3】 = 【0,1,2】
第二种:不完全初始化,初始化式中值从a【0】开始,依次向后赋值,不足的默认用0 int a【3】={};或{0};
不同数据类型数组
Int a[5]; //整形数组
Float a[5]; //浮点数组
Double a[5]; //双精度浮点数组
Char a[5]; //字符数组
程序在环境运行时,需要一定的资源支持,这些资源包括,cpu(运算能力),内存等,这些资源一般由运行时环境(一般是操作系统)来提供,譬如我们在Linux系统上./a.out运行程序时,Linux系统为我们提供了运算能力和内存。
程序越大,运行时消耗的资源越多,譬如内存占用,越大的程序,占用的内存越多,占用内存的其中之一,就是我们在程序中定义变量。
c语言程序中,变量的实质就是内存中的一个格子,当我们定义(创造一个变量)
就相当于在内存中得到了一个格子,这个格子的名字就是变量名,以后访问这个内存格子就使用该变量名就行了,这就是变量的本质。
数据类型的实质是内存中格子的不同种类,譬如整形格子(类型是int),单精度浮点型格子(float),双精度浮点型格子(double),字符型格字(char)。
二进制,二进制位,字节,8个二进制位
Sizeof运算符
作用:返回一个变量或者一个数据类型的内存占用长度,以字节为单位。
Sizeof(a)/sizeof(a[0])测试一个数组中究竟有多少个元素
数据类型的实质是内存中格子的不同种类,譬如在32位机器上
短整形格子(short) 占用2字节空间16位
整形格子(int) 占用4字节空间32位
单精度浮点型(float) 占用4字节
双精度浮点型 (double) 占用8字节空间 64位
字符型格子(char) 占用1字节空间 8位
字符数组及它的两种初始化
Char a[5];
基础知识:
- 在C语言中引出一个单个字符时,应该用单引号‘’括起来
- 定义数组同时初始化,则可以省略数组定义时[]中的长度,C语言编译器会自动推论其长度,推论依据是初始化式中初始化元素的个数。由此可知,省略[]中数组元素个数只有一种情况,那就是后面的初始化式必须为完全初始化。
- 在C语言中引出一个字符串时,应该用“”括起来,譬如“abcde”
“abcde”实际上有6个字符,分别是‘a’‘b’‘c’‘d’‘e’‘\0’
‘\0’这个字符是ASCII码表中的第一个字符,它的编码值是0,对应的字符是空字符(不可见字符,在屏幕上看不见,没法显示,一般要用转义字符方式来显示。譬如‘\n’表示回车符,‘\0’是C语言中定义的字符串的结尾标志)。于是乎变成了6个字符。
指针
%p 打印指针指向的值
Int main(void)
{
Int a = 23;
Int *p; //定义了一个int型指针变量P
P = &a; //p = (&a)取地址
*p = 111;
Printf(“a = %d.\n”,a);
a = 111;
}
&:取地址符,将它加在某个变量前面,则组合后的符号代表这个变量的地址值
A 代表变量a本身
P 代表指针变量p本身
&a 代表a的地址
*p 代表指针变量p所指向的那个变量,也就是变量A
*,指针符号,指针符号在指针定义和指针操作的时候,解析方法是不同的。
Int *p; 定义指针变量p,这里的*p含义不是代表指针变量p所指向的那个变量,在定义时这里的*含义是告诉编译器p是一个指针。
指针全称是指针变量,其实质是C语言的一种变量,这种变量比较特殊,通常它的值会被默认为某个变量的地址值(p = &a),然后我们访问变量a,不必只通过a这个变量名来访问,而可以通过p = &a,*P = xxx,这样的方式来间接访问变量。
指针的定义和初始化
指针既然是一种变量,那么肯定也可以定义,也可以初始化
第一种:先定义在赋值
Int *p;
P = &a;
第二种:定义的同时初始化
Int *p = &a;
各种不同类型的指针
指针变量本质上是一个变量,指针变量的类型属于指针类型。
Int *p;定义了一个指针类型的变量p,这个p所指向的那个变量是int型。
Char *p; p是指针变量,指向的变量是char类型
Float *P;
Double *p;
各种指针类型和它们所指向的变量类型必须匹配,否则结果不可须知。
指针定义的两种理解方法:
Int *P
第一种:首先看到p,这个是变量名,其次,p前面有个*,说明这个变量p是一个指针变量;最后,*P前面有一个int,说明这个指针变量p所指向的是一个int型数据。
Cahr *(*(*pfunc[]))(char *,char*)类似的复杂表达式,可以用相同的分析方法得到
第二种:首先看到p,这个是变量名,其次,看到p前面的int *,把int *作为一个整体来理解,int *是一种类型(复合类型),该类型表示一种指向int型数据的指针。
总结:第二种方法便于理解,但是不够本质,建议用第一种方法来理解,因为这种思维过程可以帮我们理解更复杂的表达式。
指针与数组的初步结合
数组名:做右值时,数组名表示数组的首元素地址,因此可以直接赋值给指针。
如果有int a[5];
则a和&a[0]都表示数组首元素a[0]的首地址。
注意:数组首元素的首地址和数组的首地址是不同的。前者是数组元素的地址,而后者是数组整体的地址,两个东西的含义不同,但是数值上是相同的。
根据以上,我们知道可以用一个指针指向数组的第一个元素,这样就可以用间接访问的方式去逐个访问数组中各个元素。这样访问数组就有了两种方式。
有int a[5]; int *P; P = a;
数组的方式依次访问:a[0] a[1] a[2] a[3] a[4]
指针的方式依次访问:*P *(P+1) *(P+2) *(P+3) *(P+4)
指针与++ -- 符号进行运算
指针本身也是一种变量,因此也可以进行运算,但是因为指针变量本身存在的是某个其他变量的地址值,因此该值进行* /
%等运算是无意义的,两个指针变量相加本身也无意义,相减有意义。指针变量+1,-1是有意义的。+1就代表指针所指向的格子向后挪一格,-1代表指针所指向的格子向前挪一格。
*P++ 就相当于*(P++),P先与++结合,然后P++整体再与*结合
*P++解析,++先跟P结合,但是因为++后置的时候,本身含义就是先运算后增加1(运算指的是P++整体与前面的*进行运算;增加1指的是P+1),所以实际上*P++符号整体对外表现的值是*P的值,运算完成之后再加1
所以*P++等同于*P P+=1;
*++P等同于 P += 1; *P;
(*P)++,使用()强制将*与P结合,只能先计算*P,然后对*P整体的值++。
++(*P),先*P取值,再前置++,该值+1后作为整个表达式的值。
总结:++符号和指针结合,总共有以上4种情况。--与++的情况很类似。
函数传参中使用指针
Int add(int a,int b)函数传参使用了int型数,本身是数值类型。实际调用该函数时,实参将自己拷贝一份,并将拷贝传递给形参进行运算,实参自己实际是不参与的。
所以,在函数中,是没法改变实参本身的。
结构体,共用体,枚举,宏定义,预处理
结构体
为什么需要结构体?什么是结构体?
没有结构体之前,C语言的数据依靠:变量+数组。最初最简单的时候,只需要使用基本数据类型(int char float double)来定义单个变量,需要几个变量就定义几个。
后来情况变复杂了,有时需要很多意义相关的变量(譬如需要存储及运算一个班级的学生分数)这时候数组出现了。数组解决了需要很多类型相同,意义相关的变量的问题。
但是数组是有限制的。数组最大的不足在于,一个数组只能存储很多个数据类型相同的变量。
所以碰到需要封装几个类型不同的变量的时候,数组就无能为力。
譬如对应题目:使用一个数据结构来保存一个学生的所有信息:姓名 学号 性别
这个时候就需要结构体
什么是结构体
结构体其实就是一个集合,这个集合里面包含很多个元素,这些元素的数据类型可以相同,也可以不相同,所以结构体是一种数据封装的方法。结构体存在的意义就在于把很多数据类型不相同的变量封装在一起,组成一个大的新的数据类型。
数据结构:把庞大复杂的数据用一定的方式组织起来,便于操作(查找,增加,删除等)这就叫数据结构。
结构体与数组的关联:数组是一种特殊的结构体,特殊之处在于封装内的各个元素类型是相同的。结构体和数组都是对一些元素封装,因此定义的时候都是封装作为整体定义,但是使用的时候,都是使用封装中的子元素。一般结构体变量和数组变量都不会作为一个整体操作。
使用结构体的步骤:
第一步:定义结构体类型。结构体类型的定义是在函数外面(函数外面 == 全局)的
第二步:使用第一步定义的类型定义结构体变量。
第三步:使用变量,实际上使用结构体变量的时候,使用的是结构体变量中封装的各个子元素,而不是结构体变量本身。
结构体的初始化
结构体变量和普通变量一样,作为局部变量时,如果定义的时候无初始化也无显示赋值,则结构体变量中的子元素的值是随机的。
发现2种C语言接受的结构体初始化方式。
第一种,完全初始化。{xx,xx,xx,xx,xx}
第二种,部分初始化。
{
.a = xx,
.b = xx,
.c = xx,
.d = xx,
};
新增关键字:struct
新增字符操作:.
基础知识:
- double float用%f打印,char用%c或%d打印,int用%d,字符串用%s打印,指针用%p打印
共用体(union联合,联合体)
- 共用体union在定义和使用形式上,和结构体struct很相似。但是两种数据结构是完全不同的两类东西。
结构体,是对多个数据的组合与封装
共用体,共用体中只有一个东西,只是它被好几个名字(和类型)共用。
Char -128~127
Int xxx -xxx
宏定义
#define N 321 //宏定义格式
宏定义要注意的问题;
- 宏定义一般是在函数的外面
- 宏定义必须要先定义,再去使用宏,如果先使用就会编译报错
- 宏定义中宏一般用大写
为什么使用宏定义?
在C语言中,一般使用常数的时候,都不是直接使用,而是先把该常数定义为一个宏,然后在程序中使用该宏名。这样做的好处是,等我们需要修改这个常数时,只需要在宏定义处修改一次即可,而不用到代码中到处去寻找,看哪里都用过该常数。
枚举
Enum week'
{
Sun 0
Mon 1
Tue 2
Wen 3
}