c语言基础
1,void func1(void);普通函数声明,任意参数无返回值,需要实现函数体才能使用。
2,void *func2(void);普通函数声明,任意参数返回任意函数指针,需要实现函数体才能使用。
3,void (*func3)(void);函数指针声明,函数任意参数无返回值,属于变量声明,可以赋值。
4,void (*func4(void))(void);函数指针声明,属于一个返回函数指针的函数指针,本体函数指针任意参数,返回的函数指针任意参数无返回值
5,fork只会复制当前进程的调用fork的线程到子进程中。
6,c/c++可以看到0x0001u的用法,这个u表示unsigned,还有其他的一些修饰数字的前后缀,比如01中0表示8进制,1L中L表示long int , 1u中u表示unsigned int。
7,c语言中变量定义和赋值中逗号和分号的应用:
结构体定义:内部用分号,结尾用分号。
枚举定义:内部逗号,结尾分号。
c++类定义:内部分号,结尾分号。
8,数组指针的部分误解:
如果有int a[10];,那么a和&a表示相同的地址,但是代表不同的含义,a表示数组的首地址,&a表示数组大小为10的数组的地址。
也就是a+1表示a[1]的地址,但是&a+1表示a[10]的地址
9,当表达式中有有符号数据和无符号数据时,一律将有符号转化为无符号。
这一点可能和向上转换原则不太一样,一般情况是小范围转大范围,但是有符号和无符号其实范围是一样的
10,结构体补齐规则
以前总是按照那3个准则来记忆,类似于:
对齐原则:每一成员的结束偏移量需对齐为后一成员类型的倍数
补齐原则:最终大小补齐为成员中最大值的倍数
有种方法更容易记忆。结构体成员以最大成员补齐,如果下个成员能填充到上个成员为对齐而空出的内容中的话,则这个成员会挤到上个成员会移到上个成员后的空位中
typedef struct A{
int a; //aaaa ***b cccccccc //如果没有b则a后仍空出4字节,b发现可以b的大小小于到4字节就会挤到空位中
char b;
double c;
}A; //16byte
typedef struct B{
char b;//b*** aaaa cccccccc
int a;
double c;
}B;//16byte
typedef struct C{
int a;//aaaa**** cccccccc b*******
double c;
char b;
}C;//24byte
typedef struct D{
char b;//bbbbbbbb cccccccc aaaa****
double c;
int a;
}D;//24byte
typedef struct E{
char b;//b*** aaaa c***
int a;
char c;
}E;//12byte
11,thread默认是非分离的,也就是如果thread_create后不进行thread_join,线程资源是不会释放的,一般可以手动设置detach,接下来线程结束后会自动释放,或者不设置detach,调用thread_join等待线程结束并释放资源。
12、头文件分类
stdlib.h:
getenv();atoi();abort();exit();free();malloc();
unistd.h:
getcwd();getpid();access();read();write();
ISO C标准定义的头文件(24项)
<assert.h> ---------------------- 验证程序断言
<complex.h> ---------------------- 支持复数算术运算
<ctype.h> ---------------------- 字符类型
<errno.h> ---------------------- 出错码
<fenv.h> ---------------------- 浮点环境
<float.h> ---------------------- 浮点常量
<inttypes.h> ---------------------- 整型格式转换
<iso646.h> ---------------------- 替代关系操作符宏
<limits.h> ---------------------- 实现常量
<locale.h> ---------------------- 局部类别
<math.h> ---------------------- 数学常量
<setjmp.h> ---------------------- 非局部goto
<signal.h> ---------------------- 信号
<stdarg.h> ---------------------- 可变参数表
<stdbool.h> ---------------------- 布尔类型和值
<stddef.h> ---------------------- 标准定义
<stdint.h> ---------------------- 整型
<stdio.h> ---------------------- 标准I/O库
<stdlib.h> ---------------------- 实用程序库函数
<string.h> ---------------------- 字符串操作
<tgmath.h> ---------------------- 通用类型数学宏
<time.h> ---------------------- 时间和日期
<wchar.h> ---------------------- 扩展的多字节和宽字符支持
<wctype.h> ---------------------- 宽字符分类和映射支持
POSIX标准定义的必须的头文件(26项)
<dirent.h> ---------------------- 目录项
<fcntl.h> ---------------------- 文件控制
<fnmatch.h> ---------------------- 文件名匹配类型
<glob.h> ---------------------- 路径名模式匹配类型
<grp.h> ---------------------- 组文件
<netdb.h> ---------------------- 网络数据库操作
<pwd.h> ---------------------- 口令文件
<regex.h> ---------------------- 正则表达式
<tar.h> ---------------------- tar归档值
<termios.h> ---------------------- 终端I/O
<unistd.h> ---------------------- 符号常量
<utime.h> ---------------------- 文件时间
<wordexp.h> ---------------------- 字扩展类型
<arpa/inet.h> ---------------------- Internet定义
<net/if..h> ---------------------- 套接字本地接口
<netinet/in.h> ---------------------- Internet地址族
<netinet/tcp.h>---------------------- 传输控制协议定义
<sys/mman.h>---------------------- 内存管理声明
<sys/select.h>---------------------- select函数
<sys/socket.h>---------------------- 套接字接口
<sys/stat.h> ---------------------- 文件状态
<sys/times.h> ---------------------- 进程时间
<sys/types.h> ---------------------- 基本系统数据类型
<sys/un.h> ---------------------- UNIX域套接字定义
<sys/utsname.h>----------------------系统名
<sys/wait.h> ---------------------- 进程控制
POSIX标准定义的XSI扩展头文件(26项)
<cpio.h> ---------------------- cpio归档值
<dlfcn.h> ---------------------- 动态链接
<fmtmsg.h> ---------------------- 消息显示结构
<ftw.h> ---------------------- 文件树漫游
<iconv.h> ---------------------- 代码集转换实用程序
<langinfo.h> ---------------------- 语言信息常量
<libgen.h> ---------------------- 模式匹配函数定义
<monetary.h> ---------------------- 货币类型
<ndbm.h> ---------------------- 数据库操作
<nl_types.h> ---------------------- 消息类别
<poll.h> ---------------------- 轮询函数
<search.h> ---------------------- 搜索表
<strings.h> ---------------------- 字符串操作
<syslog.h> ---------------------- 系统出错日志记录
<ucontext.h> ---------------------- 用户上下文
<ulimit.h> ---------------------- 用户限制
<utmpx.h> ---------------------- 用户帐户数据库
<sys/ipc.h> ---------------------- IPC
<sys/msg.h> ---------------------- 消息队列
<sys/resource.h>------------------- 资源操作
<sys/sem.h> ---------------------- 信号量
<sys/shm.h> ---------------------- 共享存储
<sys/statvfs.h>---------------------- 文件系统信息
<sys/time.h> ---------------------- 时间类型
<sys/timeb.h> ---------------------- 附加的日期和时间定义
<sys/uio.h> ---------------------- 矢量I/O操作
POSIX标准定义的可选头文件(8项)
<aio.h> ---------------------- 异步I/O
<mqueue.h> ---------------------- 消息队列
<pthread.h> ---------------------- 线程
<sched.h> ---------------------- 执行调度
<semaphore.h>--------------------- 信号量
<spawn.h> ---------------------- 实时spawn接口
<stropts.h> ---------------------- XSI STREAMS接口
<trace.h> ---------------------- 时间跟踪
What is glibc?
The GNU C Library project provides the core libraries for the GNU system and GNU/Linux systems, as well as many other systems that use Linux as the kernel. These libraries provide critical APIs including ISO C11, POSIX.1-2008, BSD, OS-specific APIs and more. These APIs include such foundational facilities as open, read, write, malloc, printf, getaddrinfo, dlopen, pthread_create, crypt, login, exit and more.
The GNU C Library is designed to be a backwards compatible, portable, and high performance ISO C library. It aims to follow all relevant standards including ISO C11, POSIX.1-2008, and IEEE 754-2008.
The project was started circa 1988 and is more than 30 years old. You can see the complete project release history on the wiki.
Despite the project's age there is still a lot to do so please Get Started and Get Involved!
13、无符号类型和有符号类型进行算术运算和比较运算时,有符号转换为无符号
int aa = -1;
unsigned int bb = 16;
if (aa > bb)
cout << "负数竟然大于正数了!\n";//实际aa转为无符号,大于bb
但是在算术运算时,无符号和有符号相加,结果一般也不会出什么问题,因为都是补码运算
unsigned int b = 1;
int c = -1;
printf("%d\n",c + b);//结果为0
14,16进制数据可以正数可以负数,
int a = -0x87654321;//error C4146: 一元负运算符应用于无符号类型,结果仍为无符号类型。原因是编译期首先将0x87654321赋予int,然后看到0x87654321大于INT_MAX,所以将0x87654321赋予unsigned int,然后看到前面有个负号,不能再讲unsigned转为有符号,所以报错
但是使用%x输出时会输出其补码(其实就是有符号转无符号的值),原因是%x默认是输出无符号16进制数据,%x和%0x意义一样,但是和%05x意义不一样。%x默认接受数据是unsigned int,先把数据转换位unsigned int再输出。
int a = -0x01;
printf("%x,%d\n", a,a);// ;ffffffff,-1
可以看到0x87654321虽然超过了MAX_INT,但是使用补码可以存下来,输出的时候用%x可以原封不动输出
int a = 0x87654321;
printf("%x,%d\n", a,a);// ;87654321,-2023406815
15、c语言符号
~表示按位取反
!表示逻辑取反
^表示按位异或,异或表示不同为1
16,printf家族:
printf表示格式化输入到标准输出中。
sprintf表示格式化输入到字符串中。
vprintf表示使用可变参数输入到标准输出。
vsprintf表示使用可变参数输入到字符串中。
vsnprintf表示使用可变参数输入到限制最大长度的字符串中。
17,__attribute__关键字用法和含义
__attribute__主要用于补充函数和变量的属性,比如限制函数参数类型,限制变量宽度等等,下面给出三个例子:
1,
extern int my_printf(void *my_object, const char *my_format, ...) __attribute__((format (printf, 2, 3)));
//摘取printf的定义为 int printf(const char *format, ...)
//archetype=printf:按照printf格式进行检查my_printf
//string-index=2:my_printf的第2个参数(my_format)对应了printf参数中的format
//first-to-check=3:my_printf的第3个参数(...)对应了printf参数中的...
2,
int y __attribute__((aligned (16))) = 0;
char z[15] __attribute__((aligned (16)));
//aligned应用于基础类型变量和数组时
//编译器对于基础类型变量和数组的编译原则如下:
//系统默认按照变量类型本身占用内存的字节数来对齐边界(地址原则)
//系统按照变量类型本身占用内存来分配内存字节数(内存原则)
//通俗来讲,针对于基础类型变量和数组时,属性限定了变量存储的内存首地址,但是并没有影响变量实际占用
//内存的大小。
3,
typedef struct F1{
char i;
int j[2] __attribute__((packed));
}F1_T;
//9btyes
typedef struct F2{
char i;
int j[2] __attribute__((packed));
int k;
}F2_T;
//16btyes
//系统应该为结构体成员按照最小的对齐方式来对齐边界(1bit或者1字节)
18,mmap使用方法
mmap主要作用是可以将外设硬件/flash等地址空间映射到当前进程的虚拟地址空间,从而可以通过内存操作来控制外设硬件/flash,常常用于外设驱动当中。
下面的例子是通过将一个文件映射到当前虚拟地址中,从而可以通过控制一块地址空间实现文件的读写。
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main()
{
int fd = open("./file.txt", O_RDWR);//读写权限
if(fd <= 0)
{
perror("\n");
return -1;
}
int len = lseek(fd, 0, SEEK_END);//读取文件大小
void* addr = mmap(NULL, len, PROT_WRITE, MAP_SHARED, fd, 0);//只有文件大小不为空时才能创建成功
if(addr == MAP_FAILED)
{
perror("mmap failed\n");
return -1;
}
close(fd);//内存映射成功后关闭文件描述符依然保持着映射
const char* str = "hello, world";
memcpy(addr, str, strlen(str));
return 0;
}
此例子使用有以下点需要注意:
①:映射建立后可以关闭文件描述符
②:文件大小必须要大于0,并且文件大小映射后不能再改变
$ gcc test_mmap.c -o test_mmap
$ touch file.txt
$ echo "test123" > file.txt
$ ./test_mmap
$ cat file.txt
$ hello wo
19,补码编码深入理解
原码到补码是一种映射
[-2^(n-1),2^(n-1)-1] 映射到------> [0,2^n]
正数的补码是原码,所以其实就是负数进行了映射[-2^(n-1),-1] -----> [2^(n-1),2^n-1],很容易看出映射公式:
补码=2^n+负数原码 (原码是负数)
补码=正数原码 (原码是正数)
根据这个映射原理,我们不再需要进行原码-反码-补码计算,而是进行原码-补码计算
例如:
1:求int a=-1的补码
补码=2^32-1=0xffffffff
2,int a=-1,求printf("%x",a);
%x默认输出无符号16进制,需要把有符号转化位无符号,我们知道计算机是以补码保存,所以其实就是原码转补码(原码是负数),再补码转原码(原码是正数,原码等于补码),所以最后求补码即可,补码2^32-1=0xffffffff
20,结构体位域
结构体位域表示使用此类型的多少位,如果相邻相同类型使用位数能挤进来,则挤进来。
struct test
{
int a:1;
int b:1;
int c:1;
int d;
};
sizeof(struct test) = 4+4=8
细节是:
abc0,0000,0000,0000,0000,0000,0000,0000
d
前面三个成员使用4个字节的前三位,后面的d使用单独的4个字节
20,scanf格式化输入
格式化输入多个内容的时候,每个格式化输入内容前可以有无数个空格和换行
例如:
int a,b;
sscanf("123\n\n\n\n456","%d%d",a,b);//找到第一个非空字符,为1,匹配到123,赋值给a,接下来找非空字符,为4,因此将456赋值给b
printf("%d,%d\n",a,b);//a=123,b=456
21,fgets遇到换行和文件尾结束,strtok会修改原字符串