标准库详细的可以在菜鸟查到
菜鸟教程链接
#include<assert.h>
以前在字符操作中有所用到
assert(src!=NULL);//该功能称为断言,当条件为假的时候,就会出现断言错误,显示错误信息
assert在系统定义中是个宏,虽然看起来是个函数,其实是个宏函数
如果程序中定义了NDEBUG宏,则所有的断言会被空语句代替
一个项目:
调试阶段称为debug版本
在这个阶段程序员为了找出问题,要详细报错,就会写assert
所以这个阶段不会定义NDEBUG宏
上线阶段称为release版本
上线阶段定义NDEBUG,因为在调试阶段我们已经用assert达到了目的,找出了错误并解决了,
人为一句句删除assert是不合理的。而定义NDEBUG后,
程序中的assert就不会去测试(某种意义上,定义了NDEBUG这个宏,就是删除了assert的有关语句),
提高了程序效率。
gcc -D NDEBUG x.c //在命令行定义宏测试
断言一般不用&&连接条件,尽量分开来写,因为容易判断不出来哪个错哪个对
不要执行有意义的语句i++之类 assert(i++>0);
assert写完一般空一行(编程习惯)
#include <stdio.h>
#include <assert.h>
//程序的美观和阅读性 空格 空行
size_t mylen(const char *s){
assert(s!=NULL);//断言 函数 宏函数
size_t len = 0;
for(;s[len]!='\0';len++);
return len;
}
int main(){
printf("%u\n",mylen("Hello"));
printf("%u\n",mylen(NULL));//开发或者高度阶段有这样的代码
//上线之后肯定是没有
return 0;
}
//结果返回为:
5
a.out: try.c:5: mylen: Assertion `s!=((void *)0)' failed.
已放弃 (核心已转储)
#include<ctype.h>
int isalnum(int c) 该函数检查所传的字符是否是字母和数字。
int isspace(int c) 该函数检查所传的字符是否是空白字符。
空白字符是指: \n \t 空格等 看不见的。
....还有很多,用到及查
这些函数接受 int 作为参数,它的值必须是 EOF 或表示为一个无符号字符。
如果参数 c 满足描述的条件,则这些函数返回非零.如果参数 c 不满足描述的条件,则这些函数返回零。
#include <stdio.h>
#include <ctype.h>
int main(){
int ret = isalnum('a');
printf("%d\n",ret);
ret = isalnum('*');
printf("%d\n",ret);
return 0;
}
//返回
8
0
两个转换函数:
int tolower(int c)
该函数把大写字母转换为小写字母。
int toupper(int c)
该函数把小写字母转换为大写字母。
用assert自己实现上面的两个转换函数:
'A' 65 'a' 97 '0' 48 '\0' 0
int tolower(int c){
assert(c >= 'A');
assert(c <= 'Z');
return c-'A'+'a';
}
int toupper(int c){
assert(c >= 'a');
assert(c <= 'z');
return c-'a'+'A';
}
c语言的函数:传参
char short -> int 自动转换
float -> double
#include<errno.h>
#include<errno.h> //声明extern int errno;全局了
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
当系统调用(函数)失败时才能用errno,用户自建的函数不能用这个来查询
strerror(errno);//用来查看错误信息,包含在string.h中
#include <stdio.h>
#include <errno.h> //声明了全局部变量 extern int errno;
#include <stdlib.h>
#include <string.h>
int main(){
printf("%d\n",errno); // 打印0,这时候没错误,0是success
int *p = malloc(0xFFFFFFFF);
if(p == NULL){//malloc失败 errno!=0
//printf("malloc failed!\n");
printf("%d\n",errno);// 打印了12
printf("%s\n",strerror(errno)); //Cannot allocate memory 无法分配内存
}
FILE *fp = fopen("a.txt","r"); //a.txt是不存的
if(fp == NULL){
printf("%d\n",errno); //打印了2
//printf("fopen a.txt failed!\n");
printf("%s\n",strerror(errno)); //No such file or directory
}
p = malloc(4);//malloc成功 不会把errno设置成0
if(p != NULL){
puts("malloc没有失败!\n"); //实际上malloc是成功的,这个也会打印,按理说errno应该是0
}
/* errno只能用于获取错误信息,不能用于判断调用是否成功
如果一个函数调用成功,其实是不会设置errno的
如果之前errno就是非0,后面用的时候虽然是成功调用,但是errno的值没有改变,还是之前的那个状态,所以还是错*/
if(errno != 0){
printf("不能用errno!=0来判断失败!\n"); //这里也会打印
}
//查看error中数字代表的错误意思
printf("--------------------\n");
int n = 0;
for(n=0;n<100;n++){
printf("%2d : %s\n",n,strerror(n));
}
/*--------------------
0 : Success
1 : Operation not permitted
2 : No such file or directory
3 : No such process
4 : Interrupted system call
5 : Input/output error
6 : No such device or address
7 : Argument list too long
8 : Exec format error
9 : Bad file descriptor
10 : No child processes
11 : Resource temporarily unavailable
12 : Cannot allocate memory
13 : Permission denied
14 : Bad address
15 : Block device required
16 : Device or resource busy
17 : File exists
18 : Invalid cross-device link
19 : No such device
20 : Not a directory
21 : Is a directory
22 : Invalid argument
23 : Too many open files in system
24 : Too many open files
25 : Inappropriate ioctl for device
26 : Text file busy
27 : File too large
28 : No space left on device
29 : Illegal seek
30 : Read-only file system
31 : Too many links
32 : Broken pipe
33 : Numerical argument out of domain
34 : Numerical result out of range
35 : Resource deadlock avoided
36 : File name too long
37 : No locks available
38 : Function not implemented
39 : Directory not empty
40 : Too many levels of symbolic links
41 : Unknown error 41
42 : No message of desired type
43 : Identifier removed
44 : Channel number out of range
45 : Level 2 not synchronized
46 : Level 3 halted
47 : Level 3 reset
48 : Link number out of range
49 : Protocol driver not attached
50 : No CSI structure available
51 : Level 2 halted
52 : Invalid exchange
53 : Invalid request descriptor
54 : Exchange full
55 : No anode
56 : Invalid request code
57 : Invalid slot
58 : Unknown error 58
59 : Bad font file format
60 : Device not a stream
61 : No data available
62 : Timer expired
63 : Out of streams resources
64 : Machine is not on the network
65 : Package not installed
66 : Object is remote
67 : Link has been severed
68 : Advertise error
69 : Srmount error
70 : Communication error on send
71 : Protocol error
72 : Multihop attempted
73 : RFS specific error
74 : Bad message
75 : Value too large for defined data type
76 : Name not unique on network
77 : File descriptor in bad state
78 : Remote address changed
79 : Can not access a needed shared library
80 : Accessing a corrupted shared library
81 : .lib section in a.out corrupted
82 : Attempting to link in too many shared libraries
83 : Cannot exec a shared library directly
84 : Invalid or incomplete multibyte or wide character
85 : Interrupted system call should be restarted
86 : Streams pipe error
87 : Too many users
88 : Socket operation on non-socket
89 : Destination address required
90 : Message too long
91 : Protocol wrong type for socket
92 : Protocol not available
93 : Protocol not supported
94 : Socket type not supported
95 : Operation not supported
96 : Protocol family not supported
97 : Address family not supported by protocol
98 : Address already in use
99 : Cannot assign requested address
*/
return 0;
}
#include<float.h>
5.25
101.01 ===> 1.0101 * 2^2
符号位
尾数
指数位
float.h有一个特定的宏,详细的可以看上面的网站
#include<limits.h>
很多宏:limits.h 头文件决定了各种变量类型的各种属性
CHAR_BIT 8 定义一个字节的比特数。
SCHAR_MIN -128 定义一个有符号字符的最小值。
SCHAR_MAX 127 定义一个有符号字符的最大值。
UCHAR_MAX 255 定义一个无符号字符的最大值。
CHAR_MIN 0 定义类型 char 的最小值,如果 char 表示负值,则它的值等于 SCHAR_MIN,否则等于 0。
CHAR_MAX 127 定义类型 char 的最大值,如果 char 表示负值,则它的值等于 SCHAR_MAX,否则等于 UCHAR_MAX。
MB_LEN_MAX 1 定义多字节字符中的最大字节数。
SHRT_MIN -32768 定义一个短整型的最小值。
SHRT_MAX +32767 定义一个短整型的最大值。
USHRT_MAX 65535 定义一个无符号短整型的最大值。
INT_MIN -2147483648 定义一个整型的最小值。
INT_MAX 2147483647 定义一个整型的最大值。
UINT_MAX 4294967296 定义一个无符号整型的最大值。
LONG_MIN -9223372036854775808 定义一个长整型的最小值。
LONG_MAX 9223372036854775807 定义一个长整型的最大值。
ULONG_MAX 1.8446744e+19 定义一个无符号长整型的最大值。
考虑代码的可移植性,建议使用以下类型替换int,long,long
因为32和64位处理器问题,移植后可能会出现问题
以下面这种形式写相对好
#include <inttypes.h>
int8_t 8bit uint8_t
int16_t 16bit uint16_t
int32_t 32bit uint32_t
int64_t 64bit uint64_t
#include <stdio.h>
#include <inttypes.h>
//用这些
int main(){
int8_t a = 10; //8bit 1个字节
int16_t b = 10; //16bit 2个字节
int32_t c = 10; //32bit 4
int64_t d = 10; //64bit 8
printf("%d\n",sizeof(int8_t));
printf("%d\n",sizeof(int16_t));
printf("%d\n",sizeof(int32_t));
printf("%d\n",sizeof(int64_t));
//int long long char
return 0;
//结果 1 2 4 8
}
#include<time.h>
time_t t = time(NULL)
ctime(&t)
localtime(&t) //结构体
#include <stdio.h>
#include <time.h>
int main(){
//例如当你遇到计费问题时,就要涉及到时间问题
time_t t = time(NULL);//能够获得当前系统时间 一个从1900年1月1日零点0分0秒到当前时间的秒数
printf("%d\n",t);
struct tm *pt = localtime(&t);
//因为从1900开始
printf("%d-%d-%d\n",1900+pt->tm_year,1+pt->tm_mon,pt->tm_mday);
printf("%d:%d:%d\n",pt->tm_hour,pt->tm_min,pt->tm_sec);
printf("%s\n",ctime(&t));
//能够把秒数转为固定格式
//星期 月 日 时:分:秒 年
return 0;
}
//输出
2021-5-16
16:6:53
Sun May 16 16:06:53 2021
man一个localtime //查看具体内容
struct tm* localime(const time_t *)
struct tm {
int tm_sec; /* 秒,范围从 0 到 59 */
int tm_min; /* 分,范围从 0 到 59 */
int tm_hour; /* 小时,范围从 0 到 23 */
int tm_mday; /* 一月中的第几天,范围从 1 到 31 */
int tm_mon; /* 月,范围从 0 到 11 */
int tm_year; /* 自 1900 年起的年数 */
int tm_wday; /* 一周中的第几天,范围从 0 到 6 */
int tm_yday; /* 一年中的第几天,范围从 0 到 365 */
int tm_isdst; /* 夏令时 */
};
localtime相对于ctime就是能够方便获取时间分量,就是单个数据
#include<locale.h>
#include<setjmp.h>
第一步: 定义变量 jmp_buf buf;
第二步: 设置 int ret = setjmp(buf);如果正常执行该代码,ret==0
如果是通过longjmp(buf,num)跳跃,ret==num
第三步: 在任何地方执行 longjmp(buf,num) 都会返回 setjmp(buf)处开始执行代码
#include <setjmp.h>
#include <stdio.h>
jmp_buf buf;
int n=1;
void func(void){
printf("func begin\n");
if(n==0){
longjmp(buf,1024);//会跳到setjmp()函数处继续执行
}
}
int main(){
int ret = setjmp(buf);//0 返回的是longjmp(buf,num)中设置的num
printf("main:ret = %d\n",ret);//0
--n;//0
func();
return 0;
}
//输出结果
main:ret = 0
func begin
main:ret = 1024
func begin
#include<signal.h>
signla能完成异步处理,
下面这个程序可以让ctrl c无法结束程序
#include <stdio.h>
#include <signal.h>
void func(int sig){
printf("捕获信号:%d\n",sig);
signal(SIGINT,func);
}
int main(){
//printf("进程号:%d\n",getpid());
signal(SIGINT,func);
while(1);
return 0;
}
序号 宏 & 描述
SIGABRT
程序异常终止。
SIGFPE
算术运算出错,如除数为 0 或溢出。
SIGILL
非法函数映象,如非法指令。
SIGINT
中断信号,如 ctrl-C。
SIGSEGV
非法访问存储器,如访问不存在的内存单元。
SIGTERM
发送给本程序的终止请求信号。
命令行输入 :ps aux|grep a.out 里面会有个有个进程号
命令行输入 :kill -2 进程号
如果发生了意外,程序需要去处理这个意外,(类似单片机的中断,它会保存)
回调函数
signal是捕获到信号就调用函数
#include <stdarg.h>
可变长参数列表
void func(int num,...)
va_list ap;
va_start(ap,num);
va_arg(ap,type)
va_end(ap)
#include<stddef.h>
ptrdiff_t
这是有符号整数类型,它是两个指针相减的结果。
size_t
这是无符号整数类型,它是 sizeof 关键字的结果。
wchar_t
这是一个宽字符常量大小的整数类型。
NULL也定义在这个库中
C语言中的NULL
#define NULL ((void *)0)
0地址是一个特殊地址,不能访问和修改,所以我们在指针中会用NULL来判断指针是否可读写
#include <stdio.h>
#include <stddef.h>
#include <locale.h>
#include <errno.h>
#include <string.h>
int main(){
wchar_t str[20] = L"中国万岁";//wchar_t 等宽字符类型 4Byte utf-8
printf("%u\n",sizeof(wchar_t));//4四个字节,因为是utf-8编码格式
if(setlocale(LC_CTYPE,"")==NULL){//本地化设置
printf("%s\n",strerror(errno));
return -1;
}
printf("%ls\n",str);
return 0;
}
utf-8中文汉字占2~4个字节
gbk中文汉字占3个字节
#include<stdlib.h>
atof(“3.14”);//字符转为浮点
atol atoi 等各有功能
#include <stdio.h>
//printf scanf
//sprintf sscanf
//fprintf fscanf
int main(){
char buf[100] = {};
float n = 3.14159;
sprintf(buf,"%g",n);//格式化输出到字符串
printf("%s\n",buf);
return 0;
}
atexit(func) 回调函数
#include <stdio.h>
#include <stdlib.h>
//回调
void bye(void){
printf("Goodbye!\n");
}
int main(){
printf("main begin\n");
atexit(bye);//signal 参数都有一个函数指针 回调
//注册了一个退出函数, 当进程结束之前自动调用被注册好的函数
//不能修改下面两行代码,要求在输出main end!之后 再输入Goodbye!
printf("main end!\n");
return 0;
}
设置环境变量
env | grep PATH
设置环境变量
sudo vi ~/.bashrc
/PATH
shift+g
PATH=$PATH:.
#include<string.h> memcpy()
#include <stdio.h>
#include <assert.h>
#include <string.h>
//从src处拷贝n个字节的数据到dest中去
void *mymemcpy(void *dest,const void *src,size_t n){
assert(dest != NULL);
assert(src != NULL);
char * pdest = (char *)dest;
const char * psrc = (const char *)src;
size_t i;
for(i=0;i<n;i++){
*pdest = *psrc;
pdest++;
psrc++;
}
return dest;
}
void *mymemmove(void *dest,const void *src,size_t n){
//
char *pdest = (char *)dest;
const char *psrc = (const char *)src;
size_t i;
if(psrc+n > pdest){//从后拷贝
psrc = psrc+n;
pdest = pdest+n;
for(i=0;i<n;i++){
*pdest = *psrc;
--pdest;
--psrc;
}
}else{//从前往后拷贝
for(i=0;i<n;i++){
*pdest = *psrc;
++pdest;
++psrc;
}
}
return dest;
}
int main(){
size_t i;
int arr[5] = {110,120,119,1024,9527};
for(i=0;i<5;i++){
printf("%d ",arr[i]);
}
printf("\n");
//mymemcpy(arr,arr+1,4*4);//但是src数据也发生了变化 16个字节是4个整数,
//这里我们把4个数复制到后面的位置
memmove(arr,arr+1,16);
//mymemcpy(arr+1,arr,4*4);
//memcpy(arr+1,arr,16);
//memmove(arr+1,arr,16);
for(i=0;i<5;i++){
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}
memcpy(arr+1,arr,4*4); //全部110 拷贝四个整数到后面位置
memcpy能够实现内存拷贝,但是不会考虑内存重叠问题(pointer aliasing)
优化版本:
memmove(),就不会有上面那个问题
也有方法src newsrc malloc申请动态内存保存,再复制到dest
但是效率比较低
restrict 关键字
用于告诉编译器,两个指针不能指向一个数据
int i=0;
int *s=5
int *b=6
return *a+*b 可能为12
i++ + ++i 未定义的
其实restrict是写给程序员看的,编译器不能做到什么,主要是让程序员知道这个地方指针不能指向一个数据
#include <stdio.h>
int main(){
int num = 1024;
int * restrict p = #//程序员遵守规则
int *p1 = #//要避免这种情况
*p1 = 119;
printf("%d\n",num);
printf("%d\n",*p);
return 0;
}
混合运算 signed和unsigned进行运算,会转换为unsigned运算
#include<stdio.h>
int main(){
signed int a=-20;
unsigned int b=10;
if((a+b)>0){
printf(">0\n");
}else{
printf("<0\n");
}
return 0;
}
//结果返回>0
C语言思维导图
假设有两个整数m,n
不能使用比较运算符
不能使用三目运算符
不能用if
如何比较大小
int m,n;
scanf(“%d %d”,&m,&n);
如果一正一负,较大值是正数
如果符号相等 res=m-n;
//因为二进制编码1是负数,0是正数
int mf = !((m>>31)&1); //mf=1表示 m为正 0 m为负
int nf = !((n>>31)&1); //nf=1表示 n为正 0 n为负
int notsame = mf^nf; //notsame=1 表示 m和n符号不相同
int same = !notsame; //same=1 表示m和n符号相同
int sub = m-n;//如果一正一负相减 可能溢出
int subf = !((sub>>31)&1); subf=1表示结果为正
int big = notsame*(mf*m + nf*n) + same*(subf*m + (!subf)*n);
一正一负 notsame = 1 same = 0
都为正 都为负: notsame = 0 same = 1
big = subf*m + (!subf)*n;
一正一负:notsame=1,same=0;
big=mf*m + nf*n
两个数相减的时候可能溢出
很大的正数-很小的负数可能会溢出,所以才有notsame*(mf*m + nf*n)部分
------------------------------------------------------------------------------
项目遵循的规则:
升级项目,把字符项目升级为图形界面的项目 相当于重新开发
MVC三层架构模型:
M Model模型层 结构体,类,模型的定义层
结构体定义 作为模型层
V View 视图层 用于和用户进行交互的层次
用户的输入的数据 作为调用 控制层函数的参数
C Control 控制层 逻辑功能实现的层次
控制层实现主要的逻辑功能
通过返回值 或者 传入参数 返回结果给 视图层
程序设计: 经过设计阶段
文件名
设计全局变量的名字类型
设计函数的名字 参数列表,含义,返回值类型 实现的功能
出错时返回的值