malloc
和free
维护一个内存池数组
malloc
老是分配一整块内存。根据编译器的实现,实际分配的内存也有可能比请求的稍大一些malloc
先向OS申请新的内存,仍是不够时返回NULL
。所以malloc
返回的指针必须先检查free
的参数必须为经过malloc
等函数申请到的内存指针或NULL
,它也老是释放整个分配到的内存calloc
:返回指针前将整块内存都初始化为0realloc
:bash
realloc
可能返回一块新内存的指针(原先的内容会被复制过去),所以realloc
后必须更新指针NULL
时,与malloc
行为相同⚠️函数指针(这里过重要了吧!!)函数
此处重点在于函数调用符(即函数名后的括号)的优先级比间接访问要高,所以工具
int *foo(); // foo是一个返回int*指针的函数
foo
优先与函数调用符结合,成为一个函数,而后对该函数的值(即返回值)进行间接访问获得int
,可据此推论函数返回值为int*
测试
int (*foo)(); // foo是一个返回int的函数的指针
foo
被迫先与间接访问符结合,也就是说*foo
做为总体与函数调用符结合,即*foo
为函数,则foo
为函数指针命令行
int (*foo[])(); // foo是一个函数指针数组
foo
先与[]
结合,说明foo
是一个数组名,接着foo[]
与*
结合,说明foo
中的元素为某种指针,最后*foo[]
与函数调用符()
结合,并参照类型声明为int
,说明元素为返回int
值的函数指针(晕了。。)debug
函数名在表达式中的角色与数组有些相似,也是以相似指针常量的形式出现的,所以指针
void f(int arg); // f为接收1个int参数,无返回值的函数 void (*fPtr)(int arg) = f; // 函数名能够做为指针被直接复制给函数指针 void (*fPtr)(int arg) = &f; // 也能够先取址再赋值给函数指针
事实上,&
只是显式地执行了编译器将隐式执行的工做code
命令行参数递归
argc
:参数个数(即argv
数组的长度)argv
:字符串指针数组(命令行中空格分隔的每一截都为一个参数),一般经过检查字符串开头是否为-
来识别选项参数字符串常量自己其实就是一个常量指针
char *strPtr = "abcdefg"; "abcdefg"[2] == 'c'; // true "abcdefg" + 2 == strPtr + 2; // true,所以它也能够做为一个字符串指针在printf里直接用%s输出
预约义符号(不是预处理指令!)
__FILE__
:%d
源代码名(test.c
)__LINE__
:%d
文件当前行号(25
)__DATE__
:%s
文件被编译的日期(Jan 31 1997
)__TIME__
:%s
文件被编译的时间(13:07:02
)__STDC__
:%d
编译器是否遵循ANSI C标准(1
/ 0
)⚠️带参数宏
#define macro(arg1, arg2) stuff // 参数列表的左括号必须紧邻宏名,不然就会被认为是stuff的一部分
(之前对宏有误解,觉得是单纯的把一段文本替换成另外一段文本,而实际上宏是先被按照符号解析再转换为文本进行替换的)
#define
定义能够包含其余#define
定义的符号,但宏不能够递归(大概是由于没有调用栈这种东西)利用printf
会将直接相邻的字符串链接起来的特性,能够实现简单的输出包装:
#define PRINT(FORMAT, VALUE) printf("This value is "FORMAT".\n", VALUE) // 注意没有分号! PRINT("%d", x + 4); // 则FORMAT为"%d",VALUE为x + 4 // 实际被转换为: printf("This value is ""%d"".\n", x + 4); // 三个字符串被自动链接
⚠️符号链接符##
:产生的新符号必须合法,不然就是undefined行为
#define ADD_TO_SUM(sum_number, value) sum##sum_number += value // 注意没有分号! ADD_TO_SUM(5, 23); // 5将做为sum_number的值被链接到sum,产生sum5这个符号,最后整个宏被替换为sum5 += 23; // 宏被引用时上下文中必须存在名为sum5的变量
(这就是为何sizeof不是一个函数吧,由于类型自己不能做为参数传递,以及可变参数列表也是用宏来实现)
带反作用的宏参数可能致使严重错误,好比:
++
/ --
:改变参数自己getchar()
:消耗I/O buffer命令行定义:
-D{name}
/ -D{name}=stuff
=== #define name stuff
(未指定时stuff默认为1)-U{name}
=== #undef name
条件编译:#if
/ #endif
/ #elif
/ #else
测试符号是否已被定义:
#ifdef symbol
=== #if defined(symbol)
#ifndef symbol
=== #if !defined(symbol)
⚠️能够结合命令行定义来完成条件编译
源代码test.c
:
#ifndef CHOICE // 以避免与命令行定义冲突 #define CHOICE 5 #endif int main() { printf("Choice: %d\n", CHOICE); return 0; }
直接编译执行:
> gcc test.c -o test > ./test > Choice: 5
编译时使用命令行定义:
> gcc -DCHOICE=2 test.c -o test # 提早定义CHOICE为5 > ./test > Choice: 2
#endif
后用注释标注它结束的是哪个条件以提升程序可读性⚠️使用条件编译来避免因为头文件被重复包含致使的符号冲突
#ifndef _HEADERNAME_H #define _HEADERNAME_H 1 // 若是没有1则_HEADERNAME_H被定义为空字符串,但仍然被定义,不会影响判断结果 #endif
#progma
指令是不可移植的,不一样平台的预处理器可能会彻底忽略它,或以不一样的方式执行它