C 语言复杂知识点

编译与宏

# 开头为预处理命令,包括 #include 引入文件 #define 宏,在预处理阶段处理

函数声明与语法检测在编译期检查,函数定义在链接时检测

__attribute__(constructor)) 有该宏上标的函数先于 main 执行

#include <stdbool.h> 使得 c 支持 bool 类型

extern 引用其他文件已定义过的全局变量

__typeof() 宏获得变量类型,常用于定义宏

#define swap(a, b) {\
	__typeof(a) __temp = a;\
	a = b; b = __temp;\
}
// 或者 a ^= b; b ^= a; a ^= b;

// 根据变量类型,获得指定输出
#define Type(a) _Generic((a), \
    int : "%d",\
    double : "%lf",\
    float : "%f",\
    long long : "%ld",\
    const char * : "%s",\
    char * : "%s"\
)

EOF 宏常量为 -1, NULL 为 0

变参函数引入 stdarg.h

#include <stdarg.h>
int max(int num, ...) {
	va_list arg; // 声明参数
	va_start(arg, num); // 使得 arg 指向起始参数
    while (num--) { // num 帮助判断参数个数
        int temp = va_arg(arg, int); // 开始获得改类型的参数
    }
    va_end(arg); // 释放 va_list
}
// 可通过 myprintf(const char *frm, ...) 自己实现打印函数
// 防止多次引入头文件宏重复声明
#ifndef _LOG_H
#define _LOG_H

// 通过头文件编译打印技巧
#ifdef DEBUG
#define log(frm, args...) { \
    printf("[%s : %s : %d]", __FILE__, __func__, __LINE__); \
    printf(frm, ##args); \
    printf("\n"); \
}
#else
#define log(frm, args...)
#endif
#define contact(a, b) a##b // 拼接合并 一个#变成字符常量
#endif

工程开发规范,.h 头文件放函数声明,.c 文件放函数定义,头文件和源文件分离,可以解决函数定义引用函数找不到的问题

工程项目目录编排:

  • src 源码目录
  • lib 外部库目录 可用静态库文件 lib*.a
  • include 头文件 .h
  • bin 可执行文件
  • build 构建过程中间文件(可选)

makefile 文件

.PHONY: clean // 虚拟 clean 文件,不检查
all: lib/lib1.a test.o //  依赖 
	gcc test.o -L./lib -llib1 -DDEBUG // 指定库 开启宏声明
test.o: test.cpp
	gcc -I./include -c test.cpp // -c 不链接
clean:
	rm -rf a.out test.o // 清理

语言特性与库

c 语言中的 switch 只能判断整型值变量

math.h 中返回和传参都是高精度

需要了解的一个最大公约数函数

int gcd(int a, int b) {
	return b == 0 ? a : gcd(b, a % b);
}

printf scanf 返回值为读入长度

对于 scanf 函数,我们可以通过 scanf("%[^\n]s", str) 来跳过换行输入等

sprintf 以指定格式打印到字符串,sscanf 从字符串中读出类型变量

c 语言中自带 stdin stdout stderr 文件符,对文件进行操作

FILE *fout = fopen("output", "w");
fprintf(stdout, "stdout = %s\n", str);
fprintf(fout, "fout = %s\n", str); // 写入文件
fclose(fout); // 注意关闭文件

使用随机数,引入 time.h

#include <time.h>

srand(time(0));
int val = rand() % 100; // 0 - 100 的随机数

使用动态内存函数,引入 stdlib.h

函数指针也是参数,可进行传参调用指定方法函数

double newton(double (*F)(double, double), double(*f)(double), double n);
// main 传入参数个数,参数数组,环境变量
int argc, char *argv[], char **env

指针、堆栈空间、结构体

const int *x 指针常量 int * const x 常量指针

函数内声明的变量在栈空间中,所以需要 malloc 指针等帮助下在堆空间创建,传出去,栈空间内的变量会被销毁

结构体成员描述关系会影响结构体构造的函数大小,比如 char 可能跟内存对齐 4 个字节 或 8 字节 (看系统)

自定义宏,查看结构体成员偏移

#define offset(T, a) (long) (&(((T *) (NULL))->a))

64 位系统指针变量大小是 8 字节,32 位系统指针变量大小为 4 字节

指针根据声明类型去跳位,p++ char 类型跳 8 位,int 类型跳 32 位

float 占 4 字节,double 占 8 字节,long long 占 8 字节

对于函数指针,我们可以通过 typedef 函数提升至类型,方便调用

内存泄漏,声明一块内存后,对其访问丢失,没有释放

函数系统栈空间不超过 8MB,函数结束时释放

在代码中出现的所有同样的字符串,都是字符串常量,指向同一静态区

对于常量,全局变量,静态变量(即使在函数内)都存在静态区,程序结束释放

static 在 c 语言的作用主要是隐藏,不让在文件外导出;唯一一次初始化(在程序启动时,函数中不会初始化它),保持变量的持久;默认设置为 0

申请内存函数申请的都是虚拟内存,在虚地址上

malloc 申请后,在实际访问情况下时若内存不够才尝试缺页异常;不会对原内存内容进行修改

calloc 在申请内存后,立刻把原内存中的内容置 0 了,进行了访问

realloc 在当前指针上,基于原内存地址扩充

共用体中所有元素公用一个空间,一般机器都是大端模式

union IP {
    struct {
        unsigned char a1;
        unsigned char a2;
        unsigned char a3;
        unsigned char a4;
    } ip;
    unsigned int num;
};
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页