当前位置: 首页 > news >正文

【C语言简明教程提纲】(三):字符串与编译预处理

前言

  • 本系列C语言教程将补充C语言的基础部分,适合具有一定语言基础的朋友进行复习。
  • 第一期: 【C语言简明教程】(一):数据类型,表达式与控制结构-CSDN博客
  • 第二期: 【C语言简明教程提纲】(二):函数你值得拥有-CSDN博客
  • 以下是本系列的目录
    • 字符串使用(本节)
    • 编译预处理(本节)
    • 数组与函数
    • 数组定义和引用
    • 指针定义和使用
    • 指针与数组、与字符串、与函数
    • 结构体定义、结构体数组、结构体指针
    • 文件定义和操作。
    • 常用算法
  • 关于数值和指针两个难点,可以参考之前写的文章,浅显易懂,适合初学者的朋友
    • 【C语言备课课件】(上)数组array
    • 【C语言备课课件】(下)指针pointer

0 前置知识-进制转换

0-1 进制表
  • 在 C 语言中,字符串的八进制转义字符十六进制转义字符本质上是ASCII 数值表示,因此需要简单了解进制转换。
0-2 十六进制 → 十进制(手算方法)
  • 十六进制的基数是160~9a~f
  • 计算方法:每一位 × 16 的幂 每一位 × 16 的幂每一位×16的幂
  • 例如:0 x 41 0x410x41
  • 计算就是
4×16¹+1×16=64+1=65

0-3 八进制 → 十进制(手算方法)
  • 同样的道理,八进制基数是80~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 自定义字符串长度
  • 字符串长度可以通过遍历\0计算。
#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)字符串比较
  1. strlen:计算字符串长度(不计算 \0)
charstr[]="hello";printf("%d",strlen(str));sizeof(str)//-> 包含 '\0' 的数组总大小strlen(str)//-> 不包括 '\0' 的字符长度
  1. strcpy:字符串复制。
chara[20];charb[]="hello";strcpy(a,b);
  1. strcat:字符串连接.
chara[20]="hello ";charb[]="world";strcat(a,b);
  1. strcmp:字符串比较
printf("%d",strcmp("abc","abd"));
返回值含义
0两字符串相等
>0a > b
<0a < b

1-4 字符串数组
  • 可以定义字符串数组存储多个字符串。
charstr[3][10]={"apple","banana","orange"};

2 转义字符

2-1 定义
  • 在字符串中,有些字符无法直接通过键盘输入,或者具有特殊含义,这时就需要使用转义字符(Escape Character)
  • 转义字符以反斜杠\开头。例如:
printf("hello\nworld");
  • 其中的\n表示换行符

2-2 常见转义字符
转义字符含义
\n换行
\t水平制表符(Tab)
\\输出反斜杠\
\"输出双引号"
\'输出单引号'
\0字符串结束符
  • 举例:
printf("hello\nworld\n");printf("hello\tworld\n");printf("\\n 表示换行\n");
  • 输出:
hello world hello world \n 表示换行

2-3 八进制转义字符
  • 转义字符还可以表示ASCII 码字符。格式如下:
\ooo
  • 其中ooo为 1~3 位八进制数
  • 举例:
printf("\141\n");
  • 输出结果为:a,因为141(八进制) = 97(十进制),而ASCII 码97对应字符为a

2-4 十六进制转义字符
  • 格式如下:
\xhh
  • 其中:
    • hh为十六进制数
  • 举例:
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
  • 故答案为11

3 字符串输入

  • 在 C 语言中,字符串本质是字符数组,所以输入字符串的方式和数组类似。常用方法主要有:
    1. scanf
    2. gets(不推荐)
    3. fgets(安全推荐)

3-1 使用scanf输入字符串
#include<stdio.h>intmain(){charstr[20];printf("请输入字符串:");scanf("%s",str);printf("你输入的字符串是:%s\n",str);return0;}
  • 注意事项:
    1. %s会自动在字符串末尾加上\0
    2. 遇到空格就结束输入

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 语言编译的第一步,在正式编译前会进行:
    1. 宏定义展开
    2. 头文件包含
    3. 条件编译
    4. 行号信息处理
  • 预处理由预处理器(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 条件编译
  • C 语言提供几个常用的条件编译指令:
指令作用
#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>布尔类型支持:truefalse
<assert.h>断言宏assert(),用于调试

小结

  • 本节主要介绍了 C 语言的字符串使用转义字符字符串输入与数组区别,以及编译预处理相关内容,包括宏定义、宏函数、条件编译和头文件包含
  • 下一节我们将从结构体开始
  • 如有错误,欢迎指出!
http://www.jsqmd.com/news/474922/

相关文章:

  • 【OpenClaw】Edict 三省六部制使用与实战流程
  • Tao-8k模型API调用异常处理大全:从403 Forbidden到连接超时
  • 从R到Posit:数据科学家的现代统计计算环境全解析
  • Xray实战指南:从零构建自动化Web漏洞扫描体系
  • 乐鑫Wi-Fi模组量产测试:信号板方案原理与工程落地
  • 数据中心网络工程师必备:BGP与VXLAN EVPN协同配置全解析
  • ESP32-S3-WROOM-1与WROOM-1U模组硬件解析与工程落地指南
  • Transformer模型、整体结构,编码器与解码器内部组成
  • 手把手教你用MedGemma-X:AI影像诊断助手5分钟快速部署
  • OpenCode场景应用:程序员通勤路上用手机写代码,回家无缝衔接
  • 内联函数,函数的缺省值,函数重载,右值引用
  • 谷歌Gemini Pro API vs ChatGPT API:免费、配置难度与性能对比
  • AI 辅助开发实战:高效完成基于 Spring Boot 的 JavaWeb 毕设项目
  • PROJECT MOGFACE企业级部署:基于Docker与内网穿透的高可用架构
  • 手把手教你解决Vulhub环境搭建中的docker-compose up -d报错(含CentOS联网技巧)
  • C语言快速入门9-指针
  • 补天漏洞响应平台:白帽子与企业安全合作的桥梁
  • Windows下MissionPlanner地面站编译避坑指南:从Git克隆到VS2022完整流程
  • 从linux内核理解Java怎样实现Socket通信
  • CLAP模型在农业领域的创新应用:病虫害声音早期预警
  • 从STM32到语音交互:CosyVoice在嵌入式设备语音提示系统中的应用构想
  • 手机省电技巧|告别电量焦虑,一天一充不是梦
  • STM32 RTC数字校准、时间戳与低功耗机制全栈解析
  • PLSQL连接Oracle报ORA-12541?5个常见原因及快速排查方法
  • UiPath离线激活全流程:从生成Token到成功激活的保姆级教程
  • HttpCanary实战指南:从零开始掌握Android HTTPS抓包技巧
  • STM32 SPI/I2S状态机与安全停机机制深度解析
  • 《QGIS快速入门与应用基础》215:批量应用标注样式
  • 【项目实战】如何将接口传过来的html文件通过WPF控件展示在桌面应用程序?
  • 用Unity物理引擎还原真实赛车手感:齿轮变速+悬挂系统调试指南