c语言基础
c语言环境搭建
- 安装visual studio 2019
- 安装 MinGW (模拟 posix环境,可以安装 msys2)
- 安装 wsl
安装 msys2
1
2
3
4
|
pacman -Sy base-devel
pacman -S mingw-w64-x86_64-toolchain
|
环境配置 参考博客
我选择默认的全部安装,安装 mingw 大概会占用1G的空间
将 安装 mingw 的 bin 目录 配置到环境变量,这样就可以全局调用这些命令了,例如 gcc,g++ 等
如果你没有安装 mingw 的环境,和配置好环境变量,默认是无法直接命令行编译 c语言的文件的, 可以安装一些IDE,IDE 自带开发环境,
但是最好还是自己配, 这样 vscode 上可以快捷键 直接运行
[[content/post/13.软件使用总结/代码/msys2.md|msys2环境安装]]
1
2
3
4
5
6
7
8
|
#include <stdio.h>
int main(void) {
printf("hello world\n");
return 0;
}
|
c语言断点调试
安装 c/c++ 的插件,并且全局启用
查看生成的汇编指令
参考文章
在监视控制台输入命令 -exec disassemble /m main
最基本数据类型
类型 |
解释 |
short int |
不低于2字节 |
int |
4字节 |
long int |
不小于 4字节 |
long long |
8字节 |
float |
单精度浮点(规定至少能表示6位有效数字)(10^-37,10^37) |
double |
双精度浮点 |
c语言的标准并没有给定标准,所以 类型长度不能确定
printf("%d\n",sizeof(short int))
1
2
3
4
5
6
7
8
|
int main(void)
{
printf("%d\n", sizeof(short int));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(long int));
return 0;
}
|
在 mingGW 下面,显示结果是 2 4 4
常用头文件
limits.h
转义字符
字符类型 |
解释 |
\0 |
八进制0 |
‘\61’ |
八进制的61 |
‘\x31’ |
16进制的31 |
‘\b’ |
退格,backspace |
‘\r’ |
回车 |
\t |
制表符 |
' |
字面量 |
" |
字符的字面量 |
要表示 中文,就需要使用宽字符 wchar_t
1
2
3
4
5
6
7
8
|
#include <stdio.h>
int main(void)
{
wchar_t a = L'中';
char * str = "中国";
printf("aaa %c, %s\n",a,str);
return 0;
}
|
vscode 下查看 c语言内存 布局
参考博客
宏和只读变量
宏在编译的时候会直接对变量替换,所以内存中不存在这个宏定义
1
2
3
4
|
#define color 1
#undef color
// undefine 可以取消定义,后面的行就无法使用这个了
|
函数基础
c语言规范命名
函数的原型
1
2
3
4
5
6
7
8
9
|
main() {
}
// 这种是经典 c语言的写法了
int main() {}
//表示没有参数
int main(void) {}
// void 参数表示 什么都没有
|
下面 可以运行一下这个代码
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <stdio.h>
void test1() {
puts("----");
}
int main(void)
{
test1(1,23);
test1("aaa");
return 0;
}
|
c++ 编译这个代码会报错,但是 纯c语言 是不会报错的。
==黑魔法== :
可以直接去函数的栈上获取参数
c语言 可以设置 函数原型【在头文件只是声明函数的名字, 之后引入函数的实现】
1
2
3
4
5
6
7
8
|
#include <stdio.h>
int Add(int a, int b);
int main(void)
{
int c = Add(1, 2);
printf("%d\n", c);
return 0;
}
|
如果函数参数列表声明都没有,要写 void 上去,方便编译的时候出现问题
变长参数
printf 就是用了一个变长参数
1
2
3
4
5
6
7
8
9
10
|
__mingw_ovr
__attribute__((__format__ (gnu_printf, 1, 2))) __MINGW_ATTRIB_NONNULL(1)
int printf (const char *__format, ...)
{
int __retval;
__builtin_va_list __local_argv; __builtin_va_start( __local_argv, __format );
__retval = __mingw_vfprintf( stdout, __format, __local_argv );
__builtin_va_end( __local_argv );
return __retval;
}
|
代码演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#include <stdio.h>
#include <stdarg.h>
void handle_arg(int arg_cnt, ...)
{
//定义变长参数
va_list args;
int i;
va_start(args, arg_cnt);
for (int i = 0; i < arg_cnt; i++)
{
int a = va_arg(args, int);
printf("%d: %d\n", i, a);
}
// 清理变长参数内存
va_end(args);
}
int main(void)
{
handle_arg(3, 1, 2, 3);
return 0;
}
|
c语言编译过程
graph LR
源代码-->预处理器--宏替换后的源代码-->编译器--中间文件-->链接器-->可执行文件
1
2
3
4
5
6
7
8
|
int __cdecl printf(const char* fmt,...);
int main(void)
{
printf("aaaa %d\n",1);
return 0;
}
|
静态链接和动态链接库
动态链接不需要打包到可执行文件,放在特定的路径上,运行的时候加载进来。(可以共享)
静态链接则是编译的时候就确定了,把需要用到的东西全部加载进可执行文件里面。(没法共享)
1
2
3
4
|
# 编译静态链接的方法
gcc -c -o main.o main.c
ar rcs libmain.a main.o
# libmain 就是一个静态链接库
|
程序本身运行的时候也会占用内存空间,多个程序共享动态链接库可以减少内存占用
1
2
3
4
5
6
7
|
# ldd 查看依赖哪些动态链接库
which ldd
ldd /usr/bin/ls
ldd a.out
# 查看可执行程序依赖哪些文件
gcc -shared -fPIC -wl,--out-implib main.c -o libmain.dll
|
1
2
3
4
5
6
|
# mingw 上使用 ldd命令
$ ldd a.exe
ntdll.dll => /c/WINDOWS/SYSTEM32/ntdll.dll (0x7ffb2fbd0000)
KERNEL32.DLL => /c/WINDOWS/System32/KERNEL32.DLL (0x7ffb2e3c0000)
KERNELBASE.dll => /c/WINDOWS/System32/KERNELBASE.dll (0x7ffb2d450000)
msvcrt.dll => /c/WINDOWS/System32/msvcrt.dll (0x7ffb2e320000)
|
自定义头文件
1
2
|
#include "../include/factor.h"
// 注意,使用路径的方式 必须用引号
|
使用双引号的时候,会从本地路径去查找,找不到再从搜索路径查找,
gcc 编译的时候 可以在 上面加参数添加搜索路径
1
2
3
4
5
|
#include "include/factor.h"
int main() {
printf("aaa\n");
return 0;
}
|
常用的宏
宏 |
解释 |
FILE |
所在文件 |
LINE |
行 |
FUNCTION |
所在的函数 |
数组
数组的声明和初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include <stdio.h>
// #include "io_utils.h"
#define ARRAY_SIZE 10
int main(void)
{
int a[ARRAY_SIZE]={0}; // 后面 {0} 是初始化列表,如果只会声明 int a[10], 是不会赋值的
// 使用初始化列表后,后面的默认全部填为0
// 在 局部函数声明的数组,是在栈上开辟的内存
for (size_t i = 0; i < ARRAY_SIZE; i++)
{
// a[i] = 1;
printf("%d\n",a[i]);
}
printf("%d -- %d\n",a,&main);
return 0;
}
|
打乱数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#include <stdio.h>
#include <stdlib.h>
#define LEN 100
int main(void)
{
int a[LEN] = {0};
for (size_t i = 0; i < LEN; i++)
{
a[i] = i + 1;
// printf("%d\n",a[i]);
}
srand(time(NULL));
for (size_t i = LEN - 1; i > 0; i--)
{
int pos = rand() % i;
int t = a[pos];
a[pos] = a[i];
a[i] = t;
}
for (size_t i = 0; i < LEN; i++)
{
printf("%d\n", a[i]);
}
return 0;
}
|
实现快速排序
快速排序主要有2种经典的切分算法
- Lomuto partition scheme
- Hoare partition scheme
Lomuto partition scheme
该算法的思路是: 准备一个队列, 选择序列最后一个数作为基准,从头到尾遍历这个序列,小于 pivot的数放都放入左边,左边的队列长度+1
遍历完之后 队列中的所有数都小于 pivot,不是 队列中的数都大于等会 pivot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
#include <stdio.h>
#include <stdlib.h>
#define LEN 100
int swap(int a[],int l,int r) {
int c = a[l];
a[l] = a[r];
a[r] = c;
}
int partition(int *array, int low, int high)
{
int pivot = array[high];
int p = low;
for (int i = low; i < high; i++)
{
if (array[i] < pivot)
{
swap(array,i,p++); //加入 队列,并且队列长度+1 ,
// 左边 <=p 位置的 就是 比 pivot 小的队列, 右边就是 比 pivot 大于或者等于的数
}
}
swap(array,p,high);
return p;
}
void quick_sort(int *array, int l, int r)
{
if (l>=r)
return;
int p = partition(array, l, r);
quick_sort(array, l, p - 1);
quick_sort(array, p + 1, r);
}
int main(void)
{
int a[LEN] = {0};
for (size_t i = 0; i < LEN; i++)
{
a[i] = i + 1;
// printf("%d\n",a[i]);
}
srand(time(NULL));
for (size_t i = LEN - 1; i > 0; i--)
{
int pos = rand() % i;
int t = a[pos];
a[pos] = a[i];
a[i] = t;
}
quick_sort(a, 0, LEN - 1);
for (int i = 0; i < LEN; i++)
{
printf("%d ,", a[i]);
}
return 0;
}
|
Hoare partition scheme
算法的思路是中间取出一个数作为基准元素, 两个指针从两边往中间靠拢,左边维护一个 小于 pivot的序列,右边维护一个 大于 pivot的序列
左右两个序列的边界我们称为 p 和 q, p和 q之间这是等于 pivot的序列,p和 q靠拢在一起的时候 返回 停留的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// c语言没有泛型编程,但是可以利用宏的方式来实现万能的写法
void swap(int *a, int *b)
{
int c = *a;
*a = *b;
*b = c;
}
void shuffle(int *p, int len)
{
srand(time(NULL));
for (int i = len - 1; i > 0; i--)
{
int j = rand() % i;
swap(p + i, p + j);
}
}
int *partition(int *l, int *r)
{
// pivot
// queue < pivot , return queue.tail
int pivot = *(l + (r - l) / 2);
int *p = l;
int *q = r;
while (1)
{ //这里可以用 do while ,总之左右两边一定要先和 pivot 比较一遍
while (*p < pivot)
p++;
while (*q > pivot)
q--;
if(p>=q) break;
swap(p, q);
}
return p;
}
void quick_sort(int *l, int *r)
{
if (l >= r)
return;
int *q = partition(l, r);
quick_sort(l, q - 1);
quick_sort(q + 1, r);
}
#define N 10
int main(void)
{
int *a = malloc(sizeof(int) * N);
for (int i = 0; i < N; i++)
a[i] = i + 1;
shuffle(a, N);
quick_sort(a, a+ N-1);
for (size_t i = 0; i < N; i++)
{
printf("%d,", a[i]);
}
return 0;
}
|
指针学习
指针的长度是多大?
32 位机器是4个字节, 64位机器是 8个字节
1
2
3
4
5
6
7
|
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
printf("%d -- %d\n",sizeof(int *),sizeof (long long *));
return 0;
}
|
只读指针变量和只读变量指针
1
2
3
4
5
|
int *const cp = &a;// 不能修改指针的 引用
*cp = 333;// 可以修改指向的地址的内存
int const * const p = &b; // 只读
//禁止修改
|
动态内存分配
c语言允许 从堆区分配一块内存
- malloc
- calloc
- realloc
参考文档
常见问题
- 忘记使用完毕之后释放内存
- 使用了已经释放的内存
- 使用了超出边界的内存
- 改变内存的指针,导致无法正常释放
使用建议
- 避免修改指向已经分配的内存的指针
- 对于free 之后的指针主动改为 NULL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
#define LEN 10
int *p = calloc(LEN,sizeof(int))
for (size_t i = 0; i < LEN; i++)
{
p[i] = i+1;
printf("%d\n",p[i]);
}
free(p);
return 0;
}
|
用完指针需要 free 掉, 有些操作系统可能不会回收这块内存,你需要手动销毁返回内存给操作系统
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
#include <stdio.h>
#include <stdlib.h>
void getArray(int **ptr,int len,int value) {
*ptr = malloc(sizeof(int)*len );
for (size_t i = 0; i < len; i++)
{
//括号的优先级比较高,先去地址
(*ptr)[i] = value;
}
}
int main(void)
{
#define LEN 10
int *array;
getArray(&array,10,0);
for (size_t i = 0; i < 10; i++)
{
array[i] = i+1;
}
//重新分配,数组内存不够的时候使用
array = realloc(array,20*sizeof(int));
for (size_t i = 0; i < 20; i++)
{
printf("%d,",array[i]);
}
free(array);
return 0;
}
|
函数指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#include <stdio.h>
#include <stdlib.h>
void getArray(int **ptr,int len,int value) {
*ptr = malloc(sizeof(int)*len );
for (size_t i = 0; i < len; i++)
{
//括号的优先级比较高,先去地址
(*ptr)[i] = value;
}
}
int main(void)
{
#define LEN 10
int *array;
void (* fn) (int **ptr,int len,int value) = &getArray;
// getArray(&array,10,0);
fn(&array,10,0);
//重新分配,数组内存不够的时候使用
free(array);
// 函数名 就是函数的地址, 地址解引用,还是函数的地址
(*fn)(&array,10,0);
free(array);
printf("%d-- %p\n",fn == &getArray ,fn);
return 0;
}
|
写法 |
解释 |
int *f(int) |
函数,返回 int* |
int * (f(int)) |
同上 |
int (*f)(int, double) |
函数指针,返回int |
int * (*f) (int, double) |
函数指针,返回 int* |
工具网站 cdecl
通过这个网站可以比较容易分析出类型
关键字和宏
1
2
|
typedef int boolean
typedef int Boolean
|
N种方式实现 swap方法
实现任意类型的变量交换内容
参考博客
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// c语言没有泛型编程,但是可以利用宏的方式来实现万能的写法
#define swap1(a,b,type) { type temp=a;a=b;b=temp; }
#define iswap(a,b) do { typeof(a) t=a;a=b;b=t; }while(0)
// memory swap
void mswap(void *l, void *r, size_t size)
{
void *temp = malloc(size);
if(temp!=NULL) {
memcpy(temp,l,size);
memcpy(l,r,size);
memcpy(r,temp,size);
free(temp);
}
}
int main(void)
{
int a = 1;
int b = 2;
// mswap( &a, &b, sizeof(a));
// swap1(a,b,int);
typeof (a) c = 666;
if(a) iswap(a,b);
else printf("error\n");
printf("%d %d -- %d\n", a, b,c);
return 0;
}
|
结构体
指针要用 ->
符号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// c语言没有泛型编程,但是可以利用宏的方式来实现万能的写法
int main(void) {
struct Person {
char *name;
int age;
};
struct Person p = {.name = "hello person", .age = 18};
printf("%d\n", p.age);
typedef struct Person Person;
Person p2 = {"is p2",15}; // c语言 必须要 用 struct Person ,除非 你用 typedef 声明类型
printf("p2.name=%s--\n",p2.name);
struct {
char *name;
}teacher; //匿名结构体 ,没有名字,直接定义变量的类型
teacher.name = "hello world";
printf("name = %s age=%d\n",teacher.name, (&p) -> age);
return 0;
}
|
最简单的写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// c语言没有泛型编程,但是可以利用宏的方式来实现万能的写法
int main(void) {
typedef struct {
char *name;
int age;
}Student;
printf("%d\n",sizeof(Student));
Student u = {.name="student",.age=18};
printf("name=%s,age=%d\n",u.name,u.age);
return 0;
}
|
代码打印
1
2
|
16 -- 8 -- 4
name=student,age=18
|
这里就出现了内存对齐的现象,理论上来说 8+4 得到12个字节才对, 结果使用了内存对齐,补充了4个字节
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
int main(void) {
//设置对齐为2的倍数
#pragma pack(2)
typedef struct Noxx{
char *name;
int age;
}Student;
printf("%d -- %d -- %d\n",sizeof(Student),sizeof(char *),sizeof(int));
Student u = {.name="student",.age=18};
printf("name=%s,age=%d\n",u.name,u.age);
return 0;
}
|
参考文章
内存对齐规则:
- 结构体对象大小是对齐大小的整数倍
- 成员变量起始地址对齐一个数字 ,即取 $min(成员大小,对齐大小)$
1
2
3
4
5
6
7
8
|
class child {
int age; //4
char sex ; //1
// char padding 1;
short height // 2 ,这里填充一个 padding 1, 起始地址是 min(2,4) => 2, 所以 height 前面要加一个 1的padding
};
//sizeof(child) == 8
|
32 位cpu 4字节对齐, 64位CPU 8字节对齐
计算机每次读写一个字节块,例如,假设计算机总是从内存中取8个字节,如果一个double数据的地址对齐成8的倍数,那么一个内存操作就可以读或者写,但是 如果这个double数据的地址没有对齐,数据就可能被放在两个8字节块中,那么我们可能需要执行两次内存访问,才能读写完成。显然在这样的情况下,是低效的。所以需要字节对齐来提高内存系统性能。
在有些处理器中,如果需要未对齐的数据,可能不能够正确工作甚至crash,这里我们不多讨论
1
2
3
4
5
6
|
typedef struct {
long long age; //4
int aa;
}Child;
// 输出 16
printf("%d\n",sizeof(Child));
|
联合体
联合体 共享变量的同一块内存, 下面 a,b,c 都共用一块内存,而不是分开的3个内存
1
2
3
4
5
|
union A {
char a;
int b;
long long c;// sizeof(A) is 8
};
|
枚举
1
2
3
|
typedef enum FileFormat {
PNG, JPEG,BMP
} FileFormat;
|
文件读写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#include <stdio.h>
#include <io.h>
// c语言没有泛型编程,但是可以利用宏的方式来实现万能的写法
int main(void) {
FILE *file = fopen("./temp.txt","rb");
if (file) {
char buf[1024]={0};
size_t buf_cnt = fread(buf,1,1024,file);
if (buf_cnt <= 0) {
fprintf(stderr,"error !!!! buf_cnt");
}else {
printf("%s\n",buf);
}
fclose(file);
}else {
fprintf(stderr,"error open!!");
}
return 0;
}
|
小端序 和大端序
比如: 01,02,03,04
内存里面是按照小端序存的
0x04030201
我们的系统一般情况下是小端序,要证明是大小端序,我们可以用联合体实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include <stdio.h>
int main(void) {
typedef union {
char c[2];
short s ;
} Person;
Person value = {.s = 0x100};
printf("%d %d, %d\n",value.s,value.c[0]==1,value.c[1]==1);
return 0;
}
|
字符串
方法 |
用法 |
strlen or strnlen |
字符串长度 |
strcmp or strncmp |
字符串比较 ,不能无限制比较 |
strchr |
字符串查找,从左到右查 |
strrchr |
字符串查找,reverse,从右到做查 |
strbrk |
string break ,按照多个字符查找 |
strstr |
查找子串开始的位置 |
strcat |
字符串拼接 |
strcpy(dest,src) ,or memcpy(dest,src,size) |
字符复制或者内存复制 |
1
2
3
4
5
6
7
8
9
10
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char *p = "aaa,bbb,ccc";
printf("%s\n",strchr(p,'c'));
return 0;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char *p = "aaa,bbb,ccc.bbb;cccde";
do {
p = strpbrk(p,",.;");
if (p) puts(p) ,p++;
}while(p);
return 0;
}
|
时间
- UTC 是世界调和时间,是国际时间的标准,不受时间影响
- GMT 格林治时间,与UTC时间一致,但是我们说gmt的时候是说零时区的时间,它不是时间标准了。
- Epoch ,一般翻译为纪元, 我们计算机程序都是从UTC 时间 1970年1月 1日0时0分0秒开始的一个整数值,这是unix计时方法,unix系统对c标准的扩展标准posix也采用这样的规定,因此这个起始时间称为unix epoch, 大多数编程语言java,js ,win系统上的c都采用unix epoch 等
时间类型:
- time_t, 表示从 epoch 开始计算的时间
- clock_t ,
- tm , 时间的结构体
- timespec , 可以查看毫秒纳秒等精确单位
- timeb , 与time_t类型类似,但是没有精确单位,只能用于计算秒数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main(void)
{
//打印时间的方法
time_t t;// 获取日历时间
time(&t);
printf("%s", ctime(&t));//转字符串 Mon Jun 06 17:41:51 2022
printf("%d\n",t); //1654508511
/*
struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
*/
struct tm *info = localtime (&t);//转时间结构体
printf("%d-%d-%d %d:%d:%d\n",info->tm_year+1900,info->tm_mon+1,info->tm_mday,info->tm_hour,info->tm_min,info->tm_sec);
return 0;
}
|
错误处理和 io
file access mode |
mean |
explain |
action |
r |
read |
open file for read |
如果不存在,就报错 |
w |
write |
create file for write |
如果存在就消除内容,否则就创建文件 |
a |
append |
append file |
create new or write end |
r+ |
read extended |
open file for read/write |
不存在就报错error |
w+ |
write extended |
create file for read/write |
create new |
a+ |
append extended |
open file for read/write |
create new,or write in end |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main(void)
{
// for (int i = 0; i < 10; i++)
// {
// puts(strerror(i));
// }
printf("---- error 类型\n" );
FILE *f = fopen("./app.txt", "w+");
char buf[2048];
setbuf(f, buf); // 设置缓冲区
char stdbuf[2048];
setbuf(stdout, stdbuf); // 设置stdout缓冲区
fflush(stdout);// flush 缓冲区
if (f == NULL)
{
perror("fopen");
exit(1);
}
else
{
puts(strerror(ferror(f)));
printf("%d\n",feof(f));
fclose(f); // 关闭句柄
}
/*
mod:
r,w,a ,r+,w+,a+
*/
return 0;
}
|
文件流缓冲区
graph LR
dma --读取到io缓冲区--> 缓冲区 --cpu读取数据--> 复制到调用者传入的内存空间
读取文件
函数 |
用法 |
getchar(void) |
从 stdin读取一个字符 |
int fgetc(FILE *s) |
读取文件 字符 |
int getc(FILE *s) |
读取一个字符 |
getchar 等价于 getc(stdin)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main(void)
{
fprintf(stdout, "Hello, World!\n");
fflush(stdout);
while(1) {
int i = getc(stdin);
if (i == EOF) {
break;
}
if(i == '0') break;
fprintf(stdout, "%c", i);
// putc(i, stdout);
}
int i=0;
do {
fscanf(stdin, "%d", &i);
printf(" number %d\n",i);
}while(0);
printf("copy files\n");
char *src = "main.c";
char *dst = "main.c.bak";
FILE *fsrc = fopen(src, "r");
FILE *fdst = fopen(dst, "w+");
char buf[1024];
while(!feof(fsrc)) {
int n = fread(buf, 1, 1024, fsrc);
fwrite(buf, 1, n, fdst);
fwrite(buf,1,n,stdout);
}
puts(ferror(fsrc));
// while(!feof(dst)) {
// int n = fread(buf,1,1024,fdst);
// fwrite(buf,1,n,stdout);
// }
fclose(fsrc);
fclose(fdst);
return 0;
}
|
格式化文本输出
| scanf| 读取屏幕字符|
| fscanf(file,fmt,…| 从文件中读取 |
| sscanf(char *buf,fmt,…) |从字符串中读取 |
创建 Cmake项目
参考文章
Conan 管理依赖
网址
通过 python的pip 安装 conan
依赖搜索
1
2
3
|
pip install conan
conan search libcurl
|
在根目录下 创建 conanfile.txt
1
2
3
4
|
[requires]
libcurl/1.0.0
[generators]
cmake
|
cpp调用 c语言的代码
因为 cpp 和 c中 符号命名规则是不一样的,不能直接 调用 c函数的代码
需要在 .h 头文件的 函数声明上面 加 extern “C” 这个语句
1
2
3
4
5
6
7
8
9
|
#ifdef __cplusplus
extern "C" {
#endif
int fib(int n);
#ifdef __cplusplus
};
#endif
|
java 调用 c
java 有 jni (java native interface )的机制去加载 c的方法