C语言与数据结构基本知识点阐述笔记
C语言与数据结构基本知识点阐述笔记?
看目录之前点击这个链接{ https://github.com/FRANK0174/Simple_Note},其中还有许多关于C语言、数据结构的资料,并且拥有笔记软件安装包(Typora破解版),最后请在我的主页上点击⭐️支持一下吧。
Los, raus mit dir. Beweise, dass du besser bist als messi. ——Hans lefferts trainer der fußball-weltmeisterschaft 2014
文章目录
- C语言与数据结构基本知识点阐述笔记:rocket:
-
-
-
- 数据类型
-
-
- 1.基本数据类型:
- 2.构造类型(Derived Types):
- 3.空类型(Void Type):
- 4.指针类型(Pointer Types):
-
- 语句的分类
-
-
- 1.赋值语句:
- 2.条件语句:
- 3.循环语句:
- 4.函数调用语句:
- 5.跳转语句:
- 6.异常处理语句(部分语言支持):
-
- C格式说明符
- 流程图表示算法
-
-
- 1.顺序结构
- 2.选择结构
- 3.循环结构
-
- 伪代码
-
-
- 1. 自然语言结构:
- 伪代码使用自然语言结构,使得算法的描述更加清晰和易读。
- 2. 无需关注具体语法:
- 3. 结构化描述:
- 4. 模块化设计:
- 5. 可读性和可理解性:
- 6. 算法设计与讨论:
-
- 进制转换
-
-
- 1.进制的权(The right to base)
- 2.以十进制为中心的转换
- 其他进制–>十进制
- 3.以二进制(Decimalism 缩写:DEC)为中心进行转换
-
- 算术移位
-
-
- 1.算术左移 (Arithmetic Left Shift):
- 2.算术右移 (Arithmetic Right Shift):
- 3.逻辑左移 (Logical Left Shift):
- 4.逻辑右移 (Logical Right Shift):
- 5.算数移位的妙用
-
- 输入输出函数
-
- 输入函数:
- 输出函数:
- 字符与字符串
-
-
- 1.字符(Char):
- 2. 字符串(String):
- 3. 注意事项:
-
- 局部变量与全局变量
-
-
- 1.局部变量
- 2.全局变量
-
- typedef关键字的使用
-
-
- 1.基本数据类型的别名:
- 2.结构体的别名:
- 3.函数指针的别名:
- 4.枚举的别名:
- 5.指针类型的别名:
-
- 指针
-
-
- 1.指针的声明和定义:
- 2.指针的解引用:
- 3.指针的运算:
- 4.指针与数组:
- 5.指针与函数:
- 6.指针与动态内存分配:
- 7.指针的 NULL 值:
- 8.指针的安全性和悬空指针:
-
- 指针的基本分类
-
-
- 1. 基本指针用法:
- 2. 指针数组:
- 3. 指向函数的指针:
- 4. 指针的指针:
- 5. const 指针:
- 6. void 指针:
-
- 字符串与字符指针
-
-
- 1. 字符串:
- 2. 字符指针:
- 3. 区别:
-
- 函数(Function)的意义及其细节
-
-
- 1.意义:
- 2.要求
- 3.注意事项
-
- C库函数的基本实现
-
-
- 1.strcpy函数
- 2.strlen函数
-
- 二维数组
-
-
- 1. 声明和初始化:
- 2. 访问元素:
- 3. 内存布局:
- 4. 多维数组的概念:
- 5. 传递给函数:
- 6. 动态分配内存:
- 7. 注意事项:
-
- C各区的定义
-
-
- 1.栈(Stack):
- 2.堆(Heap):
- 3.全局/静态区(Global/Static Area):
- 4.文字常量区(Text/Code Segment):
- 5.常量区(Constant Area):
- 6.堆栈区(BSS,Block Started by Symbol):
- 7.寄存器(Register):
-
- 为什么将堆(Heap)和栈(Stack)分开:
-
-
- 1.内存管理灵活性:
- 2.局部变量的生命周期管理:
- 3.栈的高效性:
- 4.堆的灵活性和扩展性:
- 5.安全性和稳定性:
-
- register关键字
-
-
- 1.建议性关键字:
- 2.限制条件:
- 3.不再常用:
- 4.编译器优化:
- 5.register关键字使用的条件
-
- C无内存常量
-
-
- 1.字面常量(Literal Constants):
- 2.符号常量(Symbolic Constants):
- 3.枚举常量(Enum Constants):
- 4.寄存器常量(Register constant)
- 5.普通定义的常量是有内存的
-
- 数组函数的使用
-
-
- 1.数组或字符串的长度:`sizeof()`、`strlen()`
- 2.`sizeof()`、`strlen()`两者区别:
- 3.`string`中`length()`和`size()`
-
- main函数的参数
-
-
- 1.不带参数的 `main` 函数:
- 2.带参数的 `main` 函数:
-
- 抽象状态机的定义和代码
-
-
- 1.有限状态机的基本组成部分:
- 2.状态机的代码示例(使用C语言):
-
- 递归
-
-
- 1.递归的基本原理:
- 2.递归的示例:
- 3.递归的优点与注意事项:
- 4.递归的详细说明:
- 5.递归的条件
- 6.汉诺塔问题:
-
- C代码运行的细节:
-
-
- 1. 编写源代码:
- 2. 预处理(Preprocessing):
- 3. 编译(Compilation):
- 4.链接(Linking):
- 5. 加载(Loading):
- 6. 运行时(Runtime):
- 7. 结束运行:
-
- 文件
-
-
- 1.文件指针
- 2.文件操作指令
-
- 预处理器
-
-
- 1.宏替换:
- 2.条件编译:
- 3.文件包含:
- 4.条件编译宏:
- 4.字符串化:
- 5.宏连接:
- 6.去掉注释:
- 7.预定义宏:
- 8.头文件保护:
-
- define关键字的使用
-
-
- 1.定义常量:
- 2.定义函数宏:
- 3.条件编译:
- 4.条件编译中的宏定义:
- 5.使用宏定义进行代码替换:
- 6.字符串连接:
- 7.条件宏定义:
-
- 预定义宏
-
-
- 1. `__FILE__`:
- 2. `__LINE__`:
- 3. `__DATE__`:
- 4. `__TIME__`:
- 5. `__func__`:
- 6. `__STDC__`:
- 7. `__cplusplus`:
- 8. `__PRETTY_FUNCTION__`(GCC 特有):
-
- 错误输出
-
-
- 1.使用`fprintf`函数:
- 2.使用`perror`函数:
- 3.使用`assert`宏:
- 4.使用`exit`函数:
-
- C可变参数
-
-
- 1.使用`stdarg.h`头文件:
- 2.创建可变参数函数:
- 3.示例:计算可变参数的和
-
- C内存管理
-
-
- 1.内存分配函数:
- 2.内存释放函数:
- 3.内存重新分配函数:
- 4.内存管理的注意事项:
-
- System()函数详解
-
-
- 1.使用示例:
- 2.注意事项:
- 3.命令一揽表
-
- C枚举
-
-
- 1.定义一个枚举类型:
- 2.使用枚举类型:
- 3.枚举成员的整数值:
- 4.访问枚举成员的值:
- 5.枚举的用途:
-
- C函数指针和回调函数
-
-
- 1.指向函数的指针
- 2.函数指针作为函数的参数
- 3.函数指针作为函数返回类型
- 4.函数指针数组
- 5.回调函数的定义:
-
- 位域
-
-
- 1.位域的定义:
- 2.位域的操作:
- 3.注意事项:
-
- 共用体
-
-
- 1.共用体的定义:
- 2.共用体的大小:
- 3.共用体的使用:
- 4.共用体的应用:
-
- C结构体
-
-
- 1.结构体的定义
- 2.结构体作为参数
- 3.指向结构体的指针
-
- . 与->在c语言中的区别
-
-
- 1.`.`(点运算符):
- 2.**`->`(箭头运算符)**:
- 3.总结:
-
- 左值和右值的区别
-
-
- 1.左值(`Lvalue`):
- 2.右值(`Rvalue`):
- 3.区别和用途:
-
- 布尔运算
-
-
- 1. 基本布尔运算符:
- **与运算(AND):**
- **或运算(OR):**
- **非运算(NOT):**
- 2. 布尔代数定律:
- **结合律:**
- **分配律:**
- **恒等律:**
- **零律:**
- 3. 布尔表达式:
- 4. 真值表:
- **真值表列出了布尔表达式对应的所有输入值的可能组合及其输出。**
- 5. 应用场景:
- **逻辑电路设计:**
- **计算机程序设计:**
- **数据库查询:**
- **网络和通信:**
- 6.总结:
-
- 原码和反码及补码的存在意义
-
-
- 1.存在意义
- 前提:
- 解释 :
- 2.计算方法
-
- 时间复杂度
-
-
- 表示方法:
- 常见时间复杂度:
- 选择时间复杂度的原则:
- 例子:
-
- 算法的意义及其简单应用
-
-
- 1.算法的意义
- 2.简单应用
-
- 数据结构简单代码
-
- 线性表篇
-
- 顺序表
- 单链表
- 单循环链表
- 双链表
- 双循环链表
- 静态链表
- 栈与队列篇
-
- 队列
- 循环队列
- 双端队列
- 链表栈
- 数组栈
- 串篇
-
- 链表串
- KMP前缀表计算函数
- 暴力匹配函数
- 树篇
-
- 二叉树
- 二叉排序树
- 平衡二叉树
- 霍夫曼树
- 图篇
-
- 有向图
- 无向图
- 加权图
- 无向完全图
- 有向完全图
- 查找和排序
-
- 查找系列
-
- 顺序查找(哨兵版)
- 折半查找
- 插值查找
- 斐波那契查找
- 哈希查找
- 分块查找
- 二叉树查找
- 排序系列
-
- 冒泡排序
- 选择排序
- 插入排序
- 快速排序
- 归并排序
- 基数排序
- 堆排序
- 希尔排序
- 桶排序
- 计数排序
- 你最爱的题库:heart_eyes:([【C语言】机试100题及代码答案(上)_c语言编程100题及答案-CSDN博客](https://blog.csdn.net/isak233/article/details/128767745?ops_request_misc=%7B%22request%5Fid%22%3A%22170651775316800182736948%22%2C%22scm%22%3A%2220140713.130102334..%22%7D&request_id=170651775316800182736948&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-5-128767745-null-null.142^v99^pc_search_result_base7&utm_term=c语言题库&spm=1018.2226.3001.4187))
-
-
数据类型
1.基本数据类型:
-
整数类型(Integer Types):
- int:通常为32位整数(取决于编译器和系统)。
- short:短整数,通常为16位。
- long:长整数,通常为32位,也可能是64位。
- long long:长长整数,通常为64位。
int a = 10; short b = 32767; long c = 100000L; long long d = 123456789012345LL;
-
浮点类型(Floating-Point Types):
- float:单精度浮点数。
- double:双精度浮点数,通常为64位。
- long double:长双精度浮点数,大小可能超过double。
float x = 3.14f; double y = 123.456; long double z = 3.141592653589793238L;
2.构造类型(Derived Types):
-
数组(Arrays):
- 由相同类型的元素组成的集合。
int numbers[5] = { 1, 2, 3, 4, 5}; -
结构体(Structures):
- 允许将不同类型的数据组合在一起。
struct Point { int x; int y; }; struct Point p1 = { 10, 20}; -
共用体(Unions):
- 允许使用相同的内存位置存储不同类型的数据。
union Data { int i; float f; char str[20]; }; union Data data; -
枚举(Enumerations):
- 用于定义用户定义的枚举类型。
enum Color { RED, GREEN, BLUE }; enum Color selectedColor = BLUE;
3.空类型(Void Type):
-
void:
- 表示没有数据类型。
void myFunction() { // 函数没有返回值 }
4.指针类型(Pointer Types):
-
指针(Pointers):
- 存储其他变量的内存地址。
int num = 42; int *ptr = # // ptr存储num的地址
以上是C语言中常见的数据类型。在实际编程中,选择合适的数据类型取决于程序的需求和所处理的数据。C语言的数据类型系统提供了足够的灵活性,使得程序员能够高效地管理内存和处理不同类型的数据。
语句的分类
在编程语言中,语句是一组指令,用于执行特定的操作。不同类型的语句执行不同的任务。以下是常见的几种语句类型:
1.赋值语句:
将一个值赋给变量。例如,x = 5;
2.条件语句:
根据条件选择性地执行不同的代码块。例如,if语句和switch语句。
if (condition) {
// code block executed if condition is true
} else {
// code block executed if condition is false
}
switch (expression) {
case value1:
// code block executed if expression equals value1
break;
case value2:
// code block executed if expression equals value2
break;
// ...
default:
// code block executed if expression doesn't match any case
}
3.循环语句:
重复执行一段代码,直到满足特定条件。例如,for、while、do-while 循环。
for (int i = 0; i < 5; i++) {
// code block executed 5 times
}
while (condition) {
// code block executed while condition is true
}
do {
// code block executed at least once, then repeated while condition is true
} while (condition);
4.函数调用语句:
调用一个函数执行特定任务。例如,printf(“Hello, World!”);
5.跳转语句:
控制程序执行流程的语句。例如,break、continue、return。
break; // 结束循环或 switch 语句的执行
continue; // 结束当前循环的本次迭代,继续下一次迭代
return 0; // 从函数中返回值,结束函数的执行
6.异常处理语句(部分语言支持):
处理运行时错误或异常的语句。例如,try、catch、throw(在一些面向对象的语言中)。
try {
// code that might throw an exception
} catch (Exception e) {
// handle the exception
}
这些是一些常见的语句类型,不同的编程语言可能支持不同的语句。每种语句类型都有其特定的语法和用法。
C格式说明符
以下是C语言中的格式说明符的详细说明,包括一些不太常见的格式说明符:
- %d:用于整数(十进制)的格式说明符。可以用于int类型。
- 示例:printf(“%d”, 42);,将输出整数 42。
- %ld:用于长整数(长整型,十进制)的格式说明符。通常用于long类型。
- 示例:printf(“%ld”, 10000000000L);
- %lld:用于长长整数(长长整型,十进制)的格式说明符。通常用于long long类型。
- 示例:printf(“%lld”, 100000000000000LL);
- %f:用于浮点数的格式说明符。通常用于float和double类型。
- 示例:printf(“%f”, 3.14159);
- %lf:用于双精度浮点数(双精度浮点型)的格式说明符。通常用于double类型。
- 示例:printf(“%lf”, 3.14159265359);
- %c:用于字符的格式说明符。通常用于char类型。
- 示例:printf(“%c”, ‘A’);
- %s:用于字符串的格式说明符。通常用于字符数组(字符串)。
- 示例:printf(“%s”, “Hello, World!”);
- %x:用于整数的十六进制格式。通常用于int类型。
- 示例:printf(“%x”, 255);,将输出十六进制值 ff。
- %X:与%x类似,但输出大写字母的十六进制值。
- %o:用于整数的八进制格式。通常用于int类型。
- 示例:printf(“%o”, 64);,将输出八进制值 100。
- %u:用于无符号整数的格式说明符。通常用于unsigned int类型。
- 示例:printf(“%u”, 12345);
- %p:用于指针的格式说明符。通常用于指向任何数据类型的指针。
- 示例:printf(“%p”, &variable);,将输出指向变量 variable 的内存地址。
- %e:用于科学计数法的浮点数表示。
- 示例:printf(“%e”, 1.23e-4);,将输出 1.230000e-04。
- %E:与%e类似,但输出大写字母的指数。
- %g:用于自动选择 %f 或 %e 来表示浮点数,以较短的形式表示。
- 示例:printf(“%g”, 0.000012345);,可能输出 1.2345e-05 或 0.000012345,取决于精度。
- %G:与%g类似,但输出大写字母的指数。
- %%:用于打印百分号字符。
- 示例:printf(“The discount is 20%%.”);,将输出 “The discount is 20%.”
这些是C语言中的常见格式说明符,可以根据需要将它们与修饰符(如宽度、精度等)结合使用,以更精细地控制格式化的输出或输入。不同的数据类型需要相应的格式说明符,确保在使用 scanf 时提供正确的地址来存储输入的数据。
%#x 是C语言中的格式说明符,通常用于在输出时以十六进制格式打印整数。这个格式说明符的作用是在输出的十六进制数字前添加前缀 0x,以明确表明数字是十六进制的。以下是一个示例:
int num = 255;
printf("%#x", num);
在这个示例中,%#x 将以十六进制格式打印整数 num,输出将是 0xff。前缀 0x 表示这是一个十六进制值。
%#x 格式说明符通常用于增加可读性,特别是在调试和输出十六进制数据时非常有用。如果省略 # 符号,输出将不包含前缀 0x。
流程图表示算法
1.顺序结构
如下图所示,ABC结构为顺序结构,先执行A框,然后执行B框,其次执行C框,是最简单的一种基本结构。
2.选择结构
选择结构又称为选取结构或分支结构。如下图所示,此结构中必包含一个判断框,根据给定的条件P是否成立而选择执行A框或B框。
3.循环结构
-
当型(while型)循环结构

-
直到型(until型)循环结构

判断这两种循环类型的的方法:“先判断,后执行”为当型循环,典型为while语句。“先执行,后判断”为直到型循环,典型为do…while语句。
伪代码
伪代码(Pseudocode)是一种高级描述性的编程语言,用于描述算法或程序的逻辑结构,而不涉及特定的编程语法。它是一种自然语言和编程语言的混合,用于表示算法的思路和逻辑,而不关心具体的编程语法规则。伪代码通常在程序设计的早期阶段使用,帮助程序员和团队理清思路,确定解决问题的方法。
以下是一些关于伪代码的使用方法的说明:
1. 自然语言结构:
-
伪代码使用自然语言结构,使得算法的描述更加清晰和易读。
Algorithm:
Initialize total to 0
For each item in the list:
Add item to total
Display total
2. 无需关注具体语法:
- 伪代码不需要关心具体的编程语法规则,因此可以用简单的方式表达算法的核心思想。
Algorithm:
Set counter to 1
While counter is less than or equal to 10:
Display "Hello, World!"
Increment counter by 1
3. 结构化描述:
- 伪代码可以采用结构化的方式描述算法的逻辑结构,包括顺序、选择和循环等结构。
Algorithm:
Input a number
If the number is even:
Display "The number is even"
Else:
Display "The number is odd"
4. 模块化设计:
- 伪代码支持模块化设计,允许程序员将算法划分为多个独立的模块,以提高代码的可读性和可维护性。
Algorithm CalculateArea: Input radius Set area to π * radius^2 Return area Algorithm Main: Call CalculateArea with radius=5 and store result in result Display "The area is " + result
5. 可读性和可理解性:
- 伪代码的主要优势在于其高度的可读性和可理解性,有助于团队协作和问题解决的讨论。
Algorithm:
For each student in the class:
If student's score is greater than 90:
Display "Excellent"
Else if student's score is greater than 70:
Display "Good"
Else:
Display "Needs improvement"
6. 算法设计与讨论:
- 伪代码在算法设计和讨论中扮演着重要角色,可以帮助团队成员更容易地理解和沟通算法的思路。
Algorithm BubbleSort:
For i from 1 to n:
For j from 0 to n - i - 1:
If list[j] > list[j + 1]:
Swap list[j] and list[j + 1]
总体而言,伪代码是一种灵活且简单的工具,用于描述算法的思路和逻辑。通过使用伪代码,程序员可以更轻松地理清复杂算法的步骤,并在团队协作中更好地分享和交流设计思想。
进制转换
1.进制的权(The right to base)
- 二进制的权是1,2,4,8,16…位是底数为2的n+1次方幂
- 十六进制的权是16,256,4096…,每前进一位是底数为16的n+1次方幂
- 八进制的权是8,64,512 …,每前进一位是底数为8的n+1次方幂
2.以十进制为中心的转换
-
其他进制–>十进制
方法:按权展开求和法
-
十进制–>其他进制
方法:除基取余逆读法
-
说明:十进制数除以基数(想转成几进制基数就是几)得到商和余数(是整数),继续用得到的商除以基数,得到商和余数,直到商为0时为止。最后倒序读取余数作为结果。
3.以二进制(Decimalism 缩写:DEC)为中心进行转换
说明:每4位二进制可以表示1位十六进制(0000-1111,0-15正好是十六进制的系数范围);
-
二进制–>十六进制(hexadecimal or HEX)
方法:4合1
说明:从后向前将每4位二进制数分为1组(如果最左侧一组不够4位,可以在前面补0);计算每组二进制对应的十六进制结果;按顺序读取结果;
-
十六进制–>二进制
方法:1分4
说明:将每位十六进制数据,拆分成4位二进制。
-
二进制–>八进制(octonary number system or OCT)
方法:3合1
说明:每3位二进制合成1位八进制 。(具体算法与二–>十六相同)
-
八进制–>二进制
方法:1分3
说明:每位八进制数拆分成3位二进制;
问题:八进制和十六进制之间相互转换。
-
中间可以使用二进制(或十进制)作为桥梁转换。
算术移位
当涉及到位移操作时,了解算术移位和逻辑移位的细节非常重要,尤其是在处理有符号整数时。下面将更详细地解释这两种操作的工作方式和使用场景。
1.算术左移 (Arithmetic Left Shift):
-
算术左移将二进制数向左移动指定的位数。移位后,最左侧的位将被舍弃,右侧将填充零。
-
算术左移等效于将整数乘以2的幂。每次左移一位,相当于乘以2。
1 v a l u e 左移时 v a l u e ∗ 2 n v a l u e 右移时 v a l u e 2 n {1} value左移时value*2^n\\ value右移时\frac{value}{2^n}\\ 1value左移时value∗2nvalue右移时2nvalue
2.算术右移 (Arithmetic Right Shift):
- 算术右移将二进制数向右移动指定的位数。移位后,最右侧的位将被舍弃,而左侧将填充与原最高位相同的位(即符号位)。如果原数是正数,那么填充零;如果原数是负数,那么填充一位1。
- 算术右移通常用于保持整数的符号。它允许负数继续保持负数,而正数保持正数。
3.逻辑左移 (Logical Left Shift):
- 逻辑左移与算术左移类似,将二进制数向左移动指定的位数。移位后,最左侧的位被丢弃,右侧填充零。
- 逻辑左移通常用于无符号整数,因为它不关心符号。
4.逻辑右移 (Logical Right Shift):
- 逻辑右移将二进制数向右移动指定的位数。移位后,最右侧的位被丢弃,左侧填充零。无论原数是正数还是负数,都填充零。
- 逻辑右移通常用于无符号整数。
在算术移位中移位的长短,会导致最高位发生变化。 然而在逻辑移位中,最高位始终不会发生变化,所以适用无符号整数。 在算术移位中移位的长短,会导致最高位发生变化。\\然而在逻辑移位中,最高位始终不会发生变化,所以适用无符号整数。 在算术移位中移位的长短,会导致最高位发生变化。然而在逻辑移位中,最高位始终不会发生变化,所以适用无符号整数。
需要特别注意的是,不同的编程语言和硬件平台可能对算术移位和逻辑移位的行为有所不同,因此在使用时要谨慎,确保了解特定环境下的移位行为。通常,算术移位在处理有符号整数时更为常见,而逻辑移位在处理无符号整数和位操作时更常见。
5.算数移位的妙用
交换两数值
a=a^b;b=b^a;a=a^b;
/*eg:a=5,b=9. a到a2名称上的变化是因为区分它们的状态,其他命名同理
第一步(a=a^b):0101->a=5,1001->b=9,1100->a1=12; a=5,b=9
第二步(b=b^a):1001->b=9,1100->a1=12,0101->b1=5;
第三步(a=a^b):1100->a1=12,0101->b1=5,1001->a2=9; a2=9,b1=5
*/
输入输出函数
在C语言中,有一些标准的输入输出函数,它们用于从用户获取输入和将数据输出到屏幕。以下是一些常见的C输入输出函数:
输入函数:
- scanf():
- 用于从标准输入(键盘)读取输入。
- 格式:scanf(“格式控制字符串”, &变量1, &变量2, …);
int num; scanf("%d", &num);
- getchar():
- 用于从标准输入读取一个字符。
- 返回读取的字符。
-
char ch; ch = getchar();
- gets():
- 用于从标准输入读取一个字符串。
- 不推荐使用,因为它没有边界检查,容易导致缓冲区溢出。
-
char str[100]; gets(str);
- fgets():
- 用于从指定的文件流读取一行字符串。
- 推荐用于读取字符串,因为它可以指定读取的最大字符数。
-
char str[100]; fgets(str, sizeof(str), stdin);
输出函数:
- printf():
- 用于将输出格式化为字符串并打印到标准输出(屏幕)。
-
int num = 10; printf("The value of num is %d\n", num);
- putchar():
- 用于将一个字符写入到标准输出。
-
char ch = 'A'; putchar(ch);
- puts():
- 用于将字符串写入到标准输出。
-
char str[] = "Hello, World!"; puts(str);
- fputs():
- 用于将字符串写入到指定的文件流。
-
char str[] = "Hello, World!"; FILE *file = fopen("output.txt", "w"); if (file != NULL) { fputs(str, file); fclose(file); }
这些是一些基本的C语言输入输出函数,它们为用户提供了从标准输入读取数据和将数据输出到屏幕或文件的功能。在实际编程中,可以根据需要选择合适的函数来完成输入输出任务。
字符与字符串
在C语言中,字符和字符串是重要的数据类型,它们用于表示文本信息。以下是对C语言中字符和字符串的详细说明:
1.字符(Char):
在C语言中,char 数据类型用于表示字符。字符是一个字母、数字或符号,用单引号括起来。
char myChar = 'A';
- 字符常量: 字符常量是用单引号括起来的单个字符。
char ch = 'X';
- 字符数组: 字符数组是由字符组成的数组,用于存储字符串。
char myString[6] = "Hello"; // 字符数组,最后一个位置为字符串结束符 '\0'
2. 字符串(String):
在C语言中,字符串实际上是一个字符数组,以空字符 ‘\0’ 结尾。
char myString[] = "Hello";
- 字符串常量: 字符串常量是由字符组成的常量,用双引号括起来。
const char* greeting = "Hello, World!";
- 字符串函数: C语言提供了许多处理字符串的库函数,如 strlen(获取字符串长度)、strcpy(拷贝字符串)、strcat(连接字符串)等。
#include
int main() {
char str1[20] = "Hello";
char str2[] = "World";
// 连接字符串
strcat(str1, str2);
// 输出:HelloWorld
printf("%s\n", str1);
// 获取字符串长度
int length = strlen(str1);
// 输出:Length: 10
printf("Length: %d\n", length);
return 0;
}
- 输入输出函数: 使用 printf 和 scanf 函数来进行字符串的输入输出。
#include
int main() {
char name[20];
// 输入字符串
printf("Enter your name: ");
scanf("%s", name);
// 输出字符串
printf("Hello, %s!\n", name);
return 0;
}
3. 注意事项:
- 字符串在内存中是连续存储的字符数组,以 ‘\0’ 结尾。
- 字符串处理时需要小心数组越界的问题。
- 字符串函数通常需要使用 头文件。
- 在C语言中,字符串是不可变的,即字符串中的字符不能被直接修改。需要使用字符数组(char array[])来实现可变字符串。
C语言中字符和字符串的处理是编程中常见的任务,对它们的理解和熟练使用是进行文本处理的关键。
//截取字符串
#include
int main(){
int hoge=0x12345678;
unsigned char* hoge_p=(unsigned char*)&hoge;
printf("%x\n",hoge_p[0]);
printf("%x\n",hoge_p[1]);
printf("%x\n",hoge_p[2]);
printf("%x\n",hoge_p[3]);
printf("%x\n",hoge_p[4]);
return 0;
}
局部变量与全局变量
1.局部变量
在一个函数内定义的变量只能在本函数内生效。同样,在一条复合语句内定义的变量也只在本复合语句内生效。以上这些被称为局部变量。
char a2(int a,int b){
int a1,b1;
{
int a2,b2; //a2,b2 只在此区域生效,3至5行代码称为复合语句。
}
}
char a3{
//现在,所有在函数a2里定义的变量都无法生效。
}
2.全局变量
在函数体之外定义的变量为全局变量,全局变量可以在本源文件内所有函数共用,定义有效范围从定义位置至本源文件结束。
#include
int a = 10;
int arr(int a) {
return a;
}
int main() {
printf("%d", arr(a));
//printf("%d", arr(b)); //a与b同样是全局变量,但是因为有效范围的不同,b不能被使用,
}
int b = 10;
typedef关键字的使用
typedef 是C和C++等编程语言中的一个关键字,用于创建新的数据类型别名,使代码更易读和理解。typedef 允许程序员定义自己的数据类型名称,以简化代码和提高可维护性。以下是typedef关键字的使用示例:
1.基本数据类型的别名:
typedef int MyInt; // 创建一个整数的别名 MyInt x = 42;
这里,MyInt 成为 int 的别名,可以使用 MyInt 来声明变量,这使得代码更具可读性。
2.结构体的别名:
typedef struct {
int x;
int y;
} Point; // 创建一个结构体的别名
Point p1;
p1.x = 5;
p1.y = 10;
这里,Point 成为了一个结构体的别名,使得声明和使用结构体更加简洁。
3.函数指针的别名:
typedef int (*MathFunction)(int, int); // 创建一个函数指针类型的别名
int add(int a, int b) {
return a + b;
}
MathFunction operation = add;
int result = operation(3, 4);
在这个示例中,MathFunction 成为一个函数指针类型的别名,它可以指向具有相同签名的函数。
4.枚举的别名:
typedef enum {
RED,
GREEN,
BLUE
} Color; // 创建一个枚举类型的别名
Color favoriteColor = GREEN;
这里,Color 成为一个枚举类型的别名,使得使用枚举值更加清晰。
5.指针类型的别名:
cCopy codetypedef int* IntPtr; // 创建一个整数指针的别名 int num = 42; IntPtr ptr = #
IntPtr 成为指向整数的指针的别名,可以用于声明指向整数的指针变量
typedef 关键字可以显著提高代码的可读性,特别是在处理复杂的数据类型或在多个地方重复使用相同类型时。它还可以用于封装库中的数据类型,使其更具可移植性,因为库开发者可以更轻松地更改实际数据类型而不会影响用户的代码。
指针
C语言中的指针是一种非常重要的数据类型,它用于存储内存地址并提供了对内存中数据的直接访问。指针是C语言的关键特性之一,能够有效地进行内存管理和数据操作。下面是关于C语言指针的详细介绍:
1.指针的声明和定义:
- 使用*运算符来声明指针变量,例如:int *ptr; 表示声明了一个指向整数的指针。
- 指针变量需要初始化为合法的内存地址,通常可以使用&运算符取得一个变量的地址,并将其赋值给指针变量。
2.指针的解引用:
- 使用*运算符对指针进行解引用,以获取指针所指向的内存中的值。例如:int x = *ptr; 会将指针ptr所指向的整数值赋给变量x。
3.指针的运算:
- 指针可以进行算术运算,如加法和减法。这允许你在内存中导航,访问数组元素等。例如:ptr++ 会将指针向前移动到下一个元素。
- 指针的加法和减法操作都是基于数据类型的大小。例如,如果ptr是一个int指针,则ptr++会将指针移动到下一个整数的位置。
4.指针与数组:
- 数组名本质上是一个指向数组第一个元素的指针。例如:int arr[5]; int *ptr = arr;,ptr指向数组arr的第一个元素。
- 可以使用指针来遍历数组元素,而不必使用数组下标。例如:for (int i = 0; i < 5; i++) { printf("%d\n", *(ptr + i)); }。
5.指针与函数:
- 指针可以用于传递函数参数,允许函数修改传递给它的变量的值。这称为通过引用传递。例如:void modifyValue(int *x) { *x = 10; }。
6.指针与动态内存分配:
- 使用malloc、calloc 或 realloc 函数来动态分配内存,并返回一个指向分配内存的指针。必须记得释放这些内存,以避免内存泄漏。例如:int *arr = (int *)malloc(sizeof(int) * 10);。
7.指针的 NULL 值:
- 可以使用 NULL 宏来表示指针变量的空值。这对于检查指针是否有效非常有用。例如:int *ptr = NULL;。
8.指针的安全性和悬空指针:
- 未初始化的指针或已释放的指针可能会成为悬空指针,访问悬空指针会导致不可预测的行为,甚至导致程序崩溃。因此,指针的安全使用非常重要。
C语言指针是一个非常强大的工具,但也需要小心使用,以确保内存安全性和程序稳定性。正确使用指针可以提高代码的性能和灵活性,但不正确的使用可能会导致难以调试的错误。因此,在使用指针时要格外小心。
指针的基本分类
C语言中指针是一种强大而灵活的工具,可以进行多种操作。以下是一些常见和不常见的C指针用法的代码示例:
1. 基本指针用法:
#include
int main() {
int x = 10;
int *ptr; // 定义整型指针
ptr = &x; // 将指针指向变量x
printf("Value of x: %d\n", x);
printf("Value using pointer: %d\n", *ptr);
return 0;
}
2. 指针数组:
#include
int main() {
int arr[] = {
1, 2, 3, 4, 5};
int *ptrArr[5]; // 定义整型指针数组
for (int i = 0; i < 5; i++) {
ptrArr[i] = &arr[i]; // 每个指针指向数组的一个元素
printf("Value at index %d: %d\n", i, *ptrArr[i]);
}
return 0;
}
3. 指向函数的指针:
#include
int add(int a, int b) {
return a + b;
}
int main() {
int (*ptr)(int, int); // 定义指向函数的指针
ptr = &add; // 将指针指向函数
int result = (*ptr)(5, 3); // 通过指针调用函数
printf("Result of addition: %d\n", result);
return 0;
}
4. 指针的指针:
#include
int main() {
int x = 10;
int *ptr1 = &x;
int **ptr2 = &ptr1; // 定义指向指针的指针
printf("Value of x: %d\n", x);
printf("Value using single pointer: %d\n", *ptr1);
printf("Value using double pointer: %d\n", **ptr2);
return 0;
}
5. const 指针:
#include
int main() {
int x = 10;
const int *ptr = &x; // 定义指向常量的指针
// *ptr = 20; // 错误,不能通过指针修改常量的值
x = 20; // 可以直接修改变量的值
printf("Value using pointer: %d\n", *ptr);
return 0;
}
6. void 指针:
#include
int main() {
int x = 10;
float y = 3.14;
void *genericPtr; // 定义void指针,可以指向任何类型
genericPtr = &x;
printf("Value of x: %d\n", *(int *)genericPtr);
genericPtr = &y;
printf("Value of y: %f\n", *(float *)genericPtr);
return 0;
}
这些示例涵盖了一些常见和不常见的C指针用法,包括基本指针、指针数组、指向函数的指针、指针的指针、const 指针和void 指针。每种用法都展示了指针在不同场景下的灵活性和实用性。
字符串与字符指针
在C语言中,字符串和字符指针是相关但不同的概念。下面详细解释它们之间的区别:
1. 字符串:
-
定义: 字符串是一串以空字符 ‘\0’ 结尾的字符序列。C语言没有专门的字符串类型,通常使用字符数组来表示字符串。
char myString[] = "Hello, World!";
-
特点: 字符串以空字符 ‘\0’ 结尾,表示字符串的结束。它是一种约定,允许C语言的字符串处理函数知道字符串在哪里结束。
-
操作: C语言提供了一系列处理字符串的库函数,如 strlen(计算字符串长度)、strcpy(拷贝字符串)、strcat(连接字符串)等。
2. 字符指针:
-
定义: 字符指针是指向字符的指针。它可以指向一个字符,也可以指向字符数组,因为数组的名称可以视为指向数组第一个元素的指针。
char myChar = 'A'; char *charPtr = &myChar; char myString[] = "Hello"; char *strPtr = myString;
-
特点: 字符指针存储某个内存地址,该地址上存放一个字符的值。通过指针运算,可以遍历字符串的每个字符。
-
操作: 字符指针可以用于访问字符串中的每个字符,也可以通过指针运算实现字符串操作。例如,通过递增指针来遍历字符串。
3. 区别:
-
结尾标志: 字符串以空字符 ‘\0’ 结尾,而字符指针只是一个指向字符的地址,不一定指向以空字符结尾的字符串。
-
初始化: 字符串可以通过字符数组直接初始化,而字符指针通常需要先指向一个合法的内存地址,然后再将其初始化为字符数组的地址。
char myString[] = "Hello"; // 字符串的直接初始化 char *strPtr = myString; // 字符指针指向字符数组的地址
-
指针运算: 字符指针可以进行指针运算,让指针指向下一个字符,而字符串本身不能进行类似的指针运算。
char *strPtr = "Hello"; char firstChar = *strPtr; // 获取第一个字符 char secondChar = *(strPtr + 1); // 获取第二个字符
总体而言,字符串是字符数组,而字符指针是指向字符的指针。在处理字符串时,经常使用字符数组,而字符指针则更多用于遍历和操作字符串的字符。理解它们之间的区别有助于更有效地使用C语言进行字符串处理。
函数(Function)的意义及其细节
1.意义:
main函数是程序的初始函数,如果将所有代码写入主函数中,那么main函数将会变得特别繁杂,导致可维护行降低,并且如果同样功能的代码反复使用,代码的可读性也会变得特别低。为了消除这种弊端,函数理念应运而生,提倡模块化程序设计,在使用时,只需要组装,没必要反反复复的重新写相同的代码。
2.要求
Function单词的本意就是“功能,职责”,所以函数的规范就是一个函数实现一个特定的功能,并且函数的名称要反映函数的功能。
3.注意事项
1.函数的构造须由函数类型,函数名称,函数参数,函数体,函数返回值组成,其中函数参数和函数返回值可以视情况而定。
2.函数应“先定义,后使用”原则,并且如果函数体在主函数(同一源文件内)之后,如果使用必须在主函数提前声明。
3.函数的形参和实参,实参是主函数调用函数时的传递的参数,而形参是函数被使用时函数接受到的参数,函数参数的传递是通过拷贝完成的的,并且函数使用完之后销毁,所以有实参和形参之分。
4.函数的调用可以作为另一个函数的实参
#include
//三数比大小
int swap(int a, int b) {
if (a > b)
return a;
return b;
}
int main() {
printf("%d", swap(9, swap(8, 3)));
}
5.函数的执行是通过内存空间内的栈完成的,通过栈顶指针和栈底指针的配合使用,非常完美的将代码的结构性,空间性展现出来。
C库函数的基本实现
1.strcpy函数
char* strcpy(char* dst, const char* src)
{
if ((dst == NULL) || (src == NULL))
return NULL;
char* ret = dst;
while ((*dst++ = *src++) != '\0');
return ret;
}
2.strlen函数
int strlen(char *Str) {
int index = 0;
while (*Str++ != '\0') {
index++;
}
return index;
}
二维数组
二维数组是一种特殊的数组,它可以看作是一维数组的数组。它是在行和列的两个维度上组织数据的一种方式。在C语言中,二维数组的声明和使用有一些特殊之处。以下是对二维数组的详细介绍:
1. 声明和初始化:
int matrix[3][4]; // 声明一个3行4列的二维整型数组
// 初始化二维数组
int matrix2[2][3] = {
{
1, 2, 3},
{
4, 5, 6}
};
2. 访问元素:
二维数组的元素通过行和列的索引进行访问。
int value = matrix[1][2]; // 获取第2行第3列的元素值
3. 内存布局:
二维数组在内存中是按行存储的。例如,一个3行4列的整型数组在内存中的布局是连续存储的 3 行 * 4 列 = 12 个整数。
4. 多维数组的概念:
除了二维数组,C语言还支持更高维度的数组。例如,三维数组可以看作是一个二维数组的数组。
int threeDArray[2][3][4]; // 一个2行3列4深度的三维数组
5. 传递给函数:
在函数参数中传递二维数组时,需要指定列的大小。
void printMatrix(int rows, int cols, int matrix[rows][cols]) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
}
int main() {
int matrix[2][3] = {
{
1, 2, 3},
{
4, 5, 6}
};
printMatrix(2, 3, matrix);
return 0;
}
6. 动态分配内存:
可以使用动态内存分配函数(malloc, calloc, free)来创建动态二维数组。
int rows = 3, cols = 4;
int **dynamicMatrix = (int **)malloc(rows * sizeof(int *));
for (int i = 0; i < rows; i++) {
dynamicMatrix[i] = (int *)malloc(cols * sizeof(int));
}
7. 注意事项:
- 二维数组的大小必须在编译时确定。
- 使用多维数组时,需要小心数组越界的问题。
int matrix[3][4]; matrix[3][4] = 10; // 越界访问,可能导致未定义行为
二维数组是在许多编程场景中广泛使用的数据结构,例如矩阵运算、图形处理等。熟练掌握二维数组的声明、初始化和使用方法对于进行复杂的数据操作非常重要。
C各区的定义
在C语言中,内存分为不同的区域,通常包括以下几个主要的区域,每个区域有其特定的用途和生命周期:
1.栈(Stack):
- 栈是用于存储函数调用和局部变量的内存区域。
- 每次函数被调用时,该函数的局部变量都会被分配到栈上。
- 栈的特点是后进先出(LIFO),即最后进栈的变量最先出栈。
- 局部变量在函数退出时自动销毁,不需要手动释放。
- 栈的内存分配和释放是自动管理的,通常由编译器和操作系统负责。
2.堆(Heap):
- 堆是用于动态分配内存的内存区域,通常用于存储动态数据结构,如链表、树和对象。
- 堆内存需要手动分配和释放,程序员负责管理其生命周期。
- 堆内存通常比栈大,不受固定大小的限制。
- 堆的分配和释放由程序员通过函数如malloc、calloc、realloc和free来控制。
3.全局/静态区(Global/Static Area):
- 全局区用于存储全局变量和静态变量。
- 全局变量在程序启动时创建,并在程序退出时销毁。
- 静态变量在其作用域内保留其值,并且不会被销毁。
- 全局变量和静态变量在整个程序的生命周期内存在。
4.文字常量区(Text/Code Segment):
- 文字常量区存储程序的代码和常量数据。
- 通常,程序代码和常量字符串等在这个区域中,不可修改。
- 文字常量区的数据在程序运行期间不可更改。
5.常量区(Constant Area):
- 常量区存储不可更改的常量数据,如字符串常量。
- 这些数据在程序运行期间不可修改。
6.堆栈区(BSS,Block Started by Symbol):
- BSS区用于存储未初始化的全局变量和静态变量。
- 这些变量在程序启动时被初始化为零或空,不需要显式初始化。
7.寄存器(Register):
- 寄存器是CPU内部的内存区域,用于存储临时数据和快速访问变量。
- 程序员通常无法直接控制寄存器,编译器会自动优化变量的分配和访问。
不同的编译器、操作系统和平台可能会有不同的内存管理机制和命名约定,但这些是C语言通用的内存区域。了解这些区域的存在和基本特征对于正确管理内存和避免内存泄漏和错误非常重要。在C语言中,程序员通常需要手动管理堆内存,而栈和全局变量的管理由编译器和运行时系统自动处理。
为什么将堆(Heap)和栈(Stack)分开:
1.内存管理灵活性:
- 堆和栈的分离允许程序员根据需要灵活地分配和释放内存。栈上的内存分配和释放是自动的,而堆上的内存分配和释放由程序员手动控制。
- 堆内存的手动管理使程序能够在运行时动态地分配和释放内存,这对于处理不定数量和大小的数据结构非常有用。
2.局部变量的生命周期管理:
- 栈上的内存用于存储局部变量,这些变量的生命周期与函数调用的开始和结束直接相关。当函数退出时,栈上的局部变量自动被销毁,无需额外的操作。
- 这种自动管理有助于避免内存泄漏和资源泄漏。
3.栈的高效性:
- 栈的内存分配和释放非常高效,因为它是一个固定大小的内存区域,仅需要移动栈指针即可分配和释放内存。
- 栈上的数据结构通常具有更快的访问速度,因为栈上的数据在内存中是紧凑排列的,而且局部性原理使得访问局部变量更加高效。
4.堆的灵活性和扩展性:
- 堆允许动态分配和释放内存,因此可以处理不定数量和大小的数据。这使得堆非常适合存储动态数据结构,如链表、树和动态数组。
- 堆上的内存可以在程序运行时根据需求进行扩展,而栈的大小通常是固定的。
5.安全性和稳定性:
- 栈的自动管理有助于避免许多内存相关的错误,如空指针引用、内存泄漏和越界访问。局部变量的生命周期受到严格控制,不容易出现问题。
- 堆上的内存管理需要更多的注意和负担,但也提供了更大的灵活性,因此程序员需要小心确保内存的正确分配和释放。
总之,将堆和栈分开允许程序在内存管理、性能和安全性方面取得平衡。栈用于局部变量的自动管理和高效访问,而堆用于动态分配内存以满足程序的动态需求。这种分离使得C和类似的编程语言能够同时提供高性能、灵活性和可维护性。
register关键字
register 关键字是C语言中的一个关键字,用于向编译器建议将变量存储在寄存器中,以便提高对该变量的访问速度。但需要注意的是,register 关键字在现代编译器中的用法已经发生了变化,因此在实际编程中,很少使用 register 关键字来指示编译器将变量存储在寄存器中。以下是关于 register 关键字的一些条件和注意事项:
1.建议性关键字:
- register 关键字是一个建议性关键字,意味着它向编译器提出请求,要求将变量存储在寄存器中,以提高访问速度。
- 编译器不一定会遵循这个请求,因为它可能会根据编译器的优化策略和目标架构来决定是否将变量存储在寄存器中。
2.限制条件:
- register 关键字只能用于自动变量(即局部变量),不能用于全局变量或静态变量。
- 变量必须是可寻址的,也就是说,你不能使用 register 来声明指针或数组类型的变量。
3.不再常用:
- 在现代编译器中,register 关键字的使用已经不再普遍,因为大多数编译器都能够根据上下文和优化策略自动选择是否将变量存储在寄存器中。
- 事实上,现代编译器通常会忽略 register 关键字,因此在代码中使用它通常没有实际效果。
4.编译器优化:
- 编译器通常会进行优化,自动选择最佳的寄存器分配策略,因此程序员不需要手动指定哪些变量应该存储在寄存器中
5.register关键字使用的条件
- register变量必须是能被CPU所接受的类型。
- 这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。不过,有些机器的寄存器也能存放浮点数。
- 因为register变量可能不存放在内存中,所以不能用“&”来获取register变量的地址。
- 只有局部自动变量和形式参数可以作为寄存器变量,其它(如全局变量)不行。
- 在调用一个函数时占用一些寄存器以存放寄存器变量的值,函数调用结束后释放寄存器。此后,在调用另外一个函数时又可以利用这些寄存器来存放该函数的寄存器变量。
- 局部静态变量不能定义为寄存器变量。不能写成:register static int a, b, c;
- 由于寄存器的数量有限(不同的CPU寄存器数目不一),不能定义任意多个寄存器变量,而且某些寄存器只能接受特定类型的数据(如指针和浮点数),因此真正起作用的register修饰符的数目和类型都依赖于运行程序的机器,而任何多余的register修饰符都将被编译程序所忽略。
总的来说,register 关键字在现代C编程中的用途非常有限,因为编译器通常能够更好地处理寄存器的分配。程序员可以信赖编译器的优化能力,而不需要手动干预变量的寄存器分配。因此,在大多数情况下,不建议使用 register 关键字,而应该依赖于编译器的自动优化。
C无内存常量
在C语言中,有一种特殊类型的变量叫做”常量”,它们与普通变量不同,不会分配内存空间来存储值。C语言中有几种不同类型的常量,包括字面常量、符号常量和枚举常量,它们在编程中非常有用。以下是对这些常量的详细介绍:
1.字面常量(Literal Constants):
- 字面常量是编程语言中的固定值,直接写在程序代码中,不会分配内存空间。
- 例如,整数常量42、浮点数常量3.14、字符常量’A’和字符串常量”Hello, World!”都属于字面常量。
- 字面常量用于提供固定的数据值,而不需要存储在内存中。
int x = 42; // 整数字面常量 float pi = 3.14; // 浮点数字面常量 char letter = 'A'; // 字符字面常量 char *str = "Hello, World!"; // 字符串字面常量
2.符号常量(Symbolic Constants):
- 符号常量是使用#define指令定义的,它们代表一个固定的值,通常用于增强代码的可读性和可维护性。
- 符号常量在编译时被替换为其具体的值,不会分配内存空间。
- 例如,以下代码定义了一个符号常量,用于表示圆周率:
#define PI 3.14
- 在代码中使用符号常量时,编译器会将所有出现的 PI 替换为 3.14。
3.枚举常量(Enum Constants):
- 枚举是一种用户定义的数据类型,它包含一组命名的常量值。这些常量值不会分配内存空间。
- 例如,以下代码定义了一个枚举类型 Color,其中包含三个枚举常量:RED、GREEN 和 BLUE。
enum Color {
RED,
GREEN,
BLUE
};
- 在代码中使用枚举常量时,它们仅代表其相应的整数值,不会分配额外的内存。
4.寄存器常量(Register constant)
-
寄存器常量是一种特殊类型的符号常量,通常用于向编译器建议将某个变量存储在CPU的寄存器中,以提高对该变量的访问速度。
-
以下是使用register关键字声明寄存器常量的示例:
-
register int x; // 声明一个寄存器变量x
-
register关键字用于向编译器提出建议,但不保证变量将存储在寄存器中。
5.普通定义的常量是有内存的
const 是内存分配的!
关键字const 并不能把变量变成常量!在一个符号前加上const 限定符只是表示这个符号 不能被赋值。也就是它的值对于这个符号来说是只读的,但它并不能防止通过程序的内部(甚至是外部)的方法来修改这个值。
#include
void test(void)
{
static int a = 0;//static可以改变局部变量的生命周期(本质上是改变了变量的贮存类型)
a++;
printf("%d\n", a);
}
int main() {
for(int i=0;i<100;i++)
test();
system("pause");
return 0;
}
数组函数的使用
1.数组或字符串的长度:sizeof()、strlen()
1、sizeof():返回所占总空间的字节数
(1)对于整型字符型数组
(2)对于整型或字符型指针
2、strlen():返回字符数组或字符串所占的字节数
(1)针对字符数组
(2)针对字符指针
sizeof(…)是运算符,其值在编译时即计算好了,参数可以是数组、指针、类型、对象、函数等。它的功能是:获得保证能容纳实现所建立的最大对象的字节大小。由于在编译时计算,因此sizeof不能用来返回动态分配的内存空间的大小。
strlen(…)是函数,要在运行时才能计算。参数必须是字符型指针(char)。当数组名作为参数传入时,实际上数组就退化成指针了。它的功能是:返回字符串的长度。该字符串可能是自己定义的,也可能是内存中随机的,该函数实际完成的功能是从代表该字符串的第一个地址开始遍历,直到遇到结束符’\0’。返回的长度大小不包括’\0’。
strlen(char)函数求的是字符串的实际长度,它求得方法是从开始到遇到第一个’\0’,如果你只定义没有给它赋初值,这个结果是不定的,它会从aa首地址一直找下去,直到遇到’\0’停止。
c/c++ strlen(str)和str.length()和str.size()都可以求字符串长度,其中str.length()和str.size()是用于求string类对象的成员函,strlen(str)是用于求字符数组的长度,其参数是char。
eg:使用sizeof()函数判定数组元素的个数
sizeof(ojects)/sizeof(ojects[0])
2.sizeof()、strlen()两者区别:
(1)sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型,该类型保证能容纳实现所建立的最大对象的字节大小。
(2)sizeof是运算符,strlen是函数。
(3)sizeof可以用类型做参数,strlen只能用char做参数,且必须是以’‘\0’’结尾的。
sizeof还可以用函数做参数,比如:
short f(){
///
};
printf("%d\n", sizeof(f()));
OUTPUT:sizeof(short),即2。
(4)数组做sizeof的参数不退化,传递给strlen就退化为指针了。
(5)大部分编译程序 在编译的时候就把sizeof计算过了 是类型或是变量的长度。这就是sizeof(x)可以用来定义数组维数的原因
char str[20]="0123456789"; int a=strlen(str); //a=10; int b=sizeof(str); //而b=20;
(6)strlen的结果要在运行的时候才能计算出来,用来计算字符串的长度,不是类型占内存的大小。
(7)sizeof后如果是类型必须加括弧,如果是变量名可以不加括弧。这是因为sizeof是个操作符不是个函数。
(8)当使用了一个结构类型或变量时, sizeof 返回实际的大小, 当使用一静态地空间数组, sizeof 返回全部数组的尺寸。 sizeof 操作符不能返回被动态分配的数组或外部的数组的尺寸
3.string中length()和size()
c++中的size()和length()没有区别
如:
string str="0123456789"; cout <<"str.length()="<<str.length()<<endl;//结果为10 cout <<"str.size()="<<str.size()<<endl;//结果为10
main函数的参数
argv[0] 存储程序的名称,argv[1] 是一个指向第一个命令行参数的指针,argv[n] 是最后一个参数。如果没有提供任何参数,argc 将为 1,否则,如果传递了一个参数,argc 将被设置为 2
int main(int argc,char *argv[],char *envp[])
1.不带参数的 main 函数:
int main() {
// 函数体
return 0;
}
在这种形式下,main 函数不接受任何参数。
2.带参数的 main 函数:
int main(int argc, char *argv[]) {
// 函数体
return 0;
}
这是 main 函数的标准形式,它接受两个参数:
-
int argc: 表示命令行参数的数量(argument count)。
-
char *argv[]: 表示命令行参数的数组(argument vector)。
-
argc 参数:
- argc 是一个整数,代表命令行参数的数量。
- 它至少为1,因为程序的名称(通常是可执行文件的名称)被认为是第一个参数。
- 例如,如果你在命令行中输入 ./my_program arg1 arg2,那么 argc 的值将是3。
-
argv 参数:
- argv 是一个字符指针数组,每个指针指向一个命令行参数的字符串。
- argv[0] 存储程序的名称,argv[1] 存储第一个参数,以此类推。
- 注意,这些参数都以字符串形式存储,即使它们可能是数字或其他类型。
-
示例:
int main(int argc, char *argv[]) { printf("Program name: %s\n", argv[0]); // 打印程序的名称 printf("Number of arguments: %d\n", argc - 1); // 打印参数的数量 // 打印每个参数 for (int i = 1; i < argc; i++) { printf("Argument %d: %s\n", i, argv[i]); } return 0; }如果你运行这个程序并提供一些命令行参数,它将打印程序名称、参数数量以及每个参数的值。这种带参数的 main 函数形式允许程序从命令行接收输入,这在需要动态配置程序行为的情况下非常有用。
抽象状态机的定义和代码
状态机(State Machine)是一种抽象模型,用于描述对象在不同状态之间转换的行为。它是由一组状态、一组转移条件和一组动作组成。状态机分为有限状态机(Finite State Machine,FSM)和无限状态机(Infinite State Machine)两种类型。有限状态机有一个有限的状态集,而无限状态机的状态集可能是无限的。
1.有限状态机的基本组成部分:
-
状态(State): 描述系统的当前情况或阶段。在状态机中,可以定义一组离散的状态,如”开始”、“进行中”、”完成”等。
-
转移(Transition): 描述从一个状态到另一个状态的条件。转移可能有触发条件,当条件满足时,状态机会从一个状态切换到另一个状态。
-
动作(Action): 描述在状态转移时执行的动作或行为。可以在进入或退出某个状态时执行动作。
2.状态机的代码示例(使用C语言):
下面是一个简单的有限状态机的示例,模拟一个灯的状态变化。
#include
// 状态枚举
enum State {
OFF,
ON,
BLINKING
};
// 事件枚举
enum Event {
TURN_ON,
TURN_OFF,
TIMER_EXPIRED
};
// 状态机结构体
struct StateMachine {
enum State currentState;
};
// 处理事件的函数
void handleEvent(struct StateMachine *machine, enum Event event) {
switch (event) {
case TURN_ON:
machine->currentState = ON;
printf("Light turned ON\n");
break;
case TURN_OFF:
machine->currentState = OFF;
printf("Light turned OFF\n");
break;
case TIMER_EXPIRED:
if (machine->currentState == ON) {
machine->currentState = BLINKING;
printf("Light is now BLINKING\n");
} else if (machine->currentState == BLINKING) {
machine->currentState = ON;
printf("Light is now ON\n");
}
break;
default:
printf("Unknown event\n");
}
}
int main() {
// 初始化状态机
struct StateMachine lightStateMachine = {
OFF };
// 处理事件
handleEvent(&lightStateMachine, TURN_ON);
handleEvent(&lightStateMachine, TIMER_EXPIRED);
handleEvent(&lightStateMachine, TIMER_EXPIRED);
handleEvent(&lightStateMachine, TURN_OFF);
return 0;
}
这个示例模拟了一个灯的状态机,有三个状态(OFF、ON、BLINKING),并且定义了三个事件(TURN_ON、TURN_OFF、TIMER_EXPIRED)。在 handleEvent 函数中,根据当前状态和触发的事件,进行相应的状态转移和动作执行。
下面的示例模仿了数字电路,第一份代码有两个状态(0,1),第二份代码有五个不同的状态,可以完成一组数字的演变。
#include
#include
#define REGS_FOREACH(_) _(X) _(Y)
#define RUN_LOGIC X1 = !X && Y; \
Y1 = !X && !Y;
#define DEFINE(X) static int X, X##1;
#define UPDATE(X) X = X##1;
#define PRINT(X) printf(#X " = %d; ", X);
int main() {
REGS_FOREACH(DEFINE);
while (1) {
// clock
RUN_LOGIC;
REGS_FOREACH(UPDATE);
putchar('\n'); sleep(1);
}
}
#include
#include
#define REGS_FOREACH(_) _(X) _(Y)
#define OUTS_FOREACH(_) _(A) _(B) _(C) _(D) _(E) _(F) _(G)
#define RUN_LOGIC X1 = !X && Y; \
Y1 = !X && !Y; \
A = (!X && !Y) || (X && !Y); \
B = 1; \
C = (!X && !Y) || (!X && Y); \
D = (!X && !Y) || (X && !Y); \
E = (!X && !Y) || (X && !Y); \
F = (!X && !Y); \
G = (X && !Y);
#define DEFINE(X) static int X, X##1;
#define UPDATE(X) X = X##1;
#define PRINT
本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://net2asp.com/5a4a8a013a.html
