Code Monkey home page Code Monkey logo

rock_c's Introduction

rock-c

问题列表

  • 14、C的内存管理
  • 15、学习新的好的项目
  • 17、C编写需要配置的环境有哪些
  • 18、ffmpeg项目讲解,如何来整合调用这些库的功能
  • 21、驱动程序+嵌入式程序如何编写
  • 22、短视频直播实现技术
  • 23、个人程序员如何在未来有自己的一席之地

C做的方向

  • 28、通信socket、tcp udp编程、位操作、
  • 30、三方库libevent接入项目使用+看懂代码 视频图片流媒体处理
  • 31、C能做的东西 嵌入式(做软硬结合部分)+网络底层
【】产品--嵌入式,驱动硬件
C程序操作硬件(单片机)、完整的开发板、stm32(芯片处理器)、
0和1输入,一块液晶屏,一个stm8开发板,所有成本30元可以找到包邮的。输入、输出、cpu、内存、存储什么的就都全了

单片机+嵌入式裸机+带操作系统的驱动和内核
嵌入式首先确保自己的C语言过关,然后可以从系统编程入手,在深入到裸机开发、系统移植、驱动开发 等

QA

0、C的优缺点

C是面向过程编程,语法简洁
C语言中即使是再微小的事情都有安全隐患。它不是用来编写大型的商业软件的。
初衷是为了让编译器和库的编写者可充分利用计算机每一个bit的计算能力,初衷就是为了快
系统级编程、嵌入式系统和驱动程序等领域--底层领域
C语言的源代码可以直接转化为机器码,而其他语言的源代码则需要经过编译、解释等环节才能转化为机器码。这使得C语言在需要高效率的场景中更为适用
编译成机器码之后是某些机器的适用的机器码,不同机器的不同系统机器码会不同

1、C的数据类型

数据类型.png
C语言中定义了6种基本数据类型:short,int,long,float,double,char
4种构造类型:数组,结构体(struct),共用类型(union),枚举类型(enum)
指针类型和空类型

2、typedef unsigned char __uint8_t 作用?

typedef为C语言的关键字
作用是为一种数据类型定义一个新名字。
这里的数据类型包括内部数据类型(int,char等)和自定义数据类型(struct等)

3、#define作用

#define 定义一个标识符来表示一个常量。
其特点是定义的标识符不占内存,只是一个临时的符号,预编译后这个符号就不存在了(使用的地方在预编译的时候会直接替换成对应常量值,所以说符号就不存在了)。
用 #define 定义标识符的一般形式为:
#define 标识符 常量 //注意, 最后没有分号
#define 和 #include 一样,也是以“#”开头的。凡是以“#”开头的均为预处理指令,#define也不例外。
示例

#define UINT unsigned int
在程序中可用 UINT 作变量说明:
UINT a, b;

宏定义只是简单的字符串替换,由预处理器来处理.比如:UINT a, b;变成 unsigned int a, b;

4、C中头文件的作用和保存什么内容

C 函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和编译器自带的头文件。
在程序中要使用头文件,需要使用 C 预处理指令 #include 来引用它,在预处理阶段,会把引用的头文件内容复制到源文件中
建议把所有的常量、宏、系统全局变量和函数原型写在头文件中,在需要的时候随时引用这些头文件。
#include 这种形式用于引用系统头文件。它在系统目录的标准列表中搜索名为 file 的文件
#include "file" 这种形式用于引用用户头文件。它在包含当前文件的目录中搜索名为 file 的文件

5、输入输出

scanf() 函数用于从标准输入(键盘)读取并格式化, printf() 函数发送格式化输出到标准输出(屏幕)。

6、C中字符串如何表示

在 C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组。因此,\0 是用于标记字符串的结束。 char arr[]={'x','b'}; char site[] = "RUNOOB";

7、指针如何来操作

& 运算符访问的地址。指针类型必须与变量类型一致。
指针中对应的值存的是变量的地址,%p取的是指针的值(及存的变量的地址信息)
想取指针存的地址对应的值信息,使用*p

8、判断逻辑

switch 语句中的 expression 是一个常量表达式,必须是一个整型或枚举类型。
goto用法 goto labelname; .. . labelname: statement;

9、C程序从创建到执行流程

在完成 .c 或 .cpp 文件的编写后,我们通常直接 gcc 或 g++ 后接文件名,就可以在当前文件夹下生成 a.out 可执行文件, 之后输入 ./a.out 即可执行该二进制可执行文件。
gcc命令后面不加选项的话,就会默认执行预处理、编译、汇编、链接所有步骤,若程序没有错误的话,我们就可以得到一个可执行文件,默认为 a.out。
创建到执行流程.png
-E选项:编译器执行完预处理阶段就停止执行,后面的编译、汇编等操作就不会执行。
-S选项:编译器执行完编译阶段就会停止。
-c选项:编译器执行完汇编阶段就会停止。
详情:

  • 1、编写.c文件
  • 2、预处理
    对于使用#例如#define 和#include的会把对应的常量或者头文件替换成具体内容
  • 3、编译(将文件编译成汇编语言)
  • 4、汇编(将汇编代码变成机器码,生成目标文件 .o (windows下为 .obj ) )
  • 5、链接(生成可执行文件 win中是exe程序,Linux中是.out可执行程序)

GCC命令行的一个选项:
(1)-shared 。该选项指定gcc编译器生成动态连接库,而不是可执行文件
(2)-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要, 而不能达到真正代码段共享的目的。正是使用这个,使得动态链接库不用再编译时拷贝库函数的完整代码,实现真正的动态链接。
(3)-L:指定编译的时候动态链接库的位置,这里使用 -L. 后面跟了一个点表示要连接的库在当前目录中
(4)-ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称
另外: (1)LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。
(2)当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,不过如果没有root权限, 那么只能采用输出LD_LIBRARY_PATH的方法了。

10、C源文件中引入的头文件中的函数,如何找到函数实现的

函数的声明放到头文件(.h文件)中,使用函数时引入对应的头文件就可以,编译器会在链接阶段找到函数体。
链接会将前面编译好的.o文件、系统库的.o文件和库文件彼此相连接,有默认库文件加载路径,也可以指定库文件加载路径,指明从哪些库文件来链接
如果是用gcc,那就需要用参数(-l -L)去指定这些库的名字及路径

源文件编译后成生了目标文件(.o或.obj文件),目标文件中,这些函数和变量就视作一个个符号。
在link的时候,需要在makefile里面说明需要连接哪个.o或.obj文件(在这里是b.cpp生成的.o或.obj文件),
此时,连接器会去这个.o或.obj文件中找在b.cpp中实现的函数,再把他们build到makefile中指定的那个可以执行文件中。
在VC中,一帮情况下不需要自己写makefile,只需要将需要的文件都包括在project中,VC会自动帮你把makefile写好。

11、一个C中如何引用另外一个C的实现的逻辑

在C中通过引用其的.h文件来使用其对应的函数实现。之后再把对应的lib包含进项目中。
引入第三方包的方式

  1. 直接引入三方包实现的源文件--及直接在项目中添加三方的.c文件
  2. gcc 会分别编译 main.c 和 sum.c(三方源文件),最后再把他们链接起来构成最终的 main 程序。--gcc main.c ./thirdparty/sum.c -o main

将GoogleTest源码下载到本地,从源码编译安装到指定路径,然后再使用静态或者动态链接的方式进行调用
3. 引入头文件和静态库
静态库文件是多个目标文件(机器码文件,但还没链接成可执行文件)放到一起的文件
目标文件(Object,.o 结尾)是由源文件(.c、.cpp)编译但还未链接得到的二进制文件,目标文件此时已完成为了编译流程(预处理 -> 编译 -> 组装 -> 链接)中的前三步。
那什么又是静态库呢,怎样获得静态库呢?
静态库是由多个目标文件打包到一起得到的二进制文件,命名约定俗成以 lib 开头,中间是库名,然后是 .a 结尾,形如:libNAME.a
1. 编译成目标文件 gcc -c ./thirdparty/sum.c -o sum.o
2. sum.o 打包到静态库中,这里需要用到一个命令 ar(archive 的缩写
参数:-r replace 如果静态库中目标文件已存在,则替换为最新的。
-c 如果静态库不存在,在创建的时候不用弹出警告提示。
ar -rc libsum.a sum.o xxx.o 多个目标文件打入到libsum.a静态库中。 3. 查看静态库中目标文件列表可以用 ar -t libsum.a
把 main.c 和 上一步输出的静态库 libsum.a 合在一起编译出最终的可执行程序。
生成最终可执行程序 gcc main.c libsum.a -o main 或者gcc -o hello main.c -static -L. -lmyhello -static代表使用静态链接库,-L.代表静态链接库搜索路径 .代表当前路径

ar命令参数 意义
-r 将objfile文件插入静态库尾或者替换静态库中同名文件
-x 从静态库文件中抽取文件objfile
-t 打印静态库的成员文件列表
-d 从静态库中删除文件objfile
-s 重置静态库文件索引
-v 创建文件冗余信息
-c 创建静态库文件
  1. 引入头文件和动态链接库 把常用的一些函数功能都封装到了这些共享库(Shared Object,后缀 .so)中。
    在编译程序时共享库的内容并不打包到最终的可执行程序中,而是在程序执行时动态链接调用,因此这些共享库通常也被称之为动态链接库,程序运行时会自动到 /lib 和 /usr/lib 等库目录去搜索,当然你也可以指定一个自己的库目录。
    构建自己的动态链接库 :gcc -shared ./thirdparty/sum.c -o libsum.so
    接着去生成 main.c 的可执行程序:gcc main.c -o main -L. -lsum -Wl,-rpath=.

    -L. 指定编译时自定义的链接库目录,. 代表当前目录。
    -lsum 指定要动态链接的库,只需写名字,系统会自动加前后缀,即:libsum.so
    -Wl,-rpath=. 首先 -Wl, 是传递选项给链接器,选项之间用 , 号分隔,-rpath=. 这个选项设置了程序运行时自定义库的目录为当前目录。

12、sizeof函数的作用

返回一个对象或者类型所占的内存字节数
取得一个对象(数据类型或者数据对象)的长度(即占用内存的大小,以byte为单位)。其中类型包含基本数据类型(不包括void)、用户自定义类型(结构体、类)、函数类型
tip:当sizeof的参数是数组名时,计算的是整个数组的存储大小;当sizeof的参数是指针时,计算的是指针的大小(8字节,64位系统) length=(sizeof(a) / sizeof(int));

13、exit(1)作用

功 能: 关闭所有文件,终止正在执行的进程。
exit(1)表示异常退出.这个1是返回给操作系统的。
exit(x)(x不为0)都表示异常退出
exit(0)表示正常退出

14、C环境配置

  1. 需要在运行机器上安装gcc编译器,用于编译C源码
  2. 剩余就是编辑C源码的工具

15、ifndef和endif的用法

ifndef x //if not define的简写 宏定义的一种
条件指示符#ifndef 的最主要目的是防止头文件的重复包含和编译

#ifndef x //先测试x是否被宏定义过
#define x
程序段1 //如果x没有被宏定义过,定义x,并编译程序段1
#endif

程序段2 //如果x已经定义过了则编译程序段2的语句,“忽视”程序段1。

千万不要忽略了头文件中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。
  还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:
  #ifndef <标识>
  #define <标识>
  ……
  ……
  #endif

<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前面加下划线,并把文件名中的“.”也变成下划线,如:stdio.h
  #ifndef _STDIO_H
  #define _STDIO_H
  ……
#endif

16、static关键字的作用--静态

  1. 修饰局部变量,成为静态变量,存储在静态区。作用域在局部
  2. 修饰全局变量,只能被包含该定义的文件访问(即改变了作用域)
  3. static修饰函数使得函数只能在包含该函数定义的文件中被调用,作用域在本文件
  4. 修饰成员变量成为全局变量
  5. 用static修饰成员函数,使这个类只存在这一份函数,所有对象共享该函数
  6. 所有未加static前缀的全局变量和函数都具有全局可见性,static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏。

17、C中常量定义

1、#define 常量名 常量值
2、const 数据类型 常量名 = 常量值

18、#include 的用法

使用尖括号< >,编译器会到系统路径下查找头文件;
而使用双引号" ",编译器首先在当前目录下查找头文件,如果没有找到,再到系统路径下查找。

19、C编写的项目,提供有java的sdk是如何实现的

  • 做成动态库来调用?

20、静态链接库如何来调用

依赖静态库,在项目中把对应的.h放到开发项目中,最终调试也得通过命令来。
例如:gcc second.c ../lib/libCall.a -o second
最后执行./second就会执行。注意:静态库中别包含main函数,如果包含最终会失败,因为不知道执行哪个main函数
但编辑器CLion中不能直接执行了,只能通过命令来执行。

21、动态链接库如何来调用

生成动态链接库文件xx.so
在项目中#include动态链接库的.h文件,在目标文件中直接使用对应声明的函数。
在通过命令生成最终可执行程序 gcc main.c -o main -L. -lsum -Wl,-rpath=.

22、内存管理

C 语言为内存的分配和管理提供了几个函数。这些函数可以在 <stdlib.h> 头文件中找到。动态内存管理

  1. void calloc(int num, int size);
    在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num
    size 个字节长度的内存空间,并且每个字节的值都是 0。
  2. void free(void *address);
    该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。
  3. void *malloc(int num);
    在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。
  4. void *realloc(void *address, int newsize);
    该函数重新分配内存,把内存扩展到 newsize。

当程序退出时,操作系统会自动释放所有分配给程序的内存,但是,建议您在不需要内存时,都应该调用函数 free() 来释放内存。

malloc() 函数:用于动态分配内存。它接受一个参数,即需要分配的内存大小(以字节为单位),并返回一个指向分配内存的指针。

free() 函数:用于释放先前分配的内存。它接受一个指向要释放内存的指针作为参数,并将该内存标记为未使用状态。

calloc() 函数:用于动态分配内存,并将其初始化为零。它接受两个参数,即需要分配的内存块数和每个内存块的大小(以字节为单位),并返回一个指向分配内存的指针。

realloc() 函数:用于重新分配内存。它接受两个参数,即一个先前分配的指针和一个新的内存大小,然后尝试重新调整先前分配的内存块的大小。如果调整成功,它将返回一个指向重新分配内存的指针,否则返回一个空指针。

sizeof 运算符:用于获取数据类型或变量的大小(以字节为单位)。

指针运算符:用于获取指针所指向的内存地址或变量的值。

& 运算符:用于获取变量的内存地址。

  • 运算符:用于获取指针所指向的变量的值。

-> 运算符:用于指针访问结构体成员,语法为 pointer->member,等价于 (*pointer).member。

memcpy() 函数:用于从源内存区域复制数据到目标内存区域。它接受三个参数,即目标内存区域的指针、源内存区域的指针和要复制的数据大小(以字节为单位)。

memmove() 函数:类似于 memcpy() 函数,但它可以处理重叠的内存区域。它接受三个参数,即目标内存区域的指针、源内存区域的指针和要复制的数据大小(以字节为单位)。

示例

int *p = malloc(sizeof*p);
*p = 10;
printf("p1 = %p\n", p);
//当指针变量被释放后,它所指向的内存空间中的数据会怎样呢?free 的标准行为只是表示这块内存可以被再分配,至于它里面的数据是否被清空并没有强制要求。
free(p);
printf("p4 = %d\n", *p);
printf("p2 = %p\n", p);
*p=20;
printf("p3 = %d\n", *p);
p=NULL;
/**
*  p1 = 0x600002ee0030
p4 = -1156644816
p2 = 0x600002ee0030
p3 = 20
释放前后,p 所指向的内存空间是一样的。所以释放后 p 所指向的仍然是那块内存空间。
既然指向的仍然是那块内存空间,那么就仍然可以往里面写数据。可是释放后该内存空间已经不属于它了,
该内存空间可能会被分配给其他变量使用。如果其他变量在里面存放了值,而你现在用 p 往里面写入数据就会把那个值给覆盖,
这样就会造成其他程序错误,所以当指针变量被释放后,要立刻把它的指向改为 NULL。
*/

既然系统可以自动帮我们分配内存,为什么还需要我们程序员自己去分配内存呢?
动态内存是指在堆上分配的内存,而静态内存是指在栈上分配的内存
栈:存储局部变量、参数、返回值,一般几M,由系统分配和释放
堆:最大几G,由用户自动申请和释放。malloc函数申请到的内存块,一般我们申请的内存空间系统是不会帮我们释放的(当然有些也会由系统释放掉), 由我们的应用程序去控制,一般一个malloc就要对应一个delete/free,由程序员主动释放。
数据段静态区:static 和全局数据
代码段常量区

23、文件输入输出,标准 I/O

fwrite()用来一次性写入较大的数据块,主要用途是将数组数据一次性写入文件,适合写入二进制数据。
写入数组arr
fwrite(arr,sizeof(arr[0]),sizeof(arr) / sizeof(arr[0]), fp);
fread(),比较适合读写二进制数据。---写法和fwrite一样
feof()函数判断文件的内部指针是否指向文件结尾。
fseek() 记录当前打开的文件的读写位置(file position),即下一次读写从哪里开始
stdin 只用于读操作,称为标准输入
stdout 只用于写操作,称为标准输出
stderr 也用于写操作,称为标准错误输出
fopen()打开文件

fopen 函数参数 mode 总结:

  • "r":只读,文件必须存在。
  • "w":只写,如果不存在则创建,存在则覆盖。
  • "a":追加,如果不存在则创建。
  • "r+":允许读和写,文件必须存在。
  • "w+":允许读和写,文件不存在则创建,存在则覆盖。
  • "a+":允许读和追加,文件不存在则创建。
关闭文件
int close_file(FILE *fp){
    int r=fclose(fp);
    if(r==0){
        printf("file close success\n");
        return 0;
    }else if(r==EOF){
        printf("file close fail\n");
        return -1;
    }
    return 1;
}

void file_read_c(FILE *fp){
    //1、通过一个一个字符方式来读取文件-OK
    //fputc()和putc()用于向文件写入一个字符
    int c;
    while ((c=fgetc(fp))!=EOF){
        printf("%c",c);
    }
    printf("\n");

    //2、fputs()函数用于向文件写入字符串-OK
    char st[8];//接收字符串,每次读取6个大小
    while (fgets(st,6,fp)!=NULL){
        printf("%s",st);
    }
    printf("\n");

    //3、复制写文件-OK
    FILE *fp2;
    char st1[8];//接收字符串,每次读取6个大小
    while (fgets(st1,6,fp)!=NULL){
        fputs(st,fp2);
    }

    //4、读写二进制文件-OK
    char fileUrl[255]="/Users/opayc/products/rock_c/source/ffmpeg-6.0.tar.xz";
//    FILE *fp=fopen(fileUrl,"rb");
//
//    FILE *fp2=fopen("/Users/opayc/products/rock_c/source/ffmpeg-6.0-backup.tar.xz","a+b");
    char arr[10];
    int length=sizeof(arr) / sizeof(arr[0]);
    while (fread(arr,sizeof(arr[0]),length,fp)==length){
        fwrite(arr,sizeof(arr[0]),length,fp2);
    }
}

24、libevent打包成静态库进行依赖开发步骤

  1. 根据源码生成静态库
    执行成功后会在当前目录生成响应的Makefile
    cmake -DEVENT__DISABLE_OPENSSL=ON -DEVENT__LIBRARY_TYPE=STATIC CMakeLists.txt

cmake参数说明:
EVENT__DISABLE_OPENSSL:关闭ssl,这样可以避免下载openssl并设置环境变量。
EVENT__LIBRARY_TYPE:编译成静态库,方便后续使用时直接引用进行编译链接。
EVENT__DISABLE_DEBUG_MODE:关闭debug模式。
-G:指定构建系统生成器,据此生成不同平台下的Makefile格式。
CMAKE_MAKE_PROGRAM:make命令路径。
CMAKE_C_COMPILER:gcc路径。
CMAKE_CXX_COMPILER:g++地址。
-S:源目录。
-B:构建目录。

  1. make编译
    命令执行成功会生成对应静态库。
    之后执行,静态库在lib文件夹下
  2. 使用
    在项目中把include中的.h文件引入到项目中
    在项目中新建lib包,把make之后lib中的静态库复制到项目的lib中
  3. tip
    项目中引入的静态代码库,如果想执行,必须采用这种方式进行打包生成可运行程序。
    示例:gcc event-server.c ../lib/libevent/*.a -o event-server

25、extern用法

可以在一个文件中引用另一个文件中定义的变量或者函数。 extern int num;只是进行声明,让去其他的地方进行查找。 extern void func();

变量必须是全局变量。函数肯定是全局的。 告诉编译器这个是存在的,但是不是在这之前声明的,你到别的地方找找。

26、C中宏的含义

掌握"宏"概念的关键是“换”。一切以换为前提、做任何事情之前先要换,准确理解之前就要“换”。 宏是C语言中常用的编译预处理功能之一。 在编程时,可以使用宏来代替一些常量或表达式。

例如格式: #define 标识符 字符串 其中的标识符就是所谓的符号常量,也称为“宏名”。 类型:带参宏、不带参宏

Windows 有专有的宏_WIN32,Linux 有专有的宏__linux__

27、#if 用法的一般格式为

#if 整型常量表达式1 程序段1 #elif 整型常量表达式2 程序段2 #elif 整型常量表达式3 程序段3 #else 程序段4 #endif 它的意思是:如常“表达式1”的值为真(非0),就对“程序段1”进行编译,否则就计算“表达式2”,结果为真的话就对“程序段2”进行编译, 为假的话就继续往下匹配,直到遇到值为真的表达式,或者遇到 #else。这一点和 if else 非常类似。 需要注意的是,#if 命令要求判断条件为“整型常量表达式”,也就是说,表达式中不能包含变量,而且结果必须是整数;

28、#ifdef 的用法

#ifdef 用法的一般格式为: #ifdef 宏名 程序段1 #else 程序段2 #endif 它的意思是,如果当前的宏已被定义过,则对“程序段1”进行编译,否则对“程序段2”进行编译。

29、#ifndef 的用法

#ifndef 用法的一般格式为: #ifndef 宏名 程序段1 #else 程序段2 #endif 与 #ifdef 相比,仅仅是将 #ifdef 改为了 #ifndef。 它的意思是,如果当前的宏未被定义,则对“程序段1”进行编译,否则对“程序段2”进行编译,这与 #ifdef 的功能正好相反。

30、C中如何表示逻辑真和假

任何非0值都被视为逻辑的“真”。以0表示假

31、malloc的内存申请和释放

这里程序退出时会自动释放申请的空间,所以不使用free函数释放,也不会有太大问题, 但在大型的项目中可能会频繁的申请内存空间,或在自己定义的函数中申请内存空间,此时若不使用free释放,就会存在内存溢出的问题。

32、CLion运行C文件,也是根据CMakeLists.txt的配置来运行的

C/C++ 中如何引入第三方库

一、直接引入他人的源文件 直接在目标文件中引入.c等原文件 直接编译目标文件就行 二、分别引入头文件和源文件 目标文件引入头文件 编译时把引入的原文件一起编译到一个可运行文件中 三、引入头文件和静态库(打包好的二进制目标文件) 四、引入头文件和动态链接库(下文会具体解释什么是动态链接库) 把静态库或着动态库一起编译到可执行文件中

如何来处理主子CMakeLists.txt文件

CMake会首先处理主CMakeLists.txt,然后根据add_subdirectory的顺序逐个处理每个子目录中的CMakeLists.txt。 这样,每个子目录都会变成CMake项目的一部分,并参与编译。

stdout、stderr

stdout、stderr默认是输出到控制台,类型是FILE* 如果想输出到指定文件 // 打开文件,准备写入 freopen("output.log", "w", stdout); // 将stdout重定向到output.log文件 freopen("error.log", "w", stderr); // 将stderr重定向到error.log文件 // 现在,所有的标准输出和错误输出都会写入到对应的文件中 printf("This will go to the output file.\n"); fprintf(stderr, "This will go to the error file.\n"); // 关闭文件 fclose(stdout); fclose(stderr);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.