前言
- 本系列C语言教程将补充C语言的基础部分,适合具有一定语言基础的朋友进行复习。
- 第一期: 【C语言简明教程】(一):数据类型,表达式与控制结构-CSDN博客
- 第二期: 【C语言简明教程提纲】(二):函数你值得拥有-CSDN博客
- 以下是本系列的目录
- 字符串使用(本节)
- 编译预处理(本节)
- 数组与函数
- 数组定义和引用
- 指针定义和使用
- 指针与数组、与字符串、与函数
- 结构体定义、结构体数组、结构体指针
- 文件定义和操作。
- 常用算法
- 关于数值和指针两个难点,可以参考之前写的文章,浅显易懂,适合初学者的朋友
- 【C语言备课课件】(上)数组array
- 【C语言备课课件】(下)指针pointer
![]()
0 前置知识-进制转换
0-1 进制表
- 在 C 语言中,字符串的八进制转义字符和十六进制转义字符本质上是ASCII 数值表示,因此需要简单了解进制转换。
0-2 十六进制 → 十进制(手算方法)
- 十六进制的基数是16。
0~9和a~f - 计算方法:每一位 × 16 的幂 每一位 × 16 的幂每一位×16的幂
- 例如:0 x 41 0x410x41
- 计算就是
4×16¹+1×16⁰=64+1=65
0-3 八进制 → 十进制(手算方法)
- 同样的道理,八进制基数是8。
0~7 - 计算方法:每一位 × 8 的幂 每一位 × 8 的幂每一位×8的幂
- 例如:141 ( 八进制 ) 141(八进制)141(八进制)
- 计算就是:
1×8²+4×8¹+1×8⁰=64+32+1=97
1 字符串使用
![]()
1-1 字符串定义
- 在 C 语言中并没有专门的
string类型,字符串本质上是字符数组,并且以\0(空字符)作为结束标志。
charstr[]="hello";//等价写法charstr[]={'h','e','l','l','o','\0'};
h e l l o \0
\0非常重要,它表示字符串结束,许多字符串函数都是依靠它来判断字符串长度的。
sizeof(str)==6
1-2 自定义字符串长度
#include<stdio.h>unsignedintmyStrlen(charstr[]){unsignedinti=0;while(str[i]!='\0'){i++;}returni;}intmain(){charstr[]="hello";printf("字符串的长度为%u",myStrlen(str));return0;}
1-3 常用字符串函数
- C 语言在string.h中提供了许多字符串处理函数。
#include<string.h>
| 函数 | 作用 |
|---|
strlen(str) | 计算字符串长度 |
strcpy(a,b) | 字符串复制 |
strcat(a,b) | 字符串连接 |
strcmp(a,b) | 字符串比较 |
strlen:计算字符串长度(不计算 \0)
charstr[]="hello";printf("%d",strlen(str));sizeof(str)//-> 包含 '\0' 的数组总大小strlen(str)//-> 不包括 '\0' 的字符长度
strcpy:字符串复制。
chara[20];charb[]="hello";strcpy(a,b);
strcat:字符串连接.
chara[20]="hello ";charb[]="world";strcat(a,b);
strcmp:字符串比较
printf("%d",strcmp("abc","abd"));
| 返回值 | 含义 |
|---|
| 0 | 两字符串相等 |
| >0 | a > b |
| <0 | a < b |
1-4 字符串数组
charstr[3][10]={"apple","banana","orange"};
2 转义字符
2-1 定义
- 在字符串中,有些字符无法直接通过键盘输入,或者具有特殊含义,这时就需要使用转义字符(Escape Character)。
- 转义字符以反斜杠
\开头。例如:
printf("hello\nworld");
2-2 常见转义字符
| 转义字符 | 含义 |
|---|
\n | 换行 |
\t | 水平制表符(Tab) |
\\ | 输出反斜杠\ |
\" | 输出双引号" |
\' | 输出单引号' |
\0 | 字符串结束符 |
printf("hello\nworld\n");printf("hello\tworld\n");printf("\\n 表示换行\n");
hello world hello world \n 表示换行
2-3 八进制转义字符
\ooo
printf("\141\n");
- 输出结果为:
a,因为141(八进制) = 97(十进制),而ASCII 码97对应字符为a
2-4 十六进制转义字符
\xhh
printf("\x61\n");
- 输出结果为:
a,因为61(十六进制)= 97(十进制)
2-5 复杂例子
chars[]="a\128b\\\tcd\xdg\n";printf("%d",strlen(s));
a \12//(八进制转义字符只能使用数字 0~7)8b \//(//转义为一个/)\t c d \xd//十六进制转义字符只能使用0~9 A~F a~fg \n
3 字符串输入
- 在 C 语言中,字符串本质是字符数组,所以输入字符串的方式和数组类似。常用方法主要有:
scanfgets(不推荐)fgets(安全推荐)
3-1 使用scanf输入字符串
#include<stdio.h>intmain(){charstr[20];printf("请输入字符串:");scanf("%s",str);printf("你输入的字符串是:%s\n",str);return0;}
- 注意事项:
%s会自动在字符串末尾加上\0- 遇到空格就结束输入
3-2 使用gets(不推荐)
charstr[20];gets(str);
- 特点:
- 可以读取空格
- 遇到换行符
\n就停止读取 - 不安全:无法限制输入长度,容易导致缓冲区溢出
gets函数已经被弃用
3-3 使用fgets(安全推荐)
#include<stdio.h>intmain(){charstr[20];printf("请输入字符串:");fgets(str,sizeof(str),stdin);printf("你输入的字符串是:%s",str);return0;}
- 说明:
- 第二个参数是数组大小
- 第三个参数是输入流,一般为
stdin - 遇到换行符
\n就停止读取 - 会把换行符
\n一起读入,如果不想要可以用:
str[strcspn(str,"\n")]='\0';
3-5 字符输出函数
3-5-1getchar()函数
- 功能:从标准输入(通常是键盘)读取 一个字符,返回类型为 int。
- 函数原型:
intgetchar(void);
#include<stdio.h>intmain(){intc;printf("请输入一个字符:");c=getchar();// 读取一个字符printf("你输入的字符是:%c\n",c);return0;}
- 注意事项:
- 返回类型为 int,而不是 char,是为了能表示 EOF(-1)。
- 每次调用只读取 一个字符(包括空格、换行符)。
- 可以配合循环逐个处理字符:
intch;while((ch=getchar())!=EOF){putchar(ch);// 回显输入的字符}
3-5-2putchar()函数
- 功能:向标准输出(通常是屏幕)输出 一个字符。
- 函数原型:
intputchar(intc);
#include<stdio.h>intmain(){charch='A';putchar(ch);// 输出 Aputchar('\n');// 输出换行return0;}
- 特点:
- 可以与 getchar 搭配使用,实现字符回显或处理:
#include<stdio.h>intmain(){intch;printf("输入字符,按 Ctrl+D (Linux) 或 Ctrl+Z (Windows) 结束:\n");while((ch=getchar())!=EOF){putchar(ch);// 输出输入的字符}return0;}
4 字符串与字符数组
4-1 定义区别
charstr1[]={'h','e','l','l','o','\0'};
char*str2="hello";
| 特性 | char str[] | char *str |
|---|
| 存储位置 | 数组在栈(局部变量)或全局区 | 字符串字面量在只读区 |
| 是否可修改 | 可修改(如str[0]='H') | 不可修改(修改会导致未定义行为) |
| 长度计算 | 可用sizeof(str)得到数组大小 | sizeof(str)得到指针大小,需要strlen(str) |
4-2与函数结合
voidprintArray(chararr[]){...}printArray(str1);// 传递的是首元素地址
voidmodify(chararr[]){arr[0]='H';}modify(str1);// str1[0]变为'H'
voidmodify(char*s){s[0]='H';// 未定义行为,通常会崩溃}modify(str2);
5 编译预处理
![]()
5-1 定义
- 编译预处理(Preprocessing)是 C 语言编译的第一步,在正式编译前会进行:
- 宏定义展开
- 头文件包含
- 条件编译
- 行号信息处理
- 预处理由预处理器(cpp)完成,指令以
#开头。
5-2 常用预处理指令
| 指令 | 作用 | 示例 |
|---|
#define | 定义宏 | #define PI 3.14 |
#undef | 取消宏定义 | #undef PI |
#include | 引入头文件 | #include <stdio.h> |
#if/#ifdef/#ifndef/#else/#elif/#endif | 条件编译 | 见示例 |
#error | 报错信息 | #error "必须定义宏" |
#pragma | 特定编译器指令 | #pragma once |
5-3 宏函数
- 宏函数(Macro Function)其实是通过
#define定义的带参数的宏,它本质上并不是函数,而是在预处理阶段进行文本替换。 - 语法:
#define宏名(参数1,参数2,...)替换内容
#defineSQUARE(x)((x)*(x))
- 这里
SQUARE(x)是宏函数 (x)*(x))防止宏被放到更复杂表达式中时优先级错误。- 宏展开时会直接替换:
inta=5;intb=SQUARE(a);// 编译前会变成 ((a)*(a))
| 特性 | 说明 |
|---|
| 预处理展开 | 宏函数在编译前被展开为文本,不会生成函数调用 |
| 无类型检查 | 宏参数不会检查类型,容易出错 |
| 效率高 | 没有函数调用开销,但可能导致代码膨胀 |
| 可能产生副作用 | 如果参数中有表达式,可能被重复计算 |
| 特性 | 宏函数 | 普通函数 |
|---|
| 调用开销 | 无 | 有函数调用开销 |
| 类型检查 | 无 | 有类型检查 |
| 编译阶段 | 预处理阶段 | 编译/链接阶段 |
| 表达式副作用 | 可能重复计算 | 安全,不重复 |
5-3-1 宏函数题目(为什么会考这种啊喂)
#defineLEAP_YEAR(y)((((y)%100!=0)&&((y)%4==0))||((y)%400==0))
5-4 条件编译
| 指令 | 作用 |
|---|
#if 常量表达式 | 如果表达式非零,编译该代码块 |
#ifdef 宏名 | 如果宏已定义,编译该代码块 |
#ifndef 宏名 | 如果宏未定义,编译该代码块 |
#elif 常量表达式 | #if的 else-if 分支 |
#else | 前面的条件都不满足时编译该代码块 |
#endif | 结束条件编译块 |
5-4-1ifdef
#include<stdio.h>#defineDEBUGintmain(){#ifdefDEBUGprintf("调试模式开启\n");#endifprintf("程序运行\n");return0;}
调试模式开启 程序运行
- 如果注释掉
#define DEBUG,就不会输出“调试模式开启”。
5-4-2ifndef
#ifdef/#ifndef仅检查宏是否定义,不考虑宏值
#include<stdio.h>#ifndefVERSION#defineVERSION1#endifintmain(){printf("版本号: %d\n",VERSION);return0;}
- 如果
VERSION未定义,则定义为 1。 - 如果已经定义,则保留原定义。
5-4-3#if / #elif / #else / #endif
#if/#elif需要常量表达式,不可以直接写变量。
#include<stdio.h>#definePLATFORM2intmain(){#ifPLATFORM==1printf("Windows 平台\n");#elifPLATFORM==2printf("Linux 平台\n");#elseprintf("其他平台\n");#endifreturn0;}
5-5 文件包含
#include<stdio.h>
#include"myheader.h"
- 区别:
< >:系统目录查找" ":先当前目录,再系统目录查找
5-6 常用头文件
| 头文件 | 主要内容 |
|---|
<stdio.h> | 输入输出函数,如printf(),scanf(),fgets(),fputs() |
<stdlib.h> | 常用工具函数,如malloc(),free(),exit(),atoi(),rand() |
<string.h> | 字符串处理函数,如strlen(),strcpy(),strcat(),strcmp(),memset(),memcpy(),memcmp() |
<math.h> | 数学函数,如sqrt(),pow(),sin(),cos(),fabs() |
<ctype.h> | 字符处理函数,如isdigit(),isalpha(),toupper(),tolower() |
<time.h> | 时间函数,如time(),clock(),difftime(),strftime() |
<limits.h> | 定义各种数据类型的边界值,如INT_MAX,CHAR_MIN |
<float.h> | 定义浮点数相关的边界,如FLT_MAX,DBL_MIN |
<stdbool.h> | 布尔类型支持:true和false |
<assert.h> | 断言宏assert(),用于调试 |
小结
- 本节主要介绍了 C 语言的字符串使用、转义字符、字符串输入与数组区别,以及编译预处理相关内容,包括宏定义、宏函数、条件编译和头文件包含。
- 下一节我们将从
结构体开始 - 如有错误,欢迎指出!