#C语言基础知识入门
说明C语言代码块结构
#include <stdio.h> //这个语句的功能是进行有关的预处理操作。include称为文件包命令,后面尖括号中内容称为头部文件或收文件。
#include "demo.h" //导入的是工程内部的头文件
int main() { //main 函数生命,每一个程序中都会有一个main函数,main函数就是一个程序的入口部分。printf("你好世界\n");return 0;
}
数据类型和表达式
C语言中,数据类型可分为:1、基本数据类型 2、构造数据类型 3、指针类型 4、空类型(void)四大类
基本类型
- 整形(int)
- 字符型(char)
- 实型(浮点型)
- 单精度型(float)
- 双精度类型(double) - 枚举类型
下面是详细的类型说明:
枚举类型
枚举(Enumeration)是一种自定义的数据类型,它允许定义一组命名的常量。枚举类型的变量只能赋值为枚举列表中的一个值,这些值被称为枚举常量。枚举类型是一种非常方便的方式来组织和描述常量。
枚举定义
- 定义枚举关键字:enum
- 定义枚举的形式:enum 枚举名称 {枚举常量列表};(枚举常量的值被认为是int类型或者是unsigned int 类型,默认枚举变量值从0开始递增)
enum color {RED,GREEN,BLUE
};
/*** 上面定义了一个三种颜色的枚举,三种枚举默认值为RED=0,GREEN=1,BLUE=2* 下面定义一个性别的枚举,并给枚举值进行自定义* */enum sex {MAX =1;WOMAN =2
};
枚举的用法
枚举常用来定义一组常量选项
#include <stdio.h>
#include <string.h>enum week {Mon,Tue,Wed,Thu,Fri,Sat,Sun
};int main() {enum week today;today=Mon;switch (today) {case Mon:printf("今天是周一");break;case Tue:printf("今天是周二");break;case Wed:printf("今天是周三");break;case Thu:printf("今天是周四");break;case Fri:printf("今天是周五");break;case Sat:printf("今天是周六");break;case Sun:printf("今天是周日");break;}return 0;
}
指针类型
构造数据类型
C语言中构造类型一共由3种它们分别为数组类型,结构体类型,共用体类型。
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作为字符串结束符
#include <stdio.h>
int main() {printf("你好世界\n");//=======一维数组=======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','0','m','i','n','g'};char name2[]="xiaohong";printf("第一个名字:%s第二个名字:%s",name,name2)return 0;
}
2、结构体类型
结构体跟一些面向对象的语言(Python、C#、Java)中的类概念相似,就是一组数据由多个成员数据组成,成员数据可以是基本类型或者构造类型,在使用结构体之前必须先进行定义
结构体是由多个不同数据类型的成员组成的数据类型。结构体中的每个成员可以有不同的数据类型和命名。使用结构体可以将多个不同数据类型的信息组合成一个单一的逻辑单元,从而方便地进行操作。
- 结构体定义
- 定义结构体关键字:struct
- 定义形式:struct 结构名 { 成员数据 };
// 下面定义了一个名为Person的结构体,Person包含有一个人的姓名、年龄、性别、身高、住址信息
struct Person{char* name;int age;char sex;double height;char address[200];
};
-
结构体的用法
结构体成员变量的表示方法: 结构名.变量名或(*结构指针).变量名或者(*结构指针)->变量名
struct Person{char* name;int age;char sex;double height;char address[200];
};int main() {struct Person man; // 结构体变量实例化struct Person woman; // 结构体变量实例化struct Person* pW = &woman; // 实例化一个结构体指针变量man.name; // 结构体变量直接表示man.sex;(*pW).name; // 结构体指针变量表示pW->sex; // 结构体指针变量表示return 0;
}
结构体变量的赋值:直接给成员变量赋值,注意数组类型不能直接赋值。
#include <stdio.h>
#include <string.h>// 下面定义了一个名为Person的结构体,Person包含有一个人的姓名、年龄、性别、身高、住址信息
struct Person{char* name;int age;char sex;float height;char address[200];
};int main() {struct Person man;struct Person woman;struct Person* pW = &woman;man.name = "小明"; // 结构体变量赋值man.sex = 'M';man.age = 18;man.height = 1.78f;strcpy(man.address,"四川省成都市");(*pW).name = "小红"; // 结构体变量赋值(*pW).sex = 'W';pW->age = 19;pW->height = 1.68f;strcpy(pW->address,"四川省绵阳市"); // 数组类型不能直接赋值printf("姓名:%s\n年龄:%d\n性别:%c\n身高:%.2fm\n地址:%s\n",man.name,man.age,man.sex,man.height,man.address);printf("==============================================================================================\n");printf("姓名:%s\n年龄:%d\n性别:%c\n身高:%.2fm\n地址:%s\n",woman.name,woman.age,woman.sex,(*pW).height,pW->address);return 0;
}
3、共用体类型
共用体是一种特殊的结构体,其所有成员共享相同的内存空间。共用体中的每个成员可以有不同的数据类型,但是它们共享相同的内存空间,因此只能同时存在一个成员的值。共用体的主要用途实在不同的数据类型之间进行类型转换或节省内存空间。
共用体的定义
- 定义结构体关键字:union
- 定义形式:union 共用体名{成员数据};
#include <stdio.h>
#include <string.h>union data{int i;float f;char str[20];
};
int main() {printf("你好世界\n");union data mydata;//实例化一个共用体变量mydata.i=10;printf("mydata.i=%d\n",mydata.i);mydata.f=3.14f;printf("mydata.f= %f\n",mydata.f);strcpy(mydata.str,"hello");printf("mydata.str =%s\n",mydata.str);return 0;
}
在这个例子中,我们定义了一个名为data的共用体,包含一个整型变量i、一个浮点型变量f和一个字符数组str。在main函数中,我们定义了一个mydata的共用体变量,可以用来存储int、float或char类型的数据。
由于所有成员变量共享同一块内存空间,因此在设置mydata.f和mydata.str时,mydata.i的值被覆盖了。这也是共用体的一个特点:在任意时刻,只能有一个成员变量是有效的。
共用体的用法
主要用途:在不同的数据类型之间进行类型转换或节省内存空间
#include <stdio.h>
#include <string.h>union data{int i;float f;char str[20];
};
int main() {printf("你好世界\n");union data mydata;//实例化一个共用体变量mydata.i=10;printf("mydata.i=%d\n",mydata.i);mydata.f=3.14f;printf("mydata.f= %f\n",mydata.f);strcpy(mydata.str,"hello");printf("mydata.str =%s\n",mydata.str);return 0;
}
常量
C语言中常量的定义有两种方式,假如我们要定义一个int类型的常量TEMP,值为1:
- 预定义命令: #define TEMP =1
- const 关键字: const int TEMP =1
运算表达式
算术运算表达式
- 加 : +
- 减:-
- 乘:*
- 除:/
- 取余:%
- 自增:++
- 自减:–
注意: 自增和自减跟赋值运算结合的时候如果运算符在左边,会先进行自增或自减运算,请看下面例子:
void test1(){
int a = 1;
int b = ++a; //结果是b=2
}void test2(){
int a = 1;
int b = a++; //结果是b=1
}
关系运算表达式
- 等于:==
- 大于:>
- 大于等于:>=
- 小于:<
- 小于等于:<=
- 不等于:!=
逻辑运算符
C语言中非0为真
- 与:&&
- 或:||
- 非:!
位运算符
-
位与:&
对每一位进行逻辑与运算,0表示假,1表示真:0011 &1111 = 0011 -
位或:|
对每一位进行逻辑或运算,0表示假,1表示真:0011 | 1111 =1111 -
位非:~
对每一位进行逻辑非运算,0表示假,1表示真:~1111=0000 -
位异或:^
对每一位进行逻辑异或运算,0表示假,1表示真:0011^1111=1100 -
左移:<<
高位溢出丢弃,低位不足补0:01100100<<2=10010000 -
右移:>>
- 正数:高位补0,低位溢出舍去:01111111 >> 4 = 00000111
- 负数:高位补1,低位溢出舍去:11111111 >> 4 = 11111111
C语言的语句
表达式语句
定义:由表达式和分号组成的语句:x+y=z;
函数调用语句
定义:函数名、实际参数和括号组成:函数名(参数);
控制语句
条件判断语句:
- 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会一直向下执行直到结束。
循环执行语句:
- for语句
结构:
for(表达式1;表达式2;表达式3){
语句;
}
循环逻辑:
step1:先执行表达式1
step2:然后执行表达式2
step3:如果step2结果为真,执行语句,否则退出循环
step4:如果step3没有退出循环,则执行表达式3
step5:重复执行step2-step4直至循环退出
//用法
for (循环变量赋初值;循环条件;循环变量增量){执行语句;
}
- while语句
条件循环语句,当满足循环条件的情况下执行
//用法
while (循环条件){执行语句;
}
- do while 语句
与while循环的区别:do…while会先执行一遍循环体里面的语句,在进行条件判断,也就是说,do…while至少会执行一次循环体中的语句
//用法
do{执行语句;
}while (循环条件);
转向语句:
- 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转向到哪个地方。
复合语句:
定义:将多个语句用大括号括起来组成一个复合语句
{int a = 1;a++;int b = a + 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;
}
函数的参数
- 形参:形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。
- 实参:实参在主调函数中,是调用函数时传递的参数。
- 参数传递:函数的参数由主调函数的实参传递给被调函数的形参,因此实参与形参的顺序、类型必须保持一致。
函数的返回值
函数返回值是一个类型与函数声明中定义的返回类型相同的值,如果函数声明中没有定义返回类型,则默认为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。
函数的调用
-调用的一般形式:函数名(实参);
被调用函数的声明和函数原型:在主调函数中调用某函数之前对该被调函数进行声明,这与使用变量之前要先进行变量说明是一样的。在主函数中对被调函数作说明的目的是使编译系统知道被调函数返回值的类型,以便在主调函数中按此种类型对返回值作相应的处理。其一般形式为:类型说明符 被调函数名(类型 形参,类型 形参…);或者类型说明符 被调函数名(类型,类型…);
全局变量与局部变量
作用域:表示一个变量起作用的范围,例如:
{int a = 1; //a的作用域就是这个代码块,在代码块外部就无法访问变量a
}
全局变量
- 定义:全局变量也称为外部变量,它是在函数外部定义的变量。它不属于哪一个函数,它属于一个源程序文件。其作用域是整个源程序。
- 使用:在全局变量定义之前的函数中使用全局变量,需要使用关键字extern做全局变量说明,声明某个变量是全局变量,然后才能使用;在全局变量定义之后的函数中使用全局变量,可以省略extern关键字,不做全局变量说明也可以使用。
int a = 5; // 此处a为全局变量int main(void){int extern a; // 全局变量说明,声明a是一个全局变量,此处在a定义之后,可以省略该说明printf("%d", a); //输出结果为5
}
局部变量
定义:局部变量也称为内部变量。局部变量是函数内部定义的变量,作用域仅限于函数内部,局部变量只能在函数内部使用,函数外部无法访问。
int main(void){int a = 5; // 这是一个局部变量,a的作用域范围是main函数内,在函数外无法使用print("%d", a);a++;
}
print("%d", a);//全局作用域内找不到变量a,编译不通过
静态变量与寄存器变量
静态变量
- 定义:静态变量实在函数调用结束后不消失而保留原值的变量,如果在一个函数调用结束后,希望它能保留某个变量的值,就把这个变量用static关键字声明为静态变量。
// 定义一个自增函数,初始化局部静态变量a为0,每调用一次,a自增1
int Add() {static int a = 0;a++;return a;
}int main(){print("%d", Add());// 输出结果为1print("%d", Add());// 输出结果为2return 0;
}
寄存器变量
- 定义:寄存器变量是放在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;
}
预处理命令
预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是C语言的一个重要功能,它由预处理程序负责完成。
C语言提供了多种预处理功能,如宏定义、文件包含、条件编译等。
宏定义
C语言可以使用#define定义宏(累死常量),程序在编译处理时会把源程序中所有的宏名替换成宏定义的结果。宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成。
- 无参宏定义: #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.1415926printf("%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*bprintf("%f", s);return 0;
}
文件包名
文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。
文件包含的形式为:#include "文件名"或#include <文件名>
上面两种形式的区别:使用尖括号表示在包含文件目录中去查找(包含目录是由用户在设置环境时设置的),而不在源文件目录去查找;使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找
条件编译
预处理程序提供了条件编译的功能。可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。
条件编译有以下三种形式:
- 第一种:如果标识符已被**#define**命令定义过则对程序段1进行编译;否则对程序段2进行编译
#ifdef 标识符程序段 1
#else程序段 2
#endif
- 第二种:如果标识符未被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。
#ifndef 标识符 程序段 1
#else 程序段 2 #endif
-第三种:常量表达式的值为真(非0),则对程序段1进行编译,否则对程序段2进行编译。
#if 常量表达式程序段 1
#else 程序段 2
#endif