C语言基础学习笔记(持续更新~)
一、数据类型和表达式
C语言中二进制数、八进制数和十六进制数的表示:
二进制:二进制由 0 和 1 两个数字组成,使用时必须以0b或0B(不区分大小写)开头。例如:0b101、0B001 注意:标准的C语言并不支持二进制写法,有些编译器自己进行了扩展,才会支持二进制数字 八进制:八进制由 0~7 八个数字组成,使用时必须以0开头(注意是数字 0,不是字母 o),例如:015(十进制的13)、0177777(十进制的65535) 十六进制:十六进制由数字 0~9、字母 A~F 或 a~f(不区分大小写)组成,使用时必须以0x或0X(不区分大小写)开头,例如:0X2A(十进制的42)、0xffff(十进制的65535) |
1.基本类型
- 整型(int)
- 字符型(char)
- 实型(浮点型)
- 单精度型(float)
- 双精度型(double)
- 枚举类型
下面是详细的类型说明:
2.构造类型
1)数组类型
数组:按序排列的同类数据元素的集合
- 一维数组:
类型说明符 数组名[数组长度];
- 二维/多维数组:
类型说明符 数组名[行数][列数];
多维数组以此类推 - 字符数组:
char 数组名[数组长度];
C语言没有字符串类型,字符串通常用字符数组表示
数组定义:类型说明符 数组名[长度]; 数组引用: 一维数组数组名[索引]; 二维数组数组名[行索引][列索引]; 注:索引都是从0开始 数组赋值: 1.在定义的时候赋初值:int a[10]={1,2,3,4,5,6,7,8,9,10};或int a[]={1,2,3,4,5,6,7,8,9,10}; 2.先定义,再赋值:int a[10];a = {1,2,3,4,5,6,7,8,9,10}; 字符数组赋值: 1.char Hello[] = {'H','e','l','l','o'}; 2.char Hello[] = "Hello"; 注:字符数组第二种赋值方式比第一种方式多占一个字符,因为第二种方式会在字符数组中结尾添加一个\0作为字符串结束符 提示:数组赋值时,如果给定值数量小于数组长度,系统默认填充0 |
#include <stdio.h>
int main() {
//=====================一维数组===============
int a[5] = {1, 2}; // a={1,2,0,0,0}
int b[] = {1, 2, 3, 4, 5};// b={1,2,3,4,5}
int c[10];// 没有赋初始值系统会自动赋值一个无意义的数字,可以自行printf输出查看
printf("a第二个元素:%d\nb第一个元素:%d\n", a[1], b[0]);
//=====================二维数组===============
int aa[2][3] = {1, 2, 3, 4, 5, 6};// C语言是按行编址,所以可以这样赋值
int bb[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
//aa和bb这两个数组是相同的
printf("aa第1行第1列元素:%d\n", aa[0][0]);
printf("bb第1行第2列元素:%d\n", bb[0][1]);
//=====================字符串===============
char name[8] = {'x', 'i', 'a', 'o', 'm', 'i', 'n', 'g'};
char name2[] = "xiaohong";
printf("第一个名字:%s第二个名字:%s", name, name2);
return 0;
}
2)结构体类型
3)公用体类型
3.常量
C语言中常量的定义有两种方式,假如我们要定义一个int类型的常量TEMP,值为1:
- 预定义命令:
#define TEMP 1
- const关键字:
const int TEMP = 1
4.运算表达式
1)算术运算表达式:
- 加:+
- 减:-
- 乘:*
- 除:/
- 取余:%
- 自增:++
- 自减:--
注意:自增和自减跟赋值运算结合的时候如果运算符在左边,会先进行自增或自减运算,请看下面例子:
void test1(){
int a = 1;
int b = ++a; //结果是b=2
}
void test2(){
int a = 1;
int b = a++; //结果是b=1
}
2)关系运算表达式:
- 等于:==
- 大于:>
- 大于等于:>=
- 小于:<
- 小于等于:<=
- 不等于:!=
3)逻辑运算符:
C语言中非0为真
- 与:&&
- 或:||
- 非:!
4)位运算符:
位与:&
对每一位进行逻辑与运算,0表示假,1表示真:0011 & 1111 = 0011
位或:|
对每一位进行逻辑或运算,0表示假,1表示真:0011 | 1111 =1111
位非:~
对每一位进行逻辑非运算,0表示假,1表示真:~1111 =0000
位异或:^
对每一位进行逻辑异或运算,0表示假,1表示真:0011 ^ 1111 =1100
左移:<<
高位溢出丢弃,低位不足补0:01100100 << 2 = 10010000
二、C语言的语句
1.表达式语句
定义:由表达式和分号组成的语句:x + y = z;
2.函数调用语句
定义:函数名、实际参数和分号组成:函数名(参数);
3.控制语句
1)条件判断语句:
- if语句:单条件判断语句
// 用法
if (条件表达式){
// 条件满足
要执行的语句
}
- if…else…语句:条件分支语句
// 用法
if (条件表达式){
// 条件满足
要执行的语句
}else{
// 条件不满足
要执行的语句
}
- if…else if…else…语句:多条件分支语句
// 用法
if (条件表达式1){
// 满足条件表达式1
要执行的语句;
}else if (条件表达式2) {
// 满足条件表达式2
要执行的语句;
}else if (条件表达式3) {
// 满足条件表达式3
要执行的语句;
}
...
else if (条件表达式n) {
// 满足条件表达式n
要执行的语句;
}else{
// 所有条件表达式都不满足
要执行的语句;
}
- switch语句:开关语句,一般配合case关键字使用
switch(表达式)
{
case 常量1:
// 如果表达式的值等于常量1,执行下面的语句1
语句1 ;
break;
case 常量2:
// 如果表达式的值等于常量2,执行下面的语句2
语句2;
break;
...
case 常量n:
// 如果表达式的值等于常量n,执行下面的语句n
语句n;
break;
default:
// 默认执行的语句,如果没有通过上面的开关语句退出,就会执行下面的语句n+1
语句n+1;
//break; // default可以省略break;因为它本身就是最后执行,执行完就会退出开关语句。
}
注:switch语句如果没有break会一直向下执行直到结束。
2)循环执行语句:
- for语句
结构: for (表达式1;表达式2;表达式3){ 语句; } 循环逻辑: step1:先执行表达式1 step2:然后执行表达式2, step3:如果step2结果为真,执行语句,否则退出循环 step4:如果step3没有退出循环,则执行表达式3 step5:重复执行step2-step4直至循环退出 |
- while语句
条件循环语句,当满足循环条件的情况下循环执行
//用法
while (循环条件){
执行语句;
}
- do while语句
与while循环的区别:do....while会先执行一遍循环体里面的语句,再进行条件判断,也就是说,do...while至少会执行一次循环体中的语句。
//用法
do{
执行语句;
}while (循环条件);
3)转向语句:
- continue:continue语句一般用于循环结构中,作用是跳过当次循环,当循环语句执行到continue时,不会继续向下执行,会跳过当次循环,直接执行下一次循环。
- break:中断语句,一般用于循环结构中,作用是终止循环,当执行到break语句时,会立即退出循环。
- return:跳出函数语句,用于跳出函数并返回一个值。
- goto:强制转向语句(不推荐使用)
//用法
int main(){
int a=1;
int b=5;
loop: if (a<b){
printf("%d\n",a);
a++;
goto loop;
}
return 0;
}
输出结果:
1
2
3
4
说明:goto语句一般用于跟if语句结合形成循环结构,需要先定义一个标志符(loop),
表示goto转向到哪个地方。
避免使用goto语句,使用for循环语句
#include <stdio.h>
int main() {
int a = 1;
int b = 5;
for (; a < b; a++) {
printf("%d\n", a);
}
return 0;
}
4.复合语句
定义:将多个语句用大括号括起来组成一个复合语句
{
int a = 1;
a++;
int b = a + 1;
}
5.空语句
定义:只有分号组成的语句称为空语句
;
6.案例
1)海伦公式
根据三角形的三条边求出面积:
S:面积 p:周长的1/2 a,b,c:三角形的三条边长
#include "stdio.h"
#include "math.h"
int main(){
float a;
float b;
float c;
float area;
float p;
printf("请输入构成三角形的三条边的长度:");
scanf("%f,%f,%f", &a, &b, &c);
p = (a+b+c)/2;
area = sqrt(p*(p-a)*(p-b)*(p-c));
printf("三角形面积是:%f",area);
return 0;
}
2)一元二次方程
#include <stdio.h>
#include "math.h"
int main() {
float a,b,c;
float p,x1,x2;
printf("请输入一元二次方程的3个系数a,b,c:ax^2+bx+c=0(a≠0)\n");
scanf("%f,%f,%f",&a,&b,&c);
p = sqrt(b*b-4*a*c);
x1 = (-b+p)/(2*a);
x2 = (-b-p)/(2*a);
printf("方程的解为:x1=%f,x2=%f",x1,x2);
return 0;
}
三、函数
1.函数的概念
函数是实现了某种功能的代码块 |
- 库函数:由C系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。
- 用户定义函数:由用户按需要写的函数。对于用户自定义函数,不仅要在程序中定义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。
2.函数的定义方式
- 无参函数:
类型标识符 函数名() {
声明部分;
语句;
}
- 有参函数:
类型标识符 函数名(形参1,形参2,形参3...形参n) {
声明部分;
语句;
}
- 示例:下面定义了两个函数,第一个HelloWorld是无参函数,功能是输出一个"Hello World!"字符串,第二个FindMax是有参函数,接收两个int类型的参数,返回两个数中最大的那个数
//void HelloWorld();
//int FindMax(int a,int b);
//上面是对函数进行声明,函数的调用必须先定义,否则编译不通过,如果定义在调用函数之后,需要先声明
void HelloWorld() {
printf("Hello World!");
}
int FindMax(int a, int b) {
int max;
max = a >= b ? a : b;
return max;
}
int main(){
HelloWorld();
int a = 5;
int b = 10;
int c;
c = FindMax(a, b);
printf("\n最大数为:%d\n", c);
return 0;
}
3.函数的参数(!!)
- 形参:形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。
- 实参:实参在主调函数中,是调用函数时传递的参数。
- 参数传递:函数的参数由主调函数的实参传递给被调函数的形参,因此实参与形参的顺序、类型必须保持一致。
4.函数的返回值
函数返回值是一个类型与函数声明中定义的返回类型相同的值,如果函数声明中没有定义返回类型,则默认为 int 类型。
例如,下面是一个简单的 C 函数,它返回一个整数值:
int max(int a, int b)
{
if (a > b) {
return a;
} else {
return b;
}
}
在这个例子中,函数 max() 定义了两个 int 类型的参数 a 和 b,并在函数体内部判断它们的大小关系。如果 a 大于 b,则函数返回 a 的值;否则,函数返回 b 的值。
另外,如果函数声明中定义了 void 类型的返回值,则表示函数不会返回任何值。在这种情况下,函数体内部不能使用 return 语句返回值。例如:
void print_hello()
{
printf("Hello, world!\n");
}
在这个例子中,函数 print_hello() 不需要返回任何值,因此声明中定义的返回类型为 void。
5.函数的调用
调用的一般形式为:函数名(实参); 被调用函数的声明和函数原型:在主调函数中调用某函数之前应对该被调函数进行说明(声明),这与使用变量之前要先进行变量说明是一样的。在主调函数中对被调函数作说明的目的是使编译系统知道被调函数返回值的类型,以便在主调函数中按此种类型对返回值作相应的处理。 其一般形式为: 类型说明符 被调函数名(类型 形参,类型 形参...);或 类型说明符 被调函数名(类型,类型...); |
6.全局变量与局部变量
作用域:表示一个变量起作用的范围,例如:
{
int a = 1; //a的作用域就是这个代码块,在代码块外部就无法访问变量a
}
1)全局变量
- 定义:全局变量也称为外部变量,它是在函数外部定义的变量。它不属于哪一个函数,它属于一个源程序文件。其作用域是整个源程序。
- 使用:在全局变量定义之前的函数中使用全局变量,需要使用关键字extern做全局变量说明,声明某个变量是全局变量,然后才能使用;在全局变量定义之后的函数中使用全局变量,可以省略extern关键字,不做全局变量说明也可以使用。
int a = 5; // 此处a为全局变量
int main(void){
int extern a; // 全局变量说明,声明a是一个全局变量,此处在a定义之后,可以省略该说明
printf("%d", a); //输出结果为5
}
2)局部变量
- 定义:局部变量也称为内部变量。局部变量是函数内部定义的变量,作用域仅限于函数内部,局部变量只能在函数内部使用,函数外部无法访问
int main(void){
int a = 5; // 这是一个局部变量,a的作用域范围是main函数内,在函数外无法使用
print("%d", a);
a++;
}
print("%d", a);//全局作用域内找不到变量a,编译不通过
7.静态变量与寄存器变量
1)静态变量
- 定义:静态变量是在函数调用结束后不消失而保留原值的变量,如果在一个函数调用结束后,希望它保留某个变量的值,就把这个变量用
static
关键字声明为静态变量。
// 定义一个自增函数,初始化局部静态变量a为0,每调用一次,a自增1
int Add() {
static int a = 0;
a++;
return a;
}
int main(){
print("%d", Add());// 输出结果为1
print("%d", Add());// 输出结果为2
return 0;
}
2)寄存器变量
- 定义:寄存器变量是放在CPU寄存器中的变量,CPU寄存器可以理解为CPU的内存空间,就像是电脑的内存一样,在寄存器中运算速度非常快。使用register关键字声明。
- 注意:
- 只有局部自动变量(非静态变量)和形参可以作为寄存器变量
- 一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量
- 局部静态变量不能定义为寄存器变量
#include "stdio.h"
// 这是一个计算n的阶乘的函数,将局部变量i和f声明为寄存器变量
int fac(int n) {
register int i, f = 1;
for (i = 1; i <= n; i++) {
f = f * i;
}
return f;
}
int main() {
int i;
for (i = 0; i <= 5; i++) {
printf("%d!=%d\n", i, fac(i));
}
return 0;
}
8.预处理命令
预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是C语言的一个重要功能,它由预处理程序负责完成。 C语言提供了多种预处理功能,如宏定义、文件包含、条件编译等。 |
1)宏定义
C语言可以使用 宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。 |
- 无参宏定义:
#define 标识符 字符串
(“字符串”可以是常数、表达式、格式串等)
所有出现在源程序中的宏名都会替换成宏定义的字符串
例如:
#include <stdio.h>
#define PI 3.1415926
#define M (a+a)
int main(void) {
double a = 1.0;
double b;
b = 2*M + PI; // 等同于2*(a+a) + 3.1415926
printf("%f", b);
return 0;
}
- 带参宏定义:
#define 宏名(形参1,形参2,形参3,...形参n) 字符串
(“字符串”可以是常数、表达式、格式串等)
>#include <stdio.h>
#define S(x,y) x*y // S表示矩形面积,x,y分别表示长宽
int main(void) {
double a = 3.0,b = 4.0;
double s;
s = S(a,b); // 等同于a*b
printf("%f", s);
return 0;
}
2)文件包含
文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。 文件包含的形式为:#include "文件名"或#include <文件名> 上面两种形式的区别:使用尖括号表示在包含文件目录中去查找(包含目录是由用户在设置环境时设置的),而不在源文件目录去查找;使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找。 |
3)条件编译
预处理程序提供了条件编译的功能。可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。 |
条件编译有以下三种形式:
- 第一种:如果标识符已被
#define
命令定义过则对程序段 1 进行编译;否则对程序段 2 进行编译。
#ifdef 标识符
程序段 1
#else
程序段 2
#endif
- 第二种:如果标识符未被
#define
命令定义过则对程序段 1 进行编译,否则对程序段 2 进行编译。
#ifndef 标识符
程序段 1
#else
程序段 2
#endif
- 第三种:常量表达式的值为真(非 0),则对程序段 1 进行编译,否则对程序段 2 进行编译。
#if 常量表达式
程序段 1
#else
程序段 2
#endif