目录
一.概述
C语言是应用最广泛的程序设计语言,本文将从以下几个方面对C语言的基础知识点进行概述。(知识点基于人民邮电出版社C语言程序设计)
二.数据类型
C语言中数据包括基本类型,无值类型,指针类型,构造类型。下面主要介绍基本类型,基本类型包括整型,浮点型和字符型。
2.1 整数类型
char | 1 字节 | -128 到 127 或 0 到 255 |
unsigned char | 1 字节 | 0 到 255 |
signed char | 1 字节 | -128 到 127 |
int | 2 或 4 字节 | -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 |
unsigned int | 2 或 4 字节 | 0 到 65,535 或 0 到 4,294,967,295 |
short | 2 字节 | -32,768 到 32,767 |
unsigned short | 2 字节 | 0 到 65,535 |
long | 4 字节 | -2,147,483,648 到 2,147,483,647 |
unsigned long | 4 字节 | 0 到 4,294,967,295 |
2.2 浮点型
float | 4 字节 | 1.2E-38 到 3.4E+38 | 6 位有效位 |
double | 8 字节 | 2.3E-308 到 1.7E+308 | 15 位有效位 |
long double | 16 字节 | 3.4E-4932 到 1.1E+4932 | 19 位有效位 |
2.3 字符型
[signed] char | 1 字节 |
unsigned char | 1 字节 |
三.运算符,表达式和语句
3.1C语言基本语句
3.1.1输出函数
字符输出函数 | putchar(实参) | 向标准输出设备输出实参表示的字符 |
格式输出函数 | printf(格式控制, 输出列表) | eg:printf("Hello,world!"); |
字符输出函数:
#include <stdio.h>
//首先已知A的ASCII码值为65
//下面采用两种方式用putchar函数输出A
int main(){
putchar(65);
char c='A';
putchar(c) ;
}
对于putchar(65)的理解:其实参是一个整型变量,执行时会先把整型转化成字符串类型,再输出相应的字符‘A’,当实参是一个可求值的表达式时,如下:
#include <stdio.h>
//首先已知A的ASCII码值为65
//下面用putchar函数输出C
int main(){
char c='A';
putchar(c+2) ;
}
格式输出函数
printf函数中常用格式字符说明:
%d | 以十进制有符号形式输出int或unsigned int型整数 |
%u | 以十进制无符号形式输出int或unsigned int型整数 |
%o | 以八进制形式输出int或unsigned int型整数的机器码 |
%x,%X | 以十六进制形式输出int或unsigned int型整数的机器码 |
%f | 以小数形式输出单精度浮点型数据 |
%e | 以指数形式输出单精度浮点型数据 |
%c | 输出单个字符 |
%s | 输出字符串 |
printf 程序示例:
//printf()函数示例
#include <stdio.h>
int main()
{
//字符和字符串
int a=65;
printf("%c\n",a);
printf("%s\n","Chinese");
//整型
float c=1;
printf("%f\n",c);
printf("%d",c);
}
输出结果如下:
A
Chinese
1.000000
0
这时可以发现,前三行输出时正确的,而第四行c=1,输出后却变成了0,这是由于格式控制字符要求的数据类型与输出数据项的数据类型不一致,出现了错误,在写代码时一定要注意类型的一致
3.1.2输入函数
字符输入函数 | getchar(); |
格式输入函数 | scanf(); |
//输入函数示例
#include <stdio.h>
int main()
{
char c;
int a,b;
c=getchar();
putchar(c);
printf("\n请输入a和b,中间用逗号隔开:");
scanf ("%d,%d",&a,&b);
printf("a=%d,b=%d,a+b=%d",a,b,a+b);
}
输入w,3,4后的运行结果如下:
w
w
请输入a和b,中间用逗号隔开:3,4
a=3,b=4,a+b=7
3.2运算符
C语言的运算符按运算时操作数的个数可分为单目运算符、双目运算符和三目运算符。按运算符在运算时的功能可分为:算术运算符、关系运算符、逻辑运算符、位操作运算符、赋值运算符、条件运算符、逗号运算符、指针运算符、求字节数运算符、强制类型转换运算符、成员运算符、括号运算符等。由于C语言的运算符种类十分繁多,笔者只简单介绍算术、赋值、逗号及强制类型转换运算符。
3.2.1算数运算符和算数表达式
/*算数运算符简介*/
/*
+ 加法运算符
- 减法、负号运算符
* 乘法运算符
/ 除法运算符
% 取余运算符
*/
#include <stdio.h>
#include <math.h>
int main(){
int a=10;
int b=2;
printf("相加为%d,相减为%d,相乘为%d,相除为%d,取余为%d\n",a+b,a-b,a*b,a/b,a%b);
return 0;
}
输出结果如下:
相加为12,相减为8,相乘为20,相除为5,取余为0
注意点:
a.两个整数类型相除的结果是整数类型,如7/3的结果是2,不存在四舍五入,而是直接舍去小数部分,在代码中体现如下
#include <stdio.h>
#include <math.h>
int main(){
int a=7;
int b=3;
int c=4;
printf("7/3=%d;7/4=%d",a/b,a/c);
return 0;
}
b.%运算中,要求两个运算对象必须是整型,否则为非法运算。同时当%运算中有一个运算对象是负数时,运算结果的符号与被除数相同
#include <stdio.h>
#include <math.h>
int main(){
int a=7;
int b=-3;
printf("7%3=%d,7%(-3)=%d,(-7)%3=%d",7%3,7%(-3),(-7)%3);
return 0;
}
输出结果如下:
7=1,7(-3)=1,(-7)=-1
3.2.2 赋值运算符和赋值表达式
赋值运算符:“=”运算符是双目运算符,优先级仅高于逗号运算符,是右结合的
赋值表达式:变量=确定的值
复合的算数运算符及算数表达式
+= | a+=5; | a=a+5; |
*= | x*=y; | x=x*y; |
%= | r%=b; | r=r%b; |
/= | c/=b; | c=c/b; |
-= | a-=b; | a=a-b; |
3.2.3 自增自减运算符及表达式
i++=i+1 | 先运算表达式,在自增自减 |
++i=i+1 | 先自增自减,在运算表达式 |
3.2.4逗号运算符及表达式
表达式1,表达式2
顺序计算表达式1和表达式2的值,并以表达式2的值作为整个逗号表达式的值
3.2.5 关系运算符及其表达式
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
== | 等于 |
!= | 不等于 |
要将“==”和“=”区分开来,前者是关系运算符,表示两式相等;后者为赋值语句,把右边的值赋给左边
3.2.6逻辑表达式
逻辑运算符分为逻辑与,逻辑或,逻辑非,其功能与数电中相同
&& | 同真则真 |
|| | 同假则假 |
! | 取反(真假互换) |
四.循环
4.1 while循环
4.1.1基本形式
while(逻辑量)循环体语句
#include <stdio.h>
int main()
{
int a=0;
while(a<=5){
printf("%d",a);
a++;
}
}
上述代码中,当a<=5时,输出现在的a,同时a自增,直到不满足a<=5时退出循环
4.1.2 注意点
a.注意循环条件,避免死循环(此时可以用ctrl+break结束)
b.当循环体内有多个操作时,用{}将语句复合
4.2 do~while循环
4.2.1基本形式
do
循环语句
while(逻辑量)
#include <stdio.h>
int main()
{
int a=0;
do{
printf("%d",a);
a++;
}while(a<0);
}
此时输出为
0
4.2.2 与while循环的对比
而当此代码用while语句实现时:
#include <stdio.h>
int main()
{
int a=0;
while(a<0)
{
printf("%d",a);
a++;
}
}
此时运行时没有输出。
通过上述的例子可以看出,在do~while循环中,循环体至少会执行一次,循环体执行一次后再判断循环条件,而while循环先判断循环条件
4.2.3注意点
a.do~while语句的while后面一定要记得带“ ;”
b.do~while循环中习惯把循环体用{}括起来
4.3 for循环
4.3.1基本形式
for(表达式1;逻辑量;表达式2)
循环体语句
//用for循环计算阶乘
#include <stdio.h>
int main()
{
float a;
int n,i;
scanf("%d",&n);
for(a=1,i=1;i<=n;i++)
{
a=a*i;
}
printf("%f",a);
}
4.3.2注意点
a.for语句中各表达式和逻辑量均可以省略,但是“;”不能省略
b.当循环变量已赋初值时,可以省略语句1
c.在省略表达式2时,要避免陷入死循环,在循环体中要加入使循环趋于结束的语句,例如i--
d.循环体语句可以是空语句
4.4各个循环的使用选择
当循环次数已知时,用for循环
当循环次数未知时,用while循环
当希望循环体至少执行一次时,用do~while循环
4.5break和continue语句
break语句只能用在switch和循环体语句中,在循环中,用于跳出循环,执行循环下面的语句
continue是用来结束本次循环
4.6嵌套循环(迭代)
//小于n且个位数不等于3的素数
#include <stdio.h>
int main()
{
int n, m ,i ,flag;
scanf("%d",&n);
for(m=2;m<n;m++)
{
flag=1;
for(i=2;i<m;i++)
if(m%i==0)
{
flag=0;
break;
}
if(m%10!=3&&flag==1)
printf("%d",m);
}
printf("\n");
}
五.函数
5.1概述
为了实现特定功能而编写相对独立的程序段
函数定义 |
函数声明 |
函数调用 |
5.2函数定义
5.2.1一般形式
类型说明符 函数名(带类型说明的形参列表)
{
数据描述部分
算法实现部分
}
//定义有返回值函数实现求三个数最大值
double fmax(double a,double b,double c)
{
double m;
m=x>y?x:y;
m=m>z?m:z;
return m;
}
对于无值函数void fmax(){},函数体可以省略return语句,或return后表达式为空
5.2.2注意点
a.函数是相互独立的,互不从属
b.有参函数每个参数的类型都不能省略
c.返回值的类型与函数类型不一致时,以函数类型为准
d.未说明函数类型时,默认为整型
5.3函数声明
5.3.1一般形式
a.库函数的声明
#include <头文件>
在C语言中,头文件为stdio.h
#include <stdio.h>
b.自定义函数的声明
类型说明符 被调函数名(形参的类型表列)
内部声明 | main函数内部声明 |
外部声明 | 又称全局声明,在main函数前声明 |
缺省声明 | 函数定义在main函数之前,可以不声明 |
5.4函数调用
函数名 (实际参数表列)
//定义有返回值函数实现求三个数最大值
#include <stdio.h>
int fmax(int x,int y,int z)
{
int m;
m=x>y?x:y;
m=m>z?m:z;
return m;
}
int main()
{
int a1,a2,a3,e;
scanf("%d,%d,%d",&a1,&a2,&a3);
e=fmax(a1,a2,a3);
printf("%d",e);
}
六.指针
int *P;
p=&a;
*p=872;
在上面的代码中,定义了一个指针变量p,p用来指向变量a的地址,*p=872将872赋值给指针p指向的变量,等价于a=872
6.1指针与指针变量概述
指针:变量的地址称为变量的指针
指针变量:专门用来存放其他变量地址的特殊变量
间接访问运算符:可以实现对指针变量指向变量的间接访问
如:*p=872
个人理解:p代表指针变量,指向变量的地址;*p相当于变量,是对变量的间接访问:&a代表a的地址
6.2 指向变量的指针变量
6.2.1指针变量的定义
类型说明符 *指针变量名
需要注意的是指针变量名本身并不包含*
6.2.2指向关系的建立
//将变量的地址初始化赋值给指针变量
int a;
int *p=&a;
//指针变量给另一个指针变量做初始化
int x;
int *p=&x;
int *q=p;
//通过赋值方式
int a;
int *p;
p=&a;
6.2.3间接访问
“ * ”是间接访问运算符
//从键盘输入两数,不改变m,n的值,从小到大输出
#include <stdio.h>
int main()
{
int m,n;
int *p,*p1,*p2;
p1=&m;
p2=&n;
scanf("%d,%d",&m,&n);
if(*p1>*p1)
{
p=p1;
p1=p2;
p2=p;
}
printf("m=%d,n=%d\n",m,n);
printf("min=%d,max=%d\n",*p1,*p2);
}
在上述代码中没有改变m,n两个变量的值,而是交换了指针的指向,从而通过指针间接按顺序输出数
6.3指针变量做函数形参
//用指针改变两个变量的值
#include <stdio.h>
void swap(int *p1,int *p2)
{
int temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}
int main()
{
void swap(int *,int *);
int a,b,*pa,*pb;
pa=&a;
pb=&b;
scanf("%d,%d",pa,pb);
printf("a=%d,b=%d\n",a,b);
swap(pa,pb);
printf("a=%d,b=%d\n",a,b);
}
比较此代码与间接访问的代码,可以发现在赋值交换时,一个是直接对指针变量进行操作,改变的指向。另一个是对指针变量指向的对象进行操作。在前文的自定义函数中提到,自定义函数不能改变变量的值,而通过指针,可以实现在自定义函数中改变变量的值,大大提高了可操作性。
七.数组
7.1一维数组
7.1.1一维数组的定义
类型说明符 数组名 [整型常量表达式]
int a[10];
a[0]代表数组中的第一个数,10表示数组的容量
C语言中规定数组名表示数组所分配的内存空间的首地址,即&a[0]=a;同时a也可以理解为指向数组第一个元素的指针
7.1.2一维数组的引用
下面以代码展示两种引用方法
//数组元素的引用
//下标法引用
//&a[i]表示数组第i个元素的地址 和a+i含义一致
int a[10];
a[0]=10;
scanf("%d",&a[0]);
//指针法引用
//a+i相当于指向a[i]的指针
#include <stdio.h>
int mian()
{
int a[10],i;
for(i=0;i<=9;i++)
{
*(a+i)=i*i;
}
for(i=9;i>=0;i--)
printf("%d",*(a+i));
}
7.1.3一维数组的初始化
int a[5]={1,2,3,4,5};
注意点:
a.数组的初始化只能在数组定义时进行
int a[5];
a[5]={1,2};
这种定义方式是错误的
b.对所有变量赋初值时,数组长度可以省略,但是"[]"保留
c.可以只对数组前几个元素赋值,其余元素默认为0
7.2二维数组
int a[2][3];
int b[2][2]={1,2,3,4};
具体内容不再详细叙述
八.结构体,联合体和枚举
8.1结构体类型及结构体变量
8.1.1结构体类型的定义
struct 结构体类型名
{
类型说明符 成员1;
类型说明符 成员n;
}
struct data
{
int year;
int month;
int day;
}
8.1.2 结构体变量
结构体变量的定义
//同时定义
struct data
{
int year;
int month;
int day;
} data1,data2;
//分开定义
struct data
{
int year;
int month;
int day;
}
struct data D1,D2;
结构体变量的引用
D1.day=10;
D2.year=2022;
8.1.3结构体的嵌套
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {15, {3,2},NULL}; //结构体嵌套初始化
struct Node n2 = {11, {3, 2}, NULL};//结构体嵌套初始化
8.1.4程序实现
#include <stdio.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456};
int main()
{
printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
}
8.2联合体
联合类型的定义联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。
8.2.1基本形式
union 联合体类型名
{
类型说明符 成员1;
类型说明符 成员n;
}
union udata
{
int i;
char c;
float f;
}
8.2.2联合体变量定义
union udata
{
int i;
char c;
float f;
}u,*P;
union udata
{
int i;
char c;
float f;
}
union udata u,*p;
8.3枚举
什么是枚举? 枚举顾名思义就是一一列举。把可能的取值一一列举。
比如我们现实生活中: 一周的星期一到星期日是有限的7天,可以一一列举。
性别有:男、女、保密,也可以一一列举。
月份有12个月,也可以一一列举 颜色也可以一一列举。 这里就可以使用枚举了。
enum Color//颜色
{
RED, //0
GREEN, //1
BLUE //2
};
#include <stdio.h>
#include <stdlib.h>
int main()
{
enum color { red=1, green, blue };
enum color favorite_color;
/* 用户输入数字来选择颜色 */
printf("请输入你喜欢的颜色: (1. red, 2. green, 3. blue): ");
scanf("%u", &favorite_color);
/* 输出结果 */
switch (favorite_color)
{
case red:
printf("你喜欢的颜色是红色");
break;
case green:
printf("你喜欢的颜色是绿色");
break;
case blue:
printf("你喜欢的颜色是蓝色");
break;
default:
printf("你没有选择你喜欢的颜色");
}
return 0;
}
九.宏定义
在 C 语言中,可以采用命令 #define 来定义宏。该命令允许把一个名称指定成任何所需的文本,例如一个常量值或者一条语句。在定义了宏之后,无论宏名称出现在源代码的何处,预处理器都会把它用定义时指定的文本替换掉。
// fn_tbl.c: 以表格形式输出一个函数的值。该程序使用了嵌套的宏
// -------------------------------------------------------------
#include <stdio.h>
#include <math.h> // 函数cos()和exp()的原型
#define PI 3.141593
#define STEP (PI/8)
#define AMPLITUDE 1.0
#define ATTENUATION 0.1 // 声波传播的衰减指数
#define DF(x) exp(-ATTENUATION*(x))
#define FUNC(x) (DF(x) * AMPLITUDE * cos(x)) // 震动衰减
// 针对函数输出:
#define STR(s) #s
#define XSTR(s) STR(s) // 将宏s展开,然后字符串化
int main()
{
double x = 0.0;
printf( "\nFUNC(x) = %s\n", XSTR(FUNC(x)) ); // 输出该函数
printf("\n %10s %25s\n", "x", STR(y = FUNC(x)) ); // 表格的标题
printf("-----------------------------------------\n");
for ( ; x < 2*PI + STEP/2; x += STEP )
printf( "%15f %20f\n", x, FUNC(x) );
return 0;
}