- 为什么计算机使用补码存储数据?
- 避免了0的编码有两个的问题
- 符号位和数据位可以一起处理,减法可以通过加法实现
- 判断大小端字节序
int main(void) { int a= 0x12345678; char b = a; if(b == 0x78) { printf("little endian\n"); } else { printf("big endian\n"); } } - extern关键字
//同一个.c文件中 extern int c; //此处变量c不开辟空间 - static关键字
//file1.c static int x = 10; //file2.c extern int x; //错误。无法引用。static将x的生命周期限定在file1.c中 - sizeof() 是运算符。
- 数据类型转换规则:char -> short -> int -> long -> float -> double。
- break关键字用于结束整个循环,在嵌套循环里结束本层的循环。
continue关键字结束本次的循环。 - 短路求值问题:
- 与 -> 第一个条件为false,后面不执行;
- 或 -> 第一个条件为true,后面不执行。
- 数组给另一个数组赋值时最好一个元素一个元素的赋值。(可以直接拷贝)
- 字符串别忘记了结尾的'\0'
- 全局变量默认为0
- 变量命名规则:
- 由字母、数字、下划线组成;
- 不能是关键字,如:for、int等
- 不能以数字开头;
- 区分大小写。
- putchar('a'); //输出不自动换行
- puts("hello");//输出自动换行
- 字符串输入:char a[5];scanf("%s", a);//遇见空格或换行就结束了 gets(a); //可以输入空格,换行结束
- 二维数组可以省略行数,但不能省略列数。
- 使用函数的好处:
- 便于更新和维护;
- 方便阅读;
- 便于分工合作开发。
- 指针:
- 两个指针不能加法;
- 野指针:未指向有效地址的指针,如int *p;
- 空指针: int *p = NULL;
int *p[3] 为整型指针数组 ([]优先级高于*)int (*p)[3] 为数组指针,p指向int[3]类型的指针,通常将二维数组赋值给pint a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; int (*p)[4] = a; int i, j; for(i = 0; i < 3; i++) { for(j = 0; j < 4; j++) { printf("%02d\n", p[i][j]); //p[i][j] <=> *(*(p + i) + j) } } char *s[5] = {"hello", "world", "c", "linux", "jeffrey"}; for(i = 0; i < 5; i++) { printf("%s\n", s[i]); }- gcc编译流程
预处理 -> 编译 -> 汇编 -> 链接- 预处理:头文件展开、注释处理、宏替换
gcc -E -o hello.i hello.c - 编译:将C程序编译成汇编程序,检查语法错误
gcc -S -o hello.s hello.c - 汇编:将C程序编译成机器码源程序
gcc -c -o hello.o hello.c - 链接:将多个.o文件和库文件等链接到一起
gcc -o hello hello.c
- 预处理:头文件展开、注释处理、宏替换
-
Makefile文件编写
EXEC = main OBJS += main.o OBJS += add.o sub.o mul.o div.o CC = gcc AR = ar $(EXEC) : $(OBJS) $CC -o $@ %^ .o : .c $CC -c $< clean: rm $(EXEC) $(OBJS)$@ 目标的完整名称
$^ 所有依赖文件
$< 第一个依赖文件 - 函数指针
int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } int div(int a, int b) { return a / b; } int process(int (*p)(int, int), int a, int b) { return p(a, b); } int main() { printf("sum is %d\n", process(add, 5 , 3)); printf("sub is %d\n", process(sub, 5 , 3)); printf("mul is %d\n", process(mul, 5 , 3)); printf("div is %d\n", process(div, 6 , 3)); int(*p[4])(int, int) = {add, sub, mul, div}; int i = 0; for(i = 0; i < 4; i++) { printf("value is %d\n", p[i](4, 2)); } return 0; } - 函数参数传递:
- 值传递:通常不能改变实参的值;
- 地址传递:可以改变实参的值;
- 指针类型大小
int p; char q;
| 类型 | 32位 | 64位 | |
|---|---|---|---|
| sizeof(p) | int * | 4 | 8 |
| sizeof(*p) | int | 4 | 4 |
| sizeof(q) | char * | 4 | 8 |
| sizeof(*q) | char | 1 | 1 |
- 结构体:将描述同一事物的变量组织到一起
struct student { char name[10]; int number; int age; int score; }; struct student s = {"jeffrey", 1001, 25, 99}; printf("my name is %s, my number is %d, my age is %d, my score is %d\n", s.name, s.number, s.age, s.score); //更改时只能对成员更改,不能整体修改; //错误修改name成员 s.name = "hello"; //正确的修改name 成员 strcpy(s.name, "xiaowang"); //错误的示例 struct student s; s = {"xiaowang", 1001, 26, 98}; //除了初始化时,其他时候不可以整体赋值; //错误的示例 - 内存分配:
- 静态分配:在编译时分配好空间,如int a[10];
- 动态分配:在运行时再确定内存大小,如malloc(100);
动态分配的内存不会自动释放,需要使用free函数释放,如:void *p = malloc(200); if(p) free(p);
动态分配可能会失败,失败会返回NULL,所以使用malloc后记得判空。
- 按位左移,左移一位,结果乘2
- 按位右移,右移一位,结果÷ 2
- static关键字:
static int number; //全局变量,在本文件中有效 static int func() //函数只能在本文件中被访问 { static int x; //变量生命周期延长,默认值为0 } - a.c中有定义全局变量int x,则想要b.c中使用x,需要extern int x;
- const关键字:
const int a = 10; //a为常量,不能修改; int const *p; //修饰*p,*p为常量,不能修改; int * const p; //修饰p, p为常量,不能修改; int const * const p; //修饰*p和p,均为常量,不能修改。 - 内存分为:全局区、栈区、堆区、代码区
#include <stdio.h> int x; //全局区 int main() { int y; //栈区 static int z; //全局区 int *p = malloc(24); //malloc的内存在堆区,p在栈区 char *p = "hello"; //hello在代码区,q在栈区 } - 库文件:
- 动态库:运行时再加载;
- 静态库:在编译时,将静态库打包至可执行文件中。
win中动态库:.dll文件,静态库.lib文件;Linux中动态库.so,静态库.a.
- 动态库和静态库对比:
- 链接时间:动态库在程序运行时被加载;静态库则是在编译时被链接到目标代码中,运行时不在需要该库;
- 执行速度:静态库链接的程序略快于动态库链接的程序;
- 内存大小:动态库更节省内存。
你好啊
你好你好