如果你想要更详细的C语言知识体系,请参考下列书籍,本章将对一些重要的内容进行整理,这部分知识主要适合编程以及算法入门,会忽略一些不必要深究的细节
1.《C程序设计》
2.《C程序设计学习辅导》
3.《程序设计C语言实验指导》
4.《C Prime Plus》
其中前三本书较为基础,适合入门初学者使用,最后一本书的译本十分精彩,可谓C语言入门全集,里面会对知识广度做详细介绍,推荐阅读!
所谓头文件,是指程序预处理的时候由编译器操作的部分(这句话可跳过,初学不用深入理解),那么为什么先介绍头文件呢?因为头文件中包含了许多常用的库函数,方便我们对程序进行操作,下面举例说明:
首先给出常用的头文件:
//常用
#include
#include
#include
#include
#include
//不常用
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
接下来对常用的头文件做一下简单介绍,了解一些有用的库函数,方便程序设计(这部分可先略读,直接进入 1.2 主函数 部分进行阅读,等后面知识学完后再进行查阅)
(1)#include
库变量——
size_t:这是无符号整数类型,它是 sizeof 关键字的结果
这个变量很有用,例如你写unsigned变量时,其值就是size_t类型,再比如,需要动态分配内存空间的时候,需要写n*sizeof(int),即分配了n个int变量的空间
库宏——
EOF:这个宏是一个表示已经到达文件结束的负整数
这个宏会广泛的存在于题目的输入条件中,实际上EOF为-1,标志着文件的结束,例如我们可以这样来使用:while(scanf(“%d”,&n)!=EOF),我们在输入n的时候,顺便检查其是否到达了文件末尾,若到达了,则退出读入
库函数——
函数 | 作用 | 例如 |
---|---|---|
int scanf(const char *format, …) | 从标准输入 stdin 读取格式化输入 | scanf(“%d”,&n) |
int sscanf(const char *str, const char *format, …) | 从字符串读取格式化输入 | sscanf(str,“%d”,&n) |
int printf(const char *format, …) | 发送格式化输出到标准输出 stdout | printf(“%d”,n) |
int sprintf(char *str, const char *format, …) | 发送格式化输出到字符串 | sprintf(str,“%d”,n) |
int getchar(void) | 从标准输入 stdin 获取一个字符(一个无符号字符) | char ch = getchar() |
char *gets(char *str) | 从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定 | char s[50]; gets(s); |
int putchar(int char) | 把参数 char 指定的字符(一个无符号字符)写入到标准输出 stdout 中 | putchar(ch) |
int puts(const char *str) | 把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。换行符会被追加到输出中 | puts(s) |
这几个库函数必须掌握,用法十分常见
(2)#include
该函数库为数学类的函数,涉及数学的初等运算
库函数——
函数 | 作用 | 例如 |
---|---|---|
double fabs(double x) | 返回 x 的绝对值 | fabs(f) |
double sqrt(double x) | 返回 x 的平方根 | sqrt(f) |
double pow(double x, double y) | 返回 x 的 y 次幂 | pow(2,10)=1024 |
double ceil(double x) | 返回大于或等于 x 的最小的整数值 | ceil(4.9)=5 |
double floor(double x) | 返回小于或等于 x 的最大的整数值 | floor(4.9)=4 |
(3)#include
库宏——
NULL:这个宏是一个空指针常量的值
库函数——
该函数库主要处理字符串操作,包括拼接、复制、比较大小、长度等
函数 | 作用 | 例如 |
---|---|---|
void *memset(void *str, int c, size_t n) | 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符 | memset(a,0,sizeof(a)) |
int strcmp(const char *str1, const char *str2) | 把 str1 所指向的字符串和 str2 所指向的字符串进行比较 | strcmp(“hello”,“Hello”)>0 |
char *strcat(char *dest, const char *src) | 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾 | str = strcat(“Hello”,“World”) |
char *strcpy(char *dest, const char *src) | 把 src 所指向的字符串复制到 dest | strcpy(str,str1) |
size_t strlen(const char *str) | 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符 | int len = strlen(str) |
(4)#include
库函数——
该函数库主要处理内存动态分配处理和程序运行状态操作
函数 | 作用 | 例如 |
---|---|---|
int abs(int x) | 返回 x 的绝对值 | abs(n) |
void exit(int status) | 使程序正常终止 | exit(0) |
void *malloc(size_t size) | 分配所需的内存空间,并返回一个指向它的指针 | malloc(sizeof(int)) |
void *calloc(size_t nitems, size_t size) | 分配所需的内存空间,并返回一个指向它的指针 | calloc(n,sizeof(int)) |
void *realloc(void *ptr, size_t size) | 尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小 | realloc(ptr,sizeof(int)) |
void free(void *ptr) | 释放之前调用 calloc、malloc 或 realloc 所分配的内存空间 | free(ptr) |
(5)#include
库函数——
该函数库主要处理单个字符,涉及ASCII码的转换
函数 | 作用 | 例如 |
---|---|---|
int tolower(int c) | 该函数把大写字母转换为小写字母 | tolower(‘A’) |
int toupper(int c) | 该函数把小写字母转换为大写字母 | toupper(‘a’) |
int isalnum(int c) | 该函数检查所传的字符是否是字母和数字 | isalnum(‘7’) |
int isalpha(int c) | 该函数检查所传的字符是否是字母 | isalpha(‘a’) |
int isdigit(int c) | 该函数检查所传的字符是否是十进制数字 | isdigit(‘9’) |
int islower(int c) | 该函数检查所传的字符是否是小写字母 | islower(‘a’) |
int isupper(int c) | 该函数检查所传的字符是否是大写字母 | isupper(‘A’) |
int isspace(int c) | 该函数检查所传的字符是否是空白字符 | isspace(’ ') |
所谓主函数,指main函数,这里直接记住写法即可:
int main(){
return 0;
}
别忘记加return 0;这是程序正常执行结束的标志(因为程序内部错误会返回其他值,这里不必深究)
这里介绍两种常用的注释:// 和 /* */
//1.此注释为行注释,只能注释一行
/*
2.此注释为段落注释,可注释一个段落
*/
类型名 + 变量名 = 赋初值;
很简单,举例说明:
int a = 10; //定义整数a=10
double f = 0.22; //定义浮点数f=0.22
char s = 'A'; //定义字符s = 'A'
int b[10] = {1,2}; //定义整数数组b[0]=1,b[1]=2
这些都是对变量的定义,定义变量就必须指定类型,因此这两个概念一起理解
有了变量之后,接下来就是定义一些简单的程序结构,这里主要介绍3种:分支、循环、选择
简单来说,就是程序需要判断什么,举例:
if(a >= 10){
b = 1;
}else{
b = 2;
}
当 a > = 10 a>=10 a>=10,我们就将 b = 1 b=1 b=1,否则 b = 2 b=2 b=2,这里很好理解,稍微复杂一些的判断结构有:
if(条件1){
}else if(条件2){
if(条件4){
}else if(条件5){
}
}else if(条件3){
}else{
}
也就是说,判断可以嵌套,判断之中含有判断,一层一层的叠加
简单来说,你需要重复做一件事情,那么需要不断循环!举例:
int sum = 0;
for(int i=1;i<=10;i++){
sum += i;
}
显而易见,这里 s u m sum sum可以从1累加到10,输出即为55,就是高斯求和的展开式,复杂一些,可以有如下结构:
for(int i=1;i<=10;i++){
for(int j=1;j<=10;j++){
for(int k=1;k<=10;k++){
}
}
}
比如多层循环等等
选择是一种特殊的结构,这里单指switch:
switch(a){
case 1:b = 10;break;
case 2:b = 100;break;
case 3:b = 1000;break;
default:b = 1;break;
}
我们考察a的值,a若为1,则b的值为10,a若为2,则b的值为100,a若为3,则b的值为1000,a若为其他,则b的值为1000,这里要注意的是,break的作用是跳出switch,而不是顺序执行(初学者记住每句话后加break即可)
有了前3个概念之后,我们就可以写出简单的程序了,主体框架如下:
//1.头文件部分
//2.主函数
//输入输出 + 简单结构
再加上输入/输出,这样一个简单的程序就完成了(输入/输出请参考1.1(1)#include
#include
int main(){
//输入
//程序处理
//输出
return 0;
}
这一块书写应熟练,是整个程序基本的框架
学完这些之后,可练习:
团体程序设计天梯赛-L1组
所有5分和10分的题目
有了主程序之后,接下来就引入了子程序的概念,所谓子程序,是指相对于主程序的一个子函数,这个子函数可以完成一项或多个重复的功能,为了避免程序调用的时候,频繁书写重复的语句,因此定义一个子程序并反复调用是最佳的选择,例如,我们有一个阶乘函数:
int fac(int n);
接下来在主程序中,我们需要反复调用它:
int fac(int n){
int res = 1;
for(int i=1;i<=n;i++){
res *= i;
}
return res;
}
int main(){
int a = fac(5);
int b = fac(11);
return 0;
}
至此,子程序就介绍结束了
所谓递归,就是一系列重复的操作,那么这和之前的子程序有什么区别呢?子程序是一次操作完成一个或多个既定的功能,调用一次,返回结果;而递归是调用自己N次,逐层返回后才能得结果,例如,还是刚刚的阶乘函数,递归写法:
int fac(int n){
if(n == 1){
return 1;
}
return fac(n-1)*n;
}
fac函数中调用fac函数自身,这叫递归,递归有结束的时候,这就是边界条件,当n=1时,n!=1
顾名思义——数组,就是将一串数字按照成组的方式排列起来,一维就是线性的,例如:
1 2 3 4 5 6这就是6个线性排列的数字,放置在一个容器中,这个容器就是数组:
int a[6] = {1,2,3,4,5,6};
我们可以通过下标对数组进行访问,下标从0-(n-1),如a[3]=4
二维,在一维基础上增加了一个维度,也就是拥有行、列两个维度:
int a[2][3] = {{1,2,3},{4,5,6}};
数字1-6分成两行表示了,这也就是矩阵的表示法
同样地,也可以通过下标进行访问,如a[1][2]=6
结构体,通俗理解就是一个打包的数据类型集合,比方说我们要记录一本图书,就需要书名、ISBN号、价格、字数等多种信息,把这些信息打包,就是一个结构体,可表述为{书名、ISBN号、价格、字数}
这里要掌握结构体的两种定义方法:
(1)普通定义
struct book{
char book_name[20];
char ISBN[20];
double price;
int words;
}
这样我们就定义了一个结构体,包含了以上4个信息
(2)复杂定义
typedef struct{
char book_name[20];
char ISBN[20];
double price;
int words;
}book;
重命名意义下更常用一些(程序设计)
枚举,同一块存储空间可共用不同的类型定义,只能选择其中之一的类型
这一块可暂时略过,因为算法实践中基本不使用这种技术
这一块是C语言最为核心和精彩的一部分,但对于初学者而言,无需深入了解,指针在系统设计、文件访问有着相当深入的应用
我们主要介绍指针访问数组的情形,例如这样:
一维数组,依旧保存了6个数字,我们已经可以用下标的方式对其访问了,那么,如何用指针访问呢?
我们可以把指针看作一个箭头,开始指向数组头,即第一个格子,然后每加1,指向的格子就向后移动一格,这样,输出数组元素的值,等价于输出指针箭头指向的格子内元素的值,例如,要输出a[3]:
int a[6] = {1,2,3,4,5,6};
int *ptr = a; //创建指针
printf("%d",*(ptr+3));
这样,就完成了 7.1部分 同样的功能,二维指针原理同上
这里介绍C语言库中自带的一个函数qsort(此处可参考1.1部分 阅读库),
void qsort(void* base,size_t num,size_t width,int(__cdecl*compare)(const void*,const void*));
参数:
1 void* base,待排序数组,排序之后的结果仍放在这个数组中
2 size_t num,数组中待排序元素数量
3 size_t width。各元素的占用空间大小(单位为字节)
4 int(__cdecl*compare),指向函数的指针,用于确定排序的顺序(需要用户自定义一个比较函数)
例如,我们对一个无序的数组进行排序:
int cmp_int(const void* _a , const void* _b){
int* a = (int*)_a;
int* b = (int*)_b;
return *a - *b;
}
int main(){
int a[6] = {5,1,6,3,4,2};
qsort(a,6,sizeof(int),cmp_int);
return 0;
}
这样就完成了排序,方便快捷(C++有更好的方法,sort函数)
希望大家在掌握了C语言基本知识的基础上,再回看1.1库函数这一节,深入理解并记忆这些基本的库函数,相信对后面的程序与算法设计大有帮助!