写一个C程序first.c
1,预处理 gcc -E first.c -o first.i -->first.
2,编译 gcc -c first.i-->first.o
3,连接 gcc first.o-o first -->first
gccfirst.c
sizeof()括号内的运算式不进行运算,只是推断一下大小
优先级:
1,()最高 =最低
2,单目运算符比双目运算符高
赋值运算符,和单目运算符是右结合的
其他的基本为左结合
与或非
1 && X = X
1 || X = 1
异或
1 ^ X = -X
0 ^ X = X
按位取反:
先加1,符号反转
两个数参与运算
1,小范围的数转成大范围的数
2,最小的计算单位是int
switch语句:
case后面一定要为常量表达式,而且不能重复
函数:
void fun() {}
当传参数时,可以随意传入参数,只不过没意义
如果不想函数里面传递参数,可以规定:void fun(void){}
调用函数之前,函数必须先声明或先定义,否则,C处理成隐式声明,返回int
尽量不要使用隐式声明
隐式声明, int函数名(根据实参推)
递归
1,退出
2,递归调用向已知靠近
3,效率低,内存消耗大
4,适合解决特别复杂的问题(这个复杂问题可以简化成若干简单问题)
全局变量如果没有初始化,会自动初始化为0,作用域是整个程序,可以在任何地方访问,前面没有默认的auto修师符,也不能加auto
指针:
int* p1, p2;是定义了一个int指针和一个int变量
注意:
a[i] ==*(a+i)
*(a+i) ==*(i+a)
*(i+a) ==i[a]
所以a[i] ==i[a]
指针和数组区别:
1,sizeof结果不同
2,数组名不可更改(数组a[],指针p++p可以,而++a错误)
其他数组和指针一样,可将数组名当指针用
数组名本身:
本身是常量,不可改变,sizeof可以获取整个这片内存的长度
自定义指针指向一片数组区域:
指针是变量,可以改变,sizeof结果为4
char*fa() {
//函数中返回字符串常量的地址是可以的(在代码区)
return"abcdef";
}
char*fb() {
//static是在全局区,所以返回地址是可以的
static int x = 10;
return &x;
}
int* fc(){
//这是不可以的
int x = 10;
return &x;
}
char*fd() {
//这也不可以(在栈区分配的局部变量)
char str[] = "abcdef";
return str;
}
进程内存空间:
代码区 : 程序代码,只读区,不可改
全局区 : 全局变量
堆 : 程序员自己申请
栈 : 局部变量,函数形参...
main函数:
int main(void)
int main()
int main(int argc, char* argv[])
int maim(int argc, char** argv)
int main(int argc, char** argv, char** env)
宏
#define
宏函数:#define 标识符(...) ...
注意:标识符和(之间不能有空格
宏函数:
1,所有参数应用()将其包含,否则使用表达式时可能错误
2,在调用宏函数时,不要使用++,--,否则不能保证结果正确
优点:
1,程序可能会稍微快一点
2,宏更"通用"
3,宏函数中的参数不检查类型
缺点:
1,编译后的代码通常会变大
2,宏参数没有类型检查
3,无法用一个指针指向宏
4,宏可能会不止一次地计算它的参数(n= MAX(i++, j);)
5,可读性差,错误很难查出
"#"运算符
只能出现在宏函数中,作用是将参数字符串化
#define PRINT_INT() printf(#n
"##"运算符
将两个标记连成一个标记,一般情况下,两个标记中的一个是参数
预定义宏
__LINE__ 源程序行号
__FILE__ 源程序文件名
__DATE__ 现在日期
__TIME__ 现在时间
__STDC__ 是否支持标准C,支持为1,不支持为0
注意:
在全局变量前面加上static是为了限制其范围,只能在本文件内访问此全局变量
在函数前面加static也是为了限制其访问权限,只能在本文件内访问此函数
给类型起别名的方法:
1,先用这种类型定义一个变量
2,在最前面加typedef
那个变量名就是类型的别名
Example: typedef int I10[10];
而不是 typedef int[10] I10;
预处理器 将///* */ 去掉
编译器将#.... 去掉
全局变量
extern 类型 变量名
静态全局变量
限制变量的访问
静态全局函数
限制函数的调用
结构体
p-> 相当于 (*p).
对齐,补齐
由于内存分配会将结构中的变量分配到内存的边界上,以方便访问。
所以每个成员放的位置是从本身长度的倍数位开始放。
但本身长度超过4时,以4计。此称为对齐。
char 1倍
short 2倍
int 4倍
double4倍
整个结构变量的长度要保持内部最长成员(超过4以4计)的倍数。
如果不够,则补齐。
位段
本质是结构
struct Switch {
int s1 : 1 //1代表一个二进制位
int s2 : 1
int s3 : 1
int s4 : 1
int s5 : 1
};
函数指针
函数名其实是一个常量指针
为什么用函数指针
函数指针的应用比较广泛,函数指针是变量,
所以变量能出现的地方,函数指针就可以出现
比如,可以用数组保存很多函数,
可以把函数作为另外一个函数的参数进行传递,用一个函数返回一个函数
静态分配 :编译期间可以确定需要分配内存的大小
int x; : 在栈分配
动态分配 :编译期间不能确定所需要分配内存的大小,
只有程序运行时才能确定分配空间的大小.
在堆空间中分配,堆空间内存分配比较自由,
分配空间和释放空间由程序员自己确定,堆区也叫自由区
malloc:
分配多少空间后在之后的一个空间位置设置一个标记,当free时从开始位置释放空间,直到遇到此标记时结束,因此如果越界访问可能会破坏此标记,引发内存错误,也可能产生段错误(超过页面大小)
malloc和calloc的功能相同,可以通用,唯一的区别就是calloc会将分配好的内存清零
mallco和calloc分配空间的首地址一定要保存下来,否则在free时会出现内存错误
realloc :函数调整分配后的大小
1,当扩展内存时,不会对增加的内存初始化
2,失败时返回空指针,原内存中的数据不会改变
3,第一个参数是空指针时,如同调用malloc
4,如果以0作为第二个参数,会释放原内存块
5,小心,realloc可能会将内存块移到别的地方去。
free:
1,参数需要释放空间的首地址,确定堆空间的结束标记没有被破坏
2,两次释放同一片空间会造成内存错误(double free)
3,当free掉一片空间后,应该立即把指向该空间的指针置零
4,谁申请的空间谁释放,否则可能造成悬空指针
数组指针
int n[10];
数组a的值是第一个元素的地址(下标0)的地址
a的类型int[10]实质上是int* const类型
所以*a 等同 a[0]
如果&a,则取到整个数组的首地址
与a[0]地址相同,但意义不同,类型为int (*pa)[10] = &a
inta[3][4],a是一个数组,数组里有3个元素,每个元素是int[4]类型的数组
a是第一个元素的地址,a的类型就是int[3][4],int(*)[4]
int(*pa)[4] = a;
stdout 和stderr的区别
1,stdout带缓冲区,stderr不带缓冲区
2,stdout可以重定向,stderr不能重定向
3,什么时候stdout缓冲区会清空:
1)缓冲区清空时,缓冲区的内容会送到显示器
2)当输出换行时会清空
3)当等待用户输入时会清空
4)当调用fflush函数时清空
5)当程序运行结束时清空
6)当缓冲区满时清空
sprintf:
可以将任何数据转成指定格式的字符串
va_list:处理步骤固定,如下
va_listva; //1,声明一个va_list类型的变量,用来保存所有可变长参数
va_start(va,n); //2,将所有的参数保存到va_list里
va_arg(va,i); //3,从va_list中逐个取出参数进行处理
va_end(va); //4,释放va_list