C Language
文章目录
教程
- C 语言教程 - 网道
awesome c
C, C++ Programming Tutorials - Cprogramming.com
- C 和 C++ 系列教程,各种实践问题解答
C Programming - Wikibooks, open books for an open world
- wiki books 中的 C 语言教程
- 支持 book 内部搜索
数据结构与算法
基本类型
size_t 类型
一种类型别名,具体对应的可能是 unsigned long、 unsigned int 等,不确定。
printf:
对应的占位符
%zu或%zd- %ul 或 %u(没有 %zu 等时作为替代)
取值范围: [0, SIZE_MAX]
基本运算
运算符
% 取余
注意:运算符必须是整数
关系运算符 > < == 等
注意:不可连用
eg:
| |
幂(指数)运算
正确用法:
| |
注意:
- 易错误使用:
^, 这是按位异或运算符,不是幂
问题
溢出处理
字面量
后缀
L 和 l
- 整数: long int
小数: long double
- 注意:不是 double
字符串
- 宽字节字符串
LL 或 ll
- 整数: long long
F 或 f
- 小数: float
U 或 u
- 整数: unsigned int
组合
- ull, ULL, LLU
输入输出
指示器
类型
- 错误指示器 Erro indicator
- 文件尾部指示器 End-Of-File indicator
- 位置指示器 Position indicator
功能:
- 输入输出库函数,通过设置这些指示器,来告知对应信息
特点:
- 全局变量,独立于库函数
查看方法:
- int ferror(FILE* pf): 错误指示器
- long int ftell(FILE* pf): 位置指示器
- int feof(FILE* pf): 文件末尾指示器 EOF
- 注:通过返回值,获取指示器
常规输入输出
scanf
- scanf("%3d%d",&a,&b)
- 注:%3d 只取 3 个数字,多余的留待下一个输入
printf
- printf("%x",a)
注:
- %x,%o 不会自动天剑前缀
- %Ld, %Le:L 表示 double 类型
- %ld:l 或 ll 表示 long int,long long int
- %zd: z 表示 size_t 类型
- %hd, %hu, %hx: h 表示 short, hu 表示 unsigned short
%m.nd:其中的“.n”
- 对于%f,%e,%F:小数点后位数
- 对于%g,%G:最大有效数字位数
- 对于%s:最大输出的字符数
flags 标记
左对齐
- :显示正负号
- 空格,0 :分别表示用空格或 0 当占位符
#:多功能
- %#o:八进制,输出加 0
- %#x:输出加 0x
- %#f,%#e,%#g:小数点后没数字,也要打印一个 0
- gets(str) 以空白符当截止符
- puts(str) 自动把末尾‘\0’输出成换行
字符输入输出
形式 A
- putchar(char)
- ch=getchar() 遇到汉字,可能需要两个连续的 getchar 接收
形式 B
- ch=getch()
形式 C
- ch=getche()
辨析
ch=getchar() 按 enter 键,才能把前面键入的内容,存到 ch
- 独特:它会通过多次调用来取走输入缓冲区(之前输入的)现有内容,用完之后再要求用户输入
- 空格,‘\n’它也会取,记住:这是取字符,不是数值,不是字符串
- ch=getch() 键盘键入字符,ch 立刻就得到
ch=getche() 与 getch 相同,
除掉:你键入字符,它赋值给 ch,同时在还会它在屏幕输出
- getch(), getche()需要调用 conio.h 头文件
- 不可在 emacs 上调用 test.exe, 不然出错
对文件流的字符输入输出
特别
getc(FILE*)
例子:getc(stdin) 相当于 getchar()
putc(int, FILE*)
1 2 3 4 5 6 7 8 9 10 11 12 13#include <stdio.h> void main() { int ch; FILE *pfin = stdin; //定义一个文件指针,并指向标准输入设备(键盘) FILE *pfout = stdout; //定义一个文件指针,并指向标准输出设备(屏幕) printf("Enter a string: "); ch = getc(pfin); //使用getc()函数获取缓冲区中的第一个字符 putc(ch, pfout); //使用putc()函数输出该字符 putc('\n', pfout); //使用putc()函数输出换行字符 }
文件输入输出
- 对应头文件:<stdio.h>
开关文件
- FILE* fp
fp=fopen(str_path, str_mode)
例子
- fp=fopen("d:\home\\text.c",“rb”)
- fp=fopen("/home/text.c","rb")
char str[100]; fp=fopen(str,"rb")
读写模式
1 2 3 4 5 6 7 8 9 10 11 12 13"r"(只读) 输入数据,打开文本 "rb"(只读) 输入数据,打开文本(二进制) "w"(只写) 输出数据,打开文本 文件不存在时,会建立新文件 "wb"(只写) 输入数据,打开文本(二进制) 文件不存在时,会建立新文件 "a"(追加) 末尾添加数据 "ab"(追加) 末尾添加数据(二进制) "r+"(读写) "rb+"(读写)(二进制) "w+"(读写)文件不存在时,会建立新文件 "wb+"(读写)(二进制) 文件不存在时,会建立新文件 "a+"(读写) "ab+"(读写)(二进制)
- fclose(fp)
freopen
- *FILE *freopen( const char *path, const char *mode, FILE *stream );
- 功能把 stdin,stdout 重定向到 path 指向的文件
注意:
要记得关闭 stdin,stdout,即 fclose(stdin)
1 2 3 4 5 6 7 8 9 10 11 12#include <stdio.h> int main() { int a,b; freopen("D:\\in.txt","r",stdin); //输入重定向,输入数据将从D盘根目录下的in.txt文件中读取 freopen("D:\\out.txt","w",stdout); //输出重定向,输出数据将保存在D盘根目录下的out.txt文件中 while(scanf("%d %d",&a,&b)!=EOF) printf("%d\n",a+b); fclose(stdin);//关闭重定向输入 fclose(stdout);//关闭重定向输出 return 0; }
判断文件开关是否成功
fopen(…) == NULL
文件打开模式
| |
文件读写函数
单个字符
fgetc(fp)
- 从 fp 读取一个字符
fputc(char,fp)
- 向 fp 写入一个字符
失败判断:
- 返回 EOF 即-1
字符串
fgets(str, size, fp)
注意:
- 实际读取的字符个数是 size-1, 因为 str 带的‘\0’不必读取,它自己添加
失败判断:
- 成功则返回 str 地址
- 失败返回 NULL
fputs(str, fp)
失败判断:
- 成功返回 0
- 失败返回-1
格式化读写文件
fscanf(fp, "%d%c", &a,%ch)
失败判断:
- 成功:返回读取参数个数,即上面的 a 和 ch,是 2
- 失败:EOF, 或 读取错误
fprintf(fp, "%d%c", a, ch)
失败判断:
- 成功:返回输出字符个数
- 失败:EOF,或 写错误
实质分析
- 把文件当成 stdin,stdout 了,其他都一样
二进制读写文件
整块读写
- fread(buffer, size, count, fp)
- fwrite(buffer, size, count, fp)
注:
- buffer:拿来存储数据的地址,可以是字符数组 str[100]
- size:读取字节数
count:要读取数据项数
- 注意:实际只要 size 与 count 的乘积,小于等于 buffer 的大小就好了 如:上 size*count<=100 即可
- 一般吧 size=sizof(buff), count=1, 容易判断成功读写
- fp:file pointer, FILE*
注意:
失败判断:
成功:返回 count(上述)
1fread(&t,sizeof(Node),1,fp)==1 //判断读写成功- 失败:feof, ferror,读写错误,EOF
- size=0,或者 count=0,什么也不做
整数读写,二进制
int getw(FILE+ fp)
- 从 fp 读取一个整数
异常判断:
- 正常返回对应整数
- 错误:EOF(-1),用 feof()或 ferror()判断文件结束还是出错
int putw(int n, FILE* fp)
- 从 fp 写入
文件定位
恢复默认
void rewind(FILE* fp)
- 功能:把文件重新指向开头位置
随机定位
- int fseek(FILE* fp, long offset, int base_position)
- 功能:把文件指针 fp 指向,以 base_position 偏离 offset 个字节位置
- offset:偏移量
base_position:偏移基准
三个基准
项目 解释 SEEK_SET 文件开头 SEEK_CUR 当前位置 SEEK_END 文件末尾
例子:
1 2 3 4 5 6fseek(fp, 0L, SEEK_SET); // 定位至文件开始处 fseek(fp, 10L, SEEK_SET); // 定位至文件中的第10个字节 fseek(fp, 2L, SEEK_CUR); // 从文件当前位置前移2个字节 fseek(fp, 0L, SEEK_END); // 定位至文件结尾 fseek(fp, -10L, SEEK_END); // 从文件结尾处回退10个字节 //注:"L"表示long int错误判断:
正常调用:
- 返回指针位置 int 型
失败判断:
- 返回-1, 表示定位失败
当前位置
- long int ftell(FILE* fp)
- 功能:返回当前位置 long int
失败判断:
- 失败返回-1L
其余定位函数
int fgetpos( FILE *fp, fpos_t *pos );
- 返回值:成功返回 0,否则返回非 0
int fsetpos(FILE *fp, const fpos_t *pos);
- 返回值:成功返回 0,否则返回非 0
- 类型 fpos_t 实际是一个整数 integer
C 语言中的缓冲区
目的
协调磁盘与程序速度的不同步,
- 磁盘慢,程序快
- 磁盘以块为单位读写,512KB 为一块
- 程序一个字节一个字节地读写
- 如此可以提升整体速度
输入输出类型
- 输入缓冲区
- 输出缓冲区
- 注:输入输出各用一个缓冲区,不相同
常见输入输出类型
- stdin
- stdout
- stderr
操作类型
全缓冲
- 在这种情况下,当填满标准 I/O 缓存后才进行实际 I/O 操作。
- 全缓冲的典型代表是对磁盘文件的读写。
行缓冲
- 在这种情况下,当在输入和输出中遇到换行符时, 执行真正的 I/O 操作。这时,我们输入的字符先存放在缓冲区, 等按下回车键换行时才进行实际的 I/O 操作。
- 典型代表是标准输入(stdin)和标准输出(stdout)。
不带缓冲
- 也就是不进行缓冲,标准出错情况 stderr 是典型代表, 这使得出错信息可以直接尽快地显示出来。
- 代表 stderr
缓冲区大小
- 系统默认,标准输入输出缓冲区 512Byte 字节
C 语言,
stdio.h 定义
- 其中由宏 BUFSIZ 规定大小
- 自己设定
相关函数:
- setvbuf(), setbuf()
如何清空缓冲区(刷新)
特定条件:
- 缓冲区满时
- 黄缓冲区,遇到 Enter 键
- 关闭文件
使用特定函数
- fflush(fp)
与缓冲区 stdin 相关的几个函数
- getchar()
- getch()
- getche()
流错误
- 操作错误指示器 Error indicator
- 文件末尾指示器 End-Of-File indicator
EOF
int feof(FILE *fp)
- 功能:检查文件操作异常,是不是因为 EOF
返回值:
- 1,非零,true:是 EOF
- 0, false:不是 EOF
检查错误,非 EOF
- ferror
- 原型:int ferror(FILE *stream)
功能:
- 检查是否发生错误, 即:检查错误指示器:Error indicator
- 并返回错误序号
返回值:
- 非零,true:发生错误
- 0,false:没有发生错误
清空错误
- clearerr
- 原型:void clearerr ( FILE * stream );
功能:
- 清空错误,
- 把 Error indicator 设置为 0, 即重设
- 重设 EOF indicator
自动清空错误
- fseek
- rewind
- fsetpos
- freopen
尝试读取 stdin 输入
参考:
例子:
| |
解释:
- 通过 sscanf() 试错
C 语言错误处理
错误序号
- errno
- 头文件:<errno.h>
解释:
- 最后错误号 Last error number
- int 类型
- 代表错误类型
- 初始值:0,代表没错误
修改:
- 可被程序修改成几个特定的宏
注意:
调用输入输出库函数之前
- 要把它 errno 置零
- 原因:因为之前调用的程序,可能已经更改过它了
几个特定宏:
宏 英文 解释 例子 EDOM Domain error 数学函数运算,定义域错误 如:sqrt() ERANGE Range error 值域错误 如:pow(),结果超出 double 最大表示范围 strtod(),超出 double 表示范围 EILSEQ Illigal sequence 多字节字符 character, 给定的序列,不存在对应的字符编码
输出错误
- perror
- 头文件<stdlib.h>
- 原型:void perror ( const char * str );
功能:
- 把上次的错误,取出并通过 stderr,输出
输入方式:
- 先输出参数 str,再输出具体错误内容
注意:
操作的 errno,通过 errno 获取错误信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15#include <stdio.h> int main () { FILE *fp; fp = fopen("file.txt", "r"); if( fp == NULL ) { perror("Error: "); return(-1); } fclose(fp); return(0); }
错误字符串
- strerror strerror_r
- 头文件:<string.h>
功能:
- 返回指向错误信息的字符指针 char* str
原型:
- char* strerror(int error_number)
- char* _strerror(const char* strErrMsg)
- wchar_t* _wcserror(int error_number)
- wchar_t* __wcserror(const char* strErrMsg)
注:
- error_number: 错误代码
- strErrMsg: 用户提供的错误信息
清爽代码
if else
单行写法
1 2 3 4 5 6 7 8 9if (pFile == NULL) perror ("Error opening file"); else { fputs ("test",pFile); fflush (pFile); // flushing or repositioning required fgets (mybuffer,80,pFile); puts (mybuffer); fclose (pFile); return 0; }
格式化输入输出
- 头文件:<stdio.h>
- scanf and printf
sscanf and sprintf
- sscanf(char* buf, "%s%d", &s, &n)
- sprintf(char* buf, "%s", string)
功能:
- 即:用字符串,代替 stdin,stdout 来操作
- char* buf: 即用来代替 stdin,stdout 的字符串
snscanf and snprintf
- snprintf(char* buf, int size, "%s", str)
注:
- size: 限制 char* buf 中只有 size 个字符用于输入输出
fprintf and fscanf
- fprintf(FILE* fp, "%s", str)
功能:
- 用 FILE* fp,代替 stdin, stdout 来操作
vprintf and vscanf
- 来源头文件<stdio.h>
- 相关头文件<stdarg.h>
- 原型:int vprintf ( const char * format, va_list arg );
解释:
- 把 va_list 类型(argument list 类型)参数列表变量 arg,用 format 中的格式输出
注意:
- 使用前提:先要掌握<stdarg.h>头文件中的所有宏,不然白费力,就是看不明白
- vsprintf and vsscanf
- vsnprintf and vsnscanf
- vfprintf and vfscanf
头文件<stdarg.h>
va_list
- 中文:variable argument list 函数参数列表变量类型
解释:
- 一种特殊类型,用来存放变量 variable argumnet list(即函数的参数表)
va_start
- 原型:void va_start (va_list ap, paramN);
解释:
- 把 paramN 之后的(不包括它本身)函数参数,存放到 argument list 变量 ap 中
- 用来标志,argumnet list 变量 ap,使用范围的开始
va_end
- 原型:void va_end (va_list ap);
解释:
- 用来标志,argument list 变量 ap,使用范围的结束
va_arg
- 原型:type va_arg (va_list ap, type)
解释:
- 取出 argument list 变量 ap 中的一个元素,并把它转换成类型 type,返回
va_copy
- 原型:void va_copy (va_list dest, va_list src);
解释:
- 把 va_list src 中元素,复制到 va_list dest 中
| |
二进制用途
& 按位与
把某一位置为 0
num & 0000 11111 把前四位置为 0,取出后四位不便
取特定位的数值
实例如下
判断奇偶书
if (num % 2 = 0) ------> if(num & 1 = 0)
注解:num & 1 –> num & 0b000001 即取出最末位的数值。
把特定位置零
一体两面,与上面的例子是,同一件事的另一个看待问题的方式。
清零
num & 0000 0000
| 按位或
把某一位置为 1
num | 0000 1111 前四位保持不变,把后四位置为 1
^ 按位异或 xor
使特定位数值翻转
num ^ 0000 1111 前四位保持不变,把后四位数值翻转,0变为 1,1 变为 0 eg:
1001 0110 ^0000 1111
1001 1001
与 0 相^异或,保持不变
与 1 相^异或,数值翻转
交换整数的数值,不需要临时变量
| |
异或是可以逆运算的
即: c = a ^ b
则 (1) b = c ^ a, (2) a = c ^ b
异或满足交换律
即: c = a ^ b
则: (1) b = c ^ a = a ^ c, (2) a = c ^ b = b ^ c
注:通过这里的逆运算规律,和交换律,即可推出证明上面的交换数值算法 是正确的。
<< 左移运算符
右侧补零
左侧
符号位,没有被占用时,不影响符号,相当于乘以 2
0000 1111 >> 2 —> 0011 1100 左移 2 位,没有改变符号位,相当于乘以 2 的平方
符号位,被占用,而且改变符号
0000 1111 —> 15 十进制 0000 1111 >> 4 —> 1111 0000 左移 4 位,符号位改变,应当做改变后的符号来解释 上面例子,变为负数,当补码解释 1111 0000 - 1 —> 补码 1110 1111 —> 原码 1001 0000 —> -16 十进制 注:char 类型,最左面一位是符号位,原码补码之间的转化,保持符号位 固定不变。
| |
当符号位改变后,在不变的接下来一段时间内,左移位仍然是相当于乘 2 的操作
上一小节的代码输出,显然符合本结论
>> 右移位操作
对于正数
符号位不变,左侧补零,右侧丢弃
15 —> 0000 1111 >> 1 —> 0000 0111
15 —> 0000 1111 >> 4 —> 0000 0000 —> 0 十进制
正数,右移位的最终结果是,最后只剩下 0
不只是简单的,相当于除以 2
| |
对于负数
符号位保持不变,左侧补一,右侧舍弃,
-16 —> 1111 0000 >> 1 —> 1111 1000
-16 —> 1111 0000 >> 4 —> 1111 1111 —> -1 十进制
负数,右移位的最终结果是,最后所有位都是 1,即-1 的补码,-1
不只是相当于,简单的除以 2
| |
类型转换
负数赋值给无符号型
| |
-16 —> 1111 0000 —> 直接当成无符号型解释,最左面一位,当成普通 数值解释,变成正数,1111 0000 —-> 正数 240
二进制数值内存中的数值保持不变,直接强行解释成正数
补码
设 4 位的有符号型
| decimal | binary | |
|---|---|---|
| 原码 | 补码 | |
| 0 | 0000 | |
| 1 | 0001 | |
| 2 | 0010 | |
| 3 | 0011 | |
| 4 | 0100 | |
| -1 | 1001 | 1111 |
| -2 | 1010 | 1110 |
| -3 | 1011 | 1101 |
| -4 | 1100 | 1100 |
加法
正数 加 正数
1 + 1 —-> 0001 + 0001 —> 0010 —> 2(10 进制)
正数 加 负数,按位相加(补码形式)
1 + (-1) —-> 0001 + 1111 —> 按位相加 —>
0001
1111
10000 —> 溢出最左侧的 1 —> 0000 —> 0
负数 加 负数
(-1) + (-1) —> 1111 + 1111—> 按位相加 —->
1111
1111
11110 —-> 溢出最左侧的 1 —> 1110 负数补码 —> 负数原码 1010 —> -2(10 进制)
综上:使用补码形式的加法,能够解决所有的加减法问题,(只需要按位相加即可)
动态链接库
参考: https://www.cnblogs.com/zuofaqi/p/10440754.html
linux 制作动态链接库
Just one file
| |
More than one file
| |
linux 使用动态链接库
链接
| |
查看使用了哪些动态链接库
| |
使用命令 ldd
查看动态链接库或可执行文件本身
- 使用 nm 命令
- 可以查看文件中的符号信息
| |
关于找不到动态链接库文件
如上述例子中: libmath.so
解决方法
使用环境变量
1LD_LIBRARY_PATH=your_lib_path ./main.out复制动态链接库到,系统的动态链接库目录,或做符号链接
- /usr/lib 实测有效
- /usr/local/lib 实测无效
修改/etc/ld.so.conf 文件
- 把你的动态链接库目录,添加到/etc/ld.so.conf 文件
- 运行命令 ldconfig
- 现在就可以运行你的程序./main.out
Windows 动态链接库
注: 根据博客整理,没有测试是否有效。 https://blog.csdn.net/qq_33757398/article/details/81545966
特殊修饰符
| |
- 在函数定义的地方 加上: extern "C" _declspec(dllexport)
- 在函数声明的地方 加上:extern "C" _declspec(dllimport)
头文件
- 在制作动态链接库时的头文件不需要独立 dllimport
- 在使用动态链接库时的头文件必须有 dllimport
- _declspec 与 dllexport 等是 MFC 中的内容
头文件(.h)
| |
实现文件(.c)
| |
调用动态链接库
| |
注意事项
- 32 位动态链接库,64 位不能混用
- 复制过来,“.dll”文件和同名“.lib”文件
Python/C API
python 与 C 互相调用
- 给 python 写 C 扩展
- 把 python 嵌入到 C
在 C 中调用 python
工具
PyRun_
- PyRun_SimpleString("python expression")
- PyRun_SimpleFile
实例
| |
函数
修饰符
extern: 标明原型函数来自其他文件
- 注意:函数证明(这里说的不是定义)默认就是 extern
- static: 表示函数
只可以在当前文件使用
const
限制指针
限制指向的值:
const int* p- 即,常量指针
限制指向的变量:
int* const p- 即,指针常量
记忆:
- 表示值, const int* 限制值
数组
列表初始化
- 长度小于数组长度的处理
列表小于数组长度时,长度不足的部分被初始化为 0
例子:
| |
- 随机元素的赋值
例子:
| |
variable length arrary 变长数组
注意:这个特性,C++ 编译器不一定支持,C++ 推荐使用 vector
特点:
- 运行时声明长度,不再必须是常量
数组复制
把 a 复制到 b:通过 string.h 中的 memcpy(a,b,size) 完成
函数和数组
数组作为参数
定长数组
1 2 3 4 5 6 7 8 9/* 一维数组 */ int sum_array(int a[], int n) { // ... } /* 二维数组 */ int sum_array(int a[][4], int n) { // ... }- 变长数组
数组字面量作为参数
| |
方法:
- 做数组类型转换
unicode 和 编码格式
参考:
11.1 Programming with Unicode – C language
- Unicode 知识大全
{C语言基础}1.字符集、字符编码与字节序_TimepassbyZ 的博客-CSDN博客
- 概念介绍,没有代码例子
官方文档
宽字节字符 wchart_t
char*
locale
工具收集
文件编码识别工具
- uchardet: https://github.com/BYVoid/uchardet[[https://github.com/BYVoid/uchardet][GitHub - BYVoid/uchardet: An encoding detector library ported from Mozilla]]
编码转换工具
控制 windows console(cmd)编码格式
控制 linux 系统 locale 方法
linux 和 windows 中 GbK 编码和 utf-8 编码字符串转换
C++ 读取不同编码文件的方法
C 语言使用编码格式解释:
- C 语言没有功能齐全的字符编码库
- 通过 locale.h 中的 setlocale(category, name) 设置本地编码,类似 linux locale
C 中的 char* 相当于 python 中的 bytes, 即只支持 ASCII 字符
- 如果包含
非 ASCII 字符就无法处理了 - 需要借助别的工具把其 unicode 字符串转换成多字节的 char* 类型字符转,例如: wcstombs()
- 如果包含
如果 locale 不是
"C"(C 语言内部默认 locale, 即 ASCII 英语字符集), char* 实际存储的是多字节 bytes, 编码即 locale 指定的编码格式- 假设使用的是 gbk 编码, 参考:Ubuntu中安装GBK locale_wenwenxiong 的博客-CSDN博客
- 那么,char* 存储的就是 gbk 编码格式,多字节表示一个字符
内部 wchar_t* 使用的编码,由系统和编译器(gcc)决定
- utf-16 (Windows, AIX)
- utf-32 (Linux, MacOS)
- 参考:11. Programming languages — Programming with Unicode
编码控制和转换
控制方法:
- 通过 locale.h, setlocale 控制
- 作用范围: char 和 char* 类型以及相关函数
转换方法:
使用 stdlib.h 中 wcstomb() 等工具,把 wchart_t* 类型方法转换成 chart* 类型
- mbstowcs() 把 char* 转换成 wchart_t*
编码使用范围
- 注意区分
语言内部编码和语言外部编码 语言内部编码
char*
编码格式不固定
- 由系统和编译器决定
wchar_t*
- 使用 unicode 编码
Windows
- 使用 utf-16, BMP 以外的字符,需要两个 wchart_t 单元表示一个字符
Linux 和 MacOS
- 使用 utf-32, (和 UCS-6 一致,等价于 unicode),一个字符,一个 wchart_t
语言外部编码
范围:
- 文件,stdin, stdout 等外部存储
- 举例: cmd 和 powershell 通过
chcp 936改变 stdin stdout 编码格式
作废笔记:
内部编码 + locale 为 "C" 时,或只使用 ascii 字符时,默认 ascii 编码 + locale 为其 gbk 等非 "C"(英语语言) 等时,使用 locale 设定编码,多字节表示一个字符
locale 和 编码
- locale 影响的是 char* 相关库函数
- locale 不决定 char* 的编码格式
locale 处理的是本地化函数的结果,例如:千位分隔符、日期时间格式、货币符号等等
- 参考:<locale.h>, C Programming/locale.h - Wikibooks, open books for an open world
- lconv struct 结构体存储的没有编码相关信息
locale 中的编码, eg:
en_US.UTF-8- 这里的编码说明的是程序外部环境(编码)情况
- 代码中
char str[]中存储的字符编码由 editor 第一时间决定
- 文件编码的读取和 locale 无关,改变 locale 也不能适配文件编码的读取
bytes 类型
使用 chart* 表示
unicode 类型
使用 wchart_t* 表示
源码的编码处理
参考:
流程:
- 源码文件自己有一个编码格式:A
- 编译器解码代码文件有一个编码格式:B(源字符集)
- 编译器把代码制作成二进制机器码使用编码格式:C(执行字符集)
控制编码格式方法:
- 编辑器控制代码文件编码格式(A)
gcc 控制 B 和 C 的方法:
机器码编码格式指定(C)
1-fexec-charset=UTF-8源码文件编码格式指定(B)
1-finput-charset=GBK
变量修饰符
extern
作用:
- 修饰函数声明或全局变量:这是跨文件的函数或变量
- 修饰函数内的局部变量:这个变量是外部变量(非内部定义的局部变量),每次使用从函数外部获取它的值
用途:
修饰跨文件变量
不可作用于
赋值的变量定义并初始化语句,否则无效1 2 3 4 5 6extern int i; //正确用法 // 不规范用法 extern int j = 1; // --> 等价于 int j = ;
修饰函数声明
- c 语言中,函数证明默认自带
extern - 因此,函数不用使用
extern也可以, 即使是跨文件函数
- c 语言中,函数证明默认自带
volatile
背景介绍:
- 优化问题:compiler 会优化代码,长久不变的()会被读入 cache, 并且长久只使用这个值。
实际需求:这个变量但是可能很长时间以后突然改变,而我们需要监测的就是这个突然变化,由于 compiler 的
优化, 我们无法获取到这个最新的值,造成问题的产生 参考:declaration - Why is volatile needed in C? - Stack Overflow- 并行、并发任务可能会触发这个问题,例如:在另一个线程改变了一个全局变量,一个硬件改变了一个变量
volatile就是用来解决这种问题的,它强制 compiler 不要优化被修饰的变量,每次读取变量时, 使用的都是最新的值
作用:
- 放置 compiler 对变量进行优化处理(缓存)
- 强制读取最新的变量值(原始位置),而不是从缓存中读取过时数据
- 对于写也是一样的道理,修改的是原始位置的值,而不是缓存中的临时值
restrict
参考:
作用:
- 放置其他通过指针修改被指向的值
- 设定当前指针是访问变量的唯一方式
例子:
| |
多线程
threads.h – C 语言标准库并发
参考:
pthread.h – posix thread
参考:
- pthread详解_提出问题 解决问题的博客-CSDN博客_pthread_attr_setscope
- Multithreaded Programming (POSIX pthreads Tutorial)
- C语言基本并发操作简介_从日至臻的博客-CSDN博客_c 语言并发
相关概念
- pthread: Posix Thread
NPTL: native POSIX thread library
- Native POSIX Thread Library - Wikipedia
- 一种 pthread 在 linux 中的实现
文章作者
上次更新 2023-02-01 (9aed3e4)