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

前言


0 前置知识-进制转换

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

0-3 八进制 → 十进制(手算方法)
  • 同样的道理,八进制基数是 80~7
  • 计算方法: 每一位 × 8 的幂 每一位 × 8 的幂 每一位×8的幂
  • 例如: 141 ( 八进制 ) 141(八进制) 141(八进制)
  • 计算就是:
c 复制代码
1 × 8² + 4 × 8¹ + 1 × 8⁰  
= 64 + 32 + 1  
= 97

1 字符串使用

1-1 字符串定义
  • 在 C 语言中并没有专门的 string 类型,字符串本质上是字符数组 ,并且以 \0(空字符)作为结束标志
c 复制代码
char str[] = "hello";
//等价写法
char str[] = {'h','e','l','l','o','\0'};
  • 在内存中实际存储为:
c 复制代码
h e l l o \0
  • \0 非常重要,它表示 字符串结束,许多字符串函数都是依靠它来判断字符串长度的。
c 复制代码
sizeof(str)==6

1-2 自定义字符串长度
  • 字符串长度可以通过遍历 \0 计算。
c 复制代码
#include <stdio.h>

unsigned int myStrlen(char str[])
{
    unsigned int i = 0;
    while (str[i] != '\0')
    {
        i++;
    }
    return i;


}
int main()
{
    char str[] = "hello";
    printf("字符串的长度为%u", myStrlen(str));
    return 0;
}
  • 当然我们平常肯定用内置的函数

1-3 常用字符串函数
  • C 语言在 string.h 中提供了许多字符串处理函数。
c 复制代码
#include <string.h>
  • 常用函数如下:
函数 作用
strlen(str) 计算字符串长度
strcpy(a,b) 字符串复制
strcat(a,b) 字符串连接
strcmp(a,b) 字符串比较
  1. strlen:计算字符串长度(不计算 \0)
c 复制代码
char str[] = "hello";  
printf("%d", strlen(str));

sizeof(str) //-> 包含 '\0' 的数组总大小
strlen(str) //-> 不包括 '\0' 的字符长度
  1. strcpy:字符串复制。
c 复制代码
char a[20];
char b[] = "hello";

strcpy(a, b);
  1. strcat:字符串连接.
c 复制代码
char a[20] = "hello ";
char b[] = "world";

strcat(a, b);
  1. strcmp:字符串比较
c 复制代码
printf("%d", strcmp("abc","abd"));
返回值 含义
0 两字符串相等
>0 a > b
<0 a < b

1-4 字符串数组
  • 可以定义字符串数组存储多个字符串。
c 复制代码
char str[3][10] = {
    "apple",
    "banana",
    "orange"
};

2 转义字符

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

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

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

2-4 十六进制转义字符
  • 格式如下:
c 复制代码
\xhh
  • 其中:
    • hh为十六进制数
  • 举例:
c 复制代码
printf("\x61\n");
  • 输出结果为:a,因为61(十六进制)= 97(十进制)

2-5 复杂例子
  • 我们来看一个稍复杂的例子:
c 复制代码
char s[] = "a\128b\\\tcd\xdg\n";  
printf("%d", strlen(s));
  • 展开分析为:
c 复制代码
a
\12 //(八进制转义字符只能使用数字 0~7)
8
b
\  //(//转义为一个/)
\t
c
d
\xd //十六进制转义字符只能使用0~9 A~F a~f
g
\n
  • 故答案为11

3 字符串输入

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

3-1 使用 scanf 输入字符串
c 复制代码
#include <stdio.h>

int main()
{
    char str[20];

    printf("请输入字符串:");
    scanf("%s", str);

    printf("你输入的字符串是:%s\n", str);

    return 0;
}
  • 注意事项:
    1. %s 会自动在字符串末尾加上 \0
    2. 遇到空格就结束输入

3-2 使用 gets(不推荐)
c 复制代码
char str[20];  
gets(str);
  • 特点:
    • 可以读取空格
    • 遇到换行符 \n 就停止读取
    • 不安全:无法限制输入长度,容易导致缓冲区溢出
  • gets 函数已经被弃用

3-3 使用 fgets(安全推荐)
c 复制代码
#include <stdio.h>

int main()
{
    char str[20];

    printf("请输入字符串:");
    fgets(str, sizeof(str), stdin);

    printf("你输入的字符串是:%s", str);

    return 0;
}
  • 说明:

    • 第一个参数是存储字符串的数组
    • 第二个参数是 数组大小
    • 第三个参数是输入流,一般为 stdin
    • 遇到换行符 \n 就停止读取
    • 会把换行符 \n 一起读入,如果不想要可以用:
c 复制代码
str[strcspn(str, "\n")] = '\0';

4 字符串与字符数组

4-1 定义区别
  • 字符数组
c 复制代码
char str1[] = {'h','e','l','l','o','\0'};
  • 字符串字面量指针
c 复制代码
char *str2 = "hello";
特性 char str[] char *str
存储位置 数组在栈(局部变量)或全局区 字符串字面量在只读区
是否可修改 可修改(如 str[0]='H' 不可修改(修改会导致未定义行为)
长度计算 可用 sizeof(str) 得到数组大小 sizeof(str) 得到指针大小,需要 strlen(str)
4-2与函数结合
  • 数组名作为实参传递:
c 复制代码
void printArray(char arr[]) { ... }  
printArray(str1); // 传递的是首元素地址
  • 修改函数内部的内容会影响原数组:
c 复制代码
void modify(char arr[]) {  
    arr[0] = 'H';  
}  
modify(str1); // str1[0]变为'H'
  • 字符串字面量指针传入函数:
c 复制代码
void modify(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 定义的带参数的宏 ,它本质上并不是函数,而是 在预处理阶段进行文本替换
  • 语法:
c 复制代码
#define 宏名(参数1, 参数2, ...) 替换内容
  • 例子:
c 复制代码
#define SQUARE(x) ((x)*(x))
  • 这里 SQUARE(x) 是宏函数
  • (x)*(x)) 防止宏被放到更复杂表达式中时优先级错误。
  • 宏展开时会直接替换:
c 复制代码
int a = 5;
int b = SQUARE(a); // 编译前会变成 ((a)*(a))
特性 说明
预处理展开 宏函数在编译前被展开为文本,不会生成函数调用
无类型检查 宏参数不会检查类型,容易出错
效率高 没有函数调用开销,但可能导致代码膨胀
可能产生副作用 如果参数中有表达式,可能被重复计算
  • 总之这东西一般不太可能会用((了解为主))
特性 宏函数 普通函数
调用开销 有函数调用开销
类型检查 有类型检查
编译阶段 预处理阶段 编译/链接阶段
表达式副作用 可能重复计算 安全,不重复
5-3-1 宏函数题目(为什么会考这种啊喂)
  • 定义一个宏,用于判断所给出年份是否喂闰年
c 复制代码
#define LEAP_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
c 复制代码
#include <stdio.h>

#define DEBUG

int main() {
#ifdef DEBUG
    printf("调试模式开启\n");
#endif

    printf("程序运行\n");
    return 0;
}
  • 输出:
c 复制代码
调试模式开启  
程序运行
  • 如果注释掉 #define DEBUG,就不会输出"调试模式开启"。
5-4-2 ifndef
  • #ifdef / #ifndef 仅检查宏是否定义,不考虑宏值
c 复制代码
#include <stdio.h>

#ifndef VERSION
#define VERSION 1
#endif

int main() {
    printf("版本号: %d\n", VERSION);
    return 0;
}
  • 如果 VERSION 未定义,则定义为 1。
  • 如果已经定义,则保留原定义。
5-4-3 #if / #elif / #else / #endif
  • #if / #elif 需要常量表达式,不可以直接写变量。
c 复制代码
#include <stdio.h>

#define PLATFORM 2

int main() {
#if PLATFORM == 1
    printf("Windows 平台\n");
#elif PLATFORM == 2
    printf("Linux 平台\n");
#else
    printf("其他平台\n");
#endif

    return 0;
}

5-5 文件包含
  • 系统头文件
c 复制代码
#include <stdio.h>
  • 用户自定义头文件
c 复制代码
#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 语言的 字符串使用转义字符字符串输入与数组区别 ,以及 编译预处理相关内容 ,包括 宏定义、宏函数、条件编译和头文件包含
  • 下一节我们将从结构体开始
  • 如有错误,欢迎指出!
相关推荐
程序员敲代码吗1 小时前
DVR设备FTP更新故障及修复指南
服务器·开发语言·php
Never_Satisfied1 小时前
在JavaScript / HTML中,获取指定元素的父元素
开发语言·javascript·html
wjs20242 小时前
CSS 伪类详解
开发语言
sichuanwuyi2 小时前
wydevops——最佳应用场景解析
java·开发语言·云原生·云计算·paas·devops
晚枫歌F2 小时前
跳表Skip List以及实现代码C语言
c语言·开发语言
少控科技2 小时前
C#基础学习 - 中国民族编码资源代码
开发语言·c#
宵时待雨2 小时前
C++笔记归纳8:stack & queue
开发语言·数据结构·c++·笔记·算法
小年糕是糕手2 小时前
【35天从0开始备战蓝桥杯 -- Day2】
开发语言·jvm·数据库·c++·程序人生·考研·蓝桥杯
Gold Steps.2 小时前
Go 语言核心:函数、结构体与接口深度解析
开发语言·后端·golang