1、HelloWorld
<!-- 导包 -->
#include <stdio.h>
<!-- main函数,argc:输入的参数个数,argv:字符串数组,每一项都代表一个具体参数 -->
int main(int argc, char *argv[]){
// 打印
printf("Hello World!\n");
return 0;
}
2、VIM编译运行C程序
<!-- 第一步:新建一个.c文件 -->
vi helloworld.c
<!-- 第二步:编译.c文件(mac下使用的是clang编译器,linux下使用的是gcc编译器) -->
-g:输出debug信息,-o:输出可执行程序
clang -g -o helloworld helloworld.c
<!-- 第三步:编译成功后运行 -->
./helloworld
3、C语言中常用基本类型
类型 | 说明 |
---|---|
整型:short、int、long | 16位2字节、32位4字节、32位4字节 |
浮点型:float、double | 32位4字节、32位4字节 |
字符型:char | 8位1字节 |
无符号型:void | 一般情况下不使用,指针的时候才会使用 |
- 示例代码
#include <stdio.h>
int main(int argc, char *argv[]) {
int a = 100;
float b = 5.98;
char c = 'Y';
printf("a=%d\n", a);
printf("b=%f\n", a);
printf("c=%c\n", a);
return 0;
}
4、常量与变量
- 变量:内存中分配一小段空间,这一小段空间可以根据我们需要随时发生变化。
- 常量:内存中分配一小段空间,这一小段空间一旦赋值之后就不能再改变。
- int a = 0; //变量,可以再赋值
- const int len = 256; //常量定义
- 内存管理:
![c语言内容管理](/Library/AndroidWork/work_notes/resource/c语言内容管理.jpg)
5、指针与数组
- 指针:就是内存地址:void*、char*
- 数组:char c[2]、int arr[10]
- 示例代码:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int *a, *b;
a = (int *) malloc(sizeof(int));
b = (int *) malloc(sizeof(int));
*a = 1;
*b = 2;
int c[3] = {1, 2, 3};
printf("addr of a:%p, %p, %d\n", &a, a, *a);
printf("addr of b:%p, %p, %d\n", &b, b, *b);
printf("addr of c:%p, %p, %d, %d, %d\n", &c, c, c[0], c[1], c[2]);
return 0;;
}
6、结构体
struct st{
int a; //成员 a
int b; //成员 b
};
- 示例代码:
#include <stdio.h>
struct st {
int a;
int b;
};
int main(int argc, char *argv[]) {
struct st sst1;
struct st sst2;
sst1.a = 10;
sst1.b = 20;
sst2.a = 30;
sst2.b = 40;
printf("struct content is :%d, %d\n", sst1.a, sst1.b);
printf("struct content is :%d, %d\n", sst2.a, sst2.b);
return 0;
}
7、枚举类型
enum em{
red_color = 0,
green_color,
black_color
};
- 示例代码:
#include <stdio.h>
enum e_type {
red = 0,
greed,
blue
};
int main(int argc, char *argv[]) {
enum e_type et;
et = red;
printf("the color is %d\n", et);
et = blue;
printf("the color is %d\n", et);
return 0;
}
8、算术运算与比较运算
- 算术运算:【+、-、*、/、%】
- 比较运算:【>、==、<、>=、<=、!=】
- if/else 语句
- for 语句
- while 语句
9、函数
void func(int a){
...
}
代码示例:
#include <stdio.h>
int sum(int a, int b) {
return a + b;
}
int main(int argc, char *argv[]) {
int result = sum(3, 5);
printf("3+5=%d\n", result);
return 0;
}
10、文件操作
- 文件类型:FILE* file;
- 打开文件:FILE* fopen(path, mode);
- 关闭文件:fclose(FILE*);
#include <stdio.h>
int main(int argc, char *argv[]) {
FILE *file;
char buf[1024] = {0,};
file = fopen("1.text", "a+");
fwrite("Hello, World!", 1, 13, file);
// 将角标放到文件的开头
rewind(file);
// 将文件内容角标为1至26的内容读取到buf中
fread(buf, 1, 26, file);
fclose(file);
printf("buf:%s\n", buf);
return 0;
}
11、C语言中的指针
-
指针的物理意义?
- 它就是内存中的一个地址
- 指针本身运算
- 指针所指内容的操作
-
操作系统是如何管理内存的?
- 栈空间:每进入一个函数的时候,操作系统会帮我们分配一小段空间,当我们退出函数的时候,操作控件又会帮我们回收这一小段空间。(一般情况下大小为4M到8M左右)
- 堆空间:一旦你在某个函数分配了堆的空间之后,只要你知道它的地址,你其它函数中依然可以访问它;并且堆空间的内存分配和释放由你自己来操作。(一般情况下大小为几个G)
- 内存映射: 磁盘用的文件可以映射到内存里面,映射之后我们只要对内存中的文件进行修改,磁盘中的文件也会相应发生改变,从而大大提高我们访问IO的操作。(常用于数据库)
-
内存的分配与释放?
- 分配内存:void* mem = malloc(size);
- 释放内存:free(mem);
- 注意:上面两者都是在堆空间中去操作的。
-
内存泄漏?
- 不断的想系统申请内存。
- 申请的内存不同,也不释放。
- 避免:你申请了堆内存,一定要记得释放。
-
野指针?
- 占用别人的内存称为野指针。通俗来讲:这个指针已经被释放掉,被释放掉之后别人就会去申请内存,又将它拿到别人去使用,这个时候你又去拿这个指针去访问,其实这个指针已经是野指针了,不受你控制,是属于别人的了,而你还拿到这个指针去访问,这个时候就会导致程序carsh。
-
函数指针?
- 返回值类型 (*指针变量名)([形参列表]);
int func(int x);/* 声明一个函数 */ int (*f)(int x);/* 声明一个函数指针 */ f = func;/* 将func函数的首地址赋值给指针f */
- 示例代码:
#include <stdio.h> sum(int a, int b) { return a + b; } sub(int a, int b) { return a - b; } int main(int argc, char *argv[]) { int sumResult; int subResult; int (*f)(int, int); f = sum; sumResult = sum(3, 5); f = sub; subResult = sub(sumResult, 5); printf("3+5=%d\n", sumResult); printf("8-5=%d\n", subResult); return 0; }
12、编译器
- gcc/clang -g -O2 -o test test.c -I… -L… -l
- -g:输出文件中的调试信息
- -O:对输出文件做指令优化(O1:不做指令优化,O2:2级别的指令优化)
- -o:输出文件
- -c:不需要输出文件
- -I:指定头文件
- -L:指定库文件位置
- -l:指定使用哪个库(注意这是个小写的L)
- 编译过程?
- 预编译
- 编译
- 链接,动态链接/静态链接
- 示例代码:
第一步:创建一个.c的文件 vim add.c #ifndef __MY_LIBRARY__ #define __MY_LIBRARY__ int add(int a, int b){ return (a+b); } #endif // __MY_LIBRARY__ 第二步:编译但不需要生成删除文件 clang -g -c add.c 第三步:生成静态库 libtool -static -o libmylib.a add.o 注意:在linux下生成静态库用ar命令,动态库用gcc;Mac下用libtool 第四步:编写.h头文件和调用的.c文件 - .h头文件(add.h) #ifndef __MY_LIBRARY__ #define __MY_LIBRARY__ int add(int a, int b); #endif - 调用的.c文件(testlib.c) #include <stdio.h> // 双引号代表优先在本地目录进行查找,当然也可以用<> #include "add.h" int main(int argc, char* argv[]){ printf("add=%d\n", add(3, 3)); return 0; } 第五步:编译并指定头文件和库文件位置等等 clang -g -o testlib testlib.c -I . -L . -lmylib - 这里的.代表就是在当前文件路径 - 另外一种分两步的执行方式,如下:(与上面一步到位同理) - clang -g -c testlib.c - clang -o testlib testlib.c -I . -L . -lmylib 第六步:运行及结果 - 运行:./testlib - 结果:add=6
13、调试器原理
-
Linux和Mac下所使用的调试器是不同的,Linux下使用的是GDB,在Mac下使用的是LLDB,虽然它们不同,但是基本原理是相通的。
-
编译输出带调试信息的程序(-g)。
-
调试信息包含:指令地址、对应源代码以及行号。
-
指令完成后,回调调试器。
-
GDB/LLDB常用命令:
命令 | GDB | LLDB
—|---|—
设置断点 | b | b
运行程序 | r | r
单步执行 | n | n
全部执行 | c | c
跳入函数 | s | s
跳出函数 | finish | finish
打印内容 | p | p
查看当前已设置好的断点列表 | break list | break list
退出 | quit | quit -
操作示例:
第一步:将testlib加载到调试器
lldb testlib
第二步:设置断点为main函数
b main
第三步:使用上述列表各种命令自行操作
r/s/finish/n/c/p等等...
第四步:查看生成的信息
ls testlib*
cd testlib.dSYM/Contents/Resources/DWARF
dwarfdump testlib(使用dwarfdump工具查看)