https://github.com/impact-eintr/LinuxC
- 1960 原型A语言->ALGOL语言
- 1963 CPL语言
- 1967 BCPL
- 1970 B语言
- 1973 C语言
- 基础性语言
- 语法简洁 紧凑 方便 灵活(得益于指针)
- 运算符 数据结构丰富
- 结构化 模块化编程
- 移植性好 执行效率高
- 允许直接对硬件操作
- 概念的正确性
- 动手能力
- 主动阅读优秀的程序段
- 大量练习,编程是技术不是理论
- 基本概念
- 数据类型 运算符 表达式
- 输入输出
- 流程控制
- 数组
- 指针
- 函数
- 构造类型
- 动态内存管理
- 常用库函数
- 调试工具和调试技巧
- 当前测试环境是安装了基于
archlinux的manjarolinux发行版的物理机,大家自己搭建linux环境的话推荐试用或租用云服务器或者尝试WSL gcc版本是 10.2.0- 编辑器使用
vim(推荐vim配置vimplus)
#inlcude <stdio.h> #include <stdlib.h> int main(void){ printf("hello world\n"); exit(0); }gcc 编译c的源文件过程
gcc -vC源文件->预处理->编译->汇编->链接->可执行文件
完整过程
- 预处理
gcc -E hello.c > hello.i- 编译
gcc -S hello.i - 汇编
gcc -c hello.s - 链接->可执行文件
gcc hello.o -o hello或者
gcc hello.c -o hello又或者
make hello执行
./hello hello world在c中,如果没有出现函数原型,就默认函数的返回值是int
#include <stdio.h> int main() { <<<<<<< HEAD int *num = malloc(sizeof(int)); *num = 100; printf("%d\n",*num); ======= int *num = (int *)malloc(sizeof(int)); >>>>>>> d8201497ccb6ce60411ef4f1d6347921a54e7c22 return 0; }hello.c: 在函数‘main’中: hello.c:5:23: 警告:隐式声明函数‘malloc’ [-Wimplicit-function-declaration] 5 | int *num = (int *)malloc(sizeof(int)); | ^~~~~~ hello.c:5:23: 警告:隐式声明与内建函数‘malloc’不兼容- 正确写法
#include <stdio.h> #include <stdlib.h> int main() { int *num = (int *)malloc(sizeof(int)); return 0; }- 基本类型
- 数值类型
- short
- int
- long
- float
- double
- 字符类型
- 数值类型
- 构造类型
- 数组
- 结构体 struct
- 共用体 union
- 枚举类型 enum
- 指针类型
- 空类型 void
254 -> unsigned int -> 32bit (254)10 = (1111 1110)2 = (376)8 = (FE)16 254 B11111110(c不认识这个表示) 0376 0xFEint i; float f; doubel d; char ch; ch + i -> i f -d -> d (ch + i) - (dloat -double) -> double#incldue <stdio.h> #include <stdlib.h> #include <stdbool.h> int main() { bool a = false; printf("a = %d\n", a); exit(0); }int func(float f) { if (f < 0) { return -1; } else if (fabs(f-0) <= 1e-6) { return 0; } else { return 1; } }在iso c中 char有无符号是未定义行为
0(整形) '0'(字符常量) "0"(字符串常量) '\0'(字符常量)数据类型要和后续代码中所使用的输入输出要相匹配(小心自相矛盾)
#include <stdlib.h> #include <stdio.h> int main() { unsigned int a; a = 1 << 31; printf("%d", a); } 正确
#include <stdlib.h> #include <stdio.h> int main() { unsigned int a; a = 1 << 31; printf("%ud", a); } - 整形常量 1 890
- 实型常量 1.2 3.14
- 字符常量 '\t' '\n' '\0' '\015'(8进制) '\x7f' '\018'(错误的表示!!)
- 字符串常量 "" "a" "abXYZ" "abc\n\021\010"(a b c \n \021 \0 1 8)
- 标识常量
#include <stdlib.h> #include <stdio.h> #define PI 3.1415926 #define ADD 2+3 // 正确写法 //#define ADD (2+3) int main() { printf("%f\n", PI); printf("%d\n", ADD * ADD); } # 6 "pi.c" int main() { printf("%f\n", 3.1415926); printf("%d\n", 2+3 * 2+3); } #include <stdlib.h> #include <stdio.h> #define MAX(a,b) ({int A=a,B=b; ((A) > (B) ? (A) : (B));}) int main() { int a = 3, b = 5; printf("%d\n",MAX(a++, b++)); printf("%d\n",MAX(a++, b++)); } #include <stdlib.h> #include <stdio.h> #define MAX(a,b) ({typeof(a) A=a,B=b; ((A) > (B) ? (A) : (B));}) int main() { int a = 3, b = 5; printf("%d\n",MAX(a++, b++)); printf("%d\n",MAX(a++, b++)); } 在程序的预处理阶段,占编译时间,不占运行时间(没有函数调用的消耗),但不检查语法(比较危险)
[存储类型] 数据类型 标识符 = 值 TYPE NAME = VALUE
-
auto 默认 自动分配空间
-
register 建议型 寄存器类型 只能定义局部变量,不能定义全局变量,大小有限制,只能定义32位大小的数据类型,比如double就不可以。因为寄存器没有地址,所以一个register类型的变量无法打印出地址查看或使用。
-
static 静态型 自动初始化为0值或空值 并且static变量的值有继承性。另外常用来修饰一个变量或者函数(防止当前函数对外扩展)
#include <stdlib.h> #include <stdio.h> void func() { static int x = 1; x++; printf("%d\n", x); } int main() { func(); func(); func(); } 2 3 4- extern 说明型 意味着不能改变被说明的量的值或类型 可以用来扩展外部变量的作用域
#ifndef EXTERN_H__ #define EXTERN_H__ void func(); #endif#include "extern.h" extern int i; // 不定义 而是引用了其他地方的i int func() { printf("[%s]%d\n", __FUNCTION__, i); }#include "stdlib.h" #include "stdio.h" #include "extern.h" int i = 10; int main() { printf("[%s]%d\n", __FUNCTION__, i); }#include <stdio.h> #include <stdlib.h> int main() { int a = 1, b = 2, c = 3, d = 4; int m = 1, n = 1; (m = a > b) && (n = c > d); printf("m = %d\n n = %d\n", m, n); // m : 0 n : 1 }#include <stdio.h> #include <stdlib.h> int main() { printf("%lu, %lu, %lu, %lu, %lu, %lu, %lu\n", sizeof(int),sizeof(short), sizeof(long), sizeof(double), sizeof(float), sizeof(char), sizeof(void*)); } 4, 2, 8, 8, 4, 1, 8 - | 按位或
- & 按位与
- ^ 按位异或
- ~ 按位取反
应用
将操作数中的第n位置1 其他位不变 num = num | 1 << n; 将操作数中的第n位置0 其他位不变 num = num & ~(1<<n); 测试第n位: if(num & (1<<n))
- printf
int main() { int i = 123; printf("%4d\n", i); float f = 1.23455; printf("%.3f\n", f); char* s= "helloworld"; printf("%10.5s\n", s); }int main() { printf("[%s:%d] before while().", __FUNCTION__, __LINE__); while(1); printf("[%s:%d] after while().", __FUNCTION__, __LINE__); }正确写法
#include <stdlib.h> #include <stdio.h> int main() { printf("[%s:%d] before while().]\n", __FUNCTION__, __LINE__); // 或者 //printf("[%s:%d] before while().", __FUNCTION__, __LINE__); //fflush(stdout); while(1); printf("[%s:%d] after while().", __FUNCTION__, __LINE__); } - scanf
int main() { int i; scanf("%d", &i); printf("%d\n", i); }scanf 在使用
%s的时候要特别小心
#include <stdio.h> #include <stdlib.h> int main() { char S[3]; scanf("%s", S); // 如果输入 abcdef printf("%s", S); // 可能会出现段错误 } scanf 在循环中使用的时候要特别小心
int main() { int ret = 0; int d = 0; while(1) { ret = scanf("%d, d); if (ret == -1) { perror("Error"); break; } printf("&d\n", d); } exit(0); }处理换行
int main() { int i = 0; char c = 0; scanf("%d", &i); scanf("%*c%c", &c); // 或者 //getchar(); //scanf("%c", &c); printf("i = %d, c = %c", i, c); }跳过
构造类型 连续存放
[存储类型] 数据类型 标识符[下标]
- static
static int a[10];- {}
int a[3] = {1, 2, 3};- arr[i]
- arr+i
一个常量
#include <stdio.h> #include <stdlib.h> int main() { int arr[3] = {1, 2, 3}; printf("%ld\n", sizeof(arr)); // 下面这句是错的 arr = {4, 5, 6}; for (int i = 0;i < sizeof(arr)/sizeof(int);i++) { printf("%d", *(arr+i)); } } c对数组不进行越界检查,需要格外小心
#include <stdio.h> #include <stdlib.h> int main() { int fib[10] = {1, 1}; for (int i = 2;i < 10;i++) { fib[i] = fib[i-1]+ fib[i-2]; } for (int i = 0;i < 10;i++) { printf("%d ", fib[i]); } } #include <stdio.h> #include <stdlib.h> int main() { int arr[] = {2, 3, 5, 4, 6, 7, 1, 9}; for (int i = 0;i < sizeof(arr)/sizeof(int);i++) { for (int j = 0;j < sizeof(arr)/sizeof(int)-1-i;j++) { if(arr[j] > arr[j+1]) { int tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp; } } } for (int i = 0;i < sizeof(arr)/sizeof(int);i++) { printf("%d ", arr[i]); } } #include <stdio.h> #include <stdlib.h> int main() { int arr[] = {3, 2, 5, 4, 9, 7, 1, 6}; for (int i = 0;i < sizeof(arr)/sizeof(int);i++) { int m = i; for (int j = i+1;j < sizeof(arr)/sizeof(int);j++) { if(arr[j] < arr[m]) { m = j; } } if (m != i) { int tmp = arr[i]; arr[i] = arr[m]; arr[m] = tmp; } } for (int i = 0; i < sizeof(arr) / sizeof(int); i++) { printf("%d ", arr[i]); } } [存储类型] 数据类型 标识符[行下标][列下标]
int main() { int a[M][N] = {1, 2, 3, 4, 5}; int b[][N] = {1, 2, 3, 4, 5}; int c[M][] = {1, 2, 3, 4, 5}; // 错误 for (int i = 0;i < M;i++) { for (int j = 0;j < N;j++) { printf("%d ", *(a+i+j*)); } } }a[2][3] = b[3] + c[3] a[0] = b[0] a[1] = c[0]
[存储类型] char 标识符[]
注意部分初始化的时候,剩余部分会自动初始化为'\0'
scanf 无法获取带有分隔符的字符串(\t, \n, )
- strlen & sizeof
- strcpy & strncpy
- strcat & strncat
- strcmp & strncmp
单词统计
#include <stdio.h> #include <stdlib.h> #include <string.h> #define STRSIZE 1024 int main() { char str[STRSIZE] = {}; fgets(str, STRSIZE, stdin); int count= 0, flag = 0; for (int i = 0;str[i] != '\0';i++){ if (str[i] == ' ') { flag = 0; } else if(!flag) { count++; flag = 1; } } printf("%d\n", count); } 64位环境 指针类型占用8个字节 32位环境 指针类型占用4个字节
变量对某块内存的抽象表示 指针 == 地址 变量名 == 抽象出来的某块空间的别名
int i = 1; int *p = &i; int ** q = &p; i = 1; &i = 0x2000; p = 0x2000; &p = 0x3000; *p = 1; q = 0x3000; &q = 0x4000; *q = 0x2000; **q = 1;char *s = "hello"; void *i = s;& * 关系运算 ++ --
#include <stdlib.h> #include <stdio.h> int main () { int a[3] = {1, 2, 3}; // a是常量 p是变量 int *p = a; for (int i = 0;i < sizeof(a)/sizeof(*a);i++) { printf("%p -> %d\n", p+i, *(p+i)); } } p++ != p+1
#include <stdlib.h> #include <stdio.h> int main () { int a[3]; int *p = a; for (int i = 0;i < sizeof(a)/sizeof(*a);i++) { scanf("%d", p++); } for (int i = 0;i < sizeof(a)/sizeof(*a);i++) { printf("%d\n", *(p++)); } } #include <stdlib.h> #include <stdio.h> int main() { int a[2][3] = {{1, 2, 3},{ 4, 5, 6}}; int (*p)[3] = a; for (int i = 0;i < sizeof(a)/sizeof(*a);i++) { for (int j = 0;j < sizeof(*a)/sizeof(**a);j++) { printf("%d ", *(*(p+i)+j)); } } } #include <stdlib.h> #include <stdio.h> int main() { int a[2][3] = {{1, 2, 3},{ 4, 5, 6}}; int *p = &a[0][0]; for (int i = 0;i < sizeof(a)/sizeof(*a);i++) { for (int j = 0;j < sizeof(*a)/sizeof(**a);j++) { printf("%d ",*(p+(i * sizeof(*a)/sizeof(**a))+j)); } } } const float pi = 3.14; // 常量化变量先看到指针就是指针 先看到常量就是常量
- 常量指针 指向的内存不能通过这个指针修改
const int* p; int const *p; char *strcpy(char *restrict dest, const char *src); // src是源字符串 不应该被修改- 指针常量 指向的位置不能变 可以通过这个指针修改内存的值
int *const p; const int *const p;int *arr[3]指针数组排序
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *name[5] = {"golang", "java", "c", "dart", "erlang"}; int k; char *tmp; for (int i = 0;i < (sizeof(name)/sizeof(*name))-1;i++) { k = i; for (int j = i+1;j < (sizeof(name)/sizeof(*name));j++) { if (strcmp(name[k], name[j]) > 0) { k = j; } } if (k != i) { tmp = name[i]; name[i] = name[k]; name[k] = tmp; } } for (int i = 0;i < (sizeof(name)/sizeof(*name));i++) { printf("%s\n", *(name+i)); } } int a[2][3] = {{1, 2, 3},{ 4, 5, 6}}; int (*p)[3] = a;#include <stdlib.h> #include <stdio.h> int main() { int a[2][3] = {{1, 2, 3},{ 4, 5, 6}}; int (*p)[3] = a; for (int i = 0;i < sizeof(a)/sizeof(*a);i++) { for (int j = 0;j < sizeof(*a)/sizeof(**a);j++) { printf("%d ", *(*(p+i)+j)); } } } 没啥好说的
#include <stdlib.h> #include <stdio.h> int main() { exit(printf("Hello!\n")); } - 值传递
- 地址传递
- 全局变量
- 嵌套
#include <stdlib.h> #include <stdio.h> int max(int a, int b, int c) { int tmp = a > b ? a : b; return tmp > c ? tmp : c; } int min(int a, int b, int c) { int tmp = a < b ? a : b; return tmp < c ? tmp : c; } int dist(int a, int b, int c) { return max(a, b, c) - min(a, b, c); } int main() { printf("%d\n", dist(8, 5, 10)); } - 递归
#include <stdio.h> #include <stdlib.h> int func(int n) { if (n < 0) { return -1; } if (n == 1 || n == 2) { return 1; } return func(n-1) + func(n-2); } int main() { int n; scanf("%d", &n); printf("fib %d = %d", n, func(n)); exit(0); } #include <stdio.h> #include <stdlib.h> int func(int n) { if (n < 0) { return -1; } if (n == 0 || n == -1) { return 1; } return n * func(n - 1); } int main() { int n; scanf("%d", &n); printf("%d! = %d", n, func(n)); exit(0); } #include <stdlib.h> #include <stdio.h> // 注意这里的int *arr 的大小是8个字节 是一个普通的指针不是数组 所以一定要传大小 void printarr(int *arr, int size) { for (int i = 0;i < size;i++) { printf("%d ", *(arr+i)); } printf("\n"); } int main() { int arr[] = {1, 2, 3, 4, 5}; printarr(arr, sizeof(arr)/sizeof(*arr)); } #include <stdlib.h> #include <stdio.h> void printarr(int *arr, int size) { for (int i = 0;i < size;i++) { printf("%d ", *(arr+i)); } printf("\n"); } void printarr2(int (*p)[3], int m , int n) { for (int i = 0;i < m;i++) { for (int j =0;j < n;j++) { printf("%4d ", *(*(p+i)+j)); } printf("\n"); } } int main() { int arr[][3] = {1, 2, 3, 4, 5, 6}; printarr2(arr, 2, 3); } - 指针函数
#include <stdio.h> #include <stdlib.h> #define M 2 #define N 3 int *findnum(int (*p)[N], int num) { if (num > M - 1) { return NULL; } return *(p + num); } int main() { int arr[M][N] = {{1, 2, 3},{ 4, 5, 6}}; int *res = findnum(arr, 1); for (int i = 0; i < N; i++) { printf("%d ", *(res + i)); } } -
函数指针
类型 (*指针名)(形参) -
函数指针数组
类型 (*数组名[下标]) (形参)
指向指针函数的函数指针数组
int *(*funcp[N])(int)实际例子
int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void *), void *restrict arg);描述复杂的数据类型
struct node_st{ type1 name1; type2 name2; ... };struct day { int H; int M; int S; }; struct student_st{ char *name; struct day day; };- 结构体 .
- 结构体指针 ->
struct A { int i; char c; float f; }; int main() { // TYPE NAME = VALUE; struct A a = {123, 'A', 2.22}; // 初始化 struct A a_ = { .c = 'A', .f = 2.22}; // 部分初始化 struct A *ap = { .c = 'A', .f = 2.22}; // 部分初始化 printf("%d %c %.2f\n",a.i, a.c, a.f); // 成员引用 // 123 A 2.22 printf("%d %c %.2f\n",a_.i, a_.c, a_.f); // 成员引用 // 0 A 2.22 printf("%d %c %.2f\n",ap->i, ap->c, ap->f); // 成员引用 // 0 A 2.22 }addr % sizeof(type) 不能整除的话就要继续往下偏移
#include <stdio.h> #include <stdlib.h> struct A { int i; char c; float f; }; // 可以使用下面的方法取消对齐 常用于网络通信 struct B { int i; char c; float f; }__attribute__((packed)); int main() { struct A a; struct B b; printf("A = %ld\n", sizeof(a)); printf("B = %ld\n", sizeof(b)); } N选一 多个成员共用一块空间 取最大的成员的类型大小作为共用体的类型大小
union test_un{ int i; float f; double d; char ch; };同结构体 可以互相嵌套
成员引用:
- u.成员名
- up->成员名
32位的无符号数的高16位和低16位相加
#include <stdlib.h> #include <stdio.h> #include <stdint.h> int main() { uint32_t i = 0x11223344; printf("%x\n", (i>>16)+(i&0xFFFF)); }另一种写法
#include <stdlib.h> #include <stdio.h> #include <stdint.h> union { struct { uint16_t i; uint16_t j; }x; uint32_t y; }u; int main() { uint32_t i = 0x11223344; printf("%x\n", (i>>16)+(i&0xFFFF)); u.y = 0x11223344; printf("%x\n", u.x.i + u.x.j); } enum 标识符{ 成员1; ... };enum dar { MON = 1, TUS, WEB, THR, FRI, SAT, SUN, }; int main() { enum day a = FRI; printf("%d\n", a); }enum status { RUNNING = 1, STOP, PAUSE, }; struct job { int id; int state; time_t start, end; }; int main() { struct job_st job1; switch(jobs.state) { case RUNNING: // TODO break; case STOP: // TODO break; case PAUSE: // TODO break; default: // TODO abort(); } } typedef type typename
typedef int INT int main() { INT i = 9; }typedef 和 define 的区别
#define IP int * type int *IP; int main() { // 宏 IP p, q; int *p, q; // 一个int * 一个int // typedef IP p, q; int *p, *q; // 两个int * }数组
typedef int ARR[6]; // int [6] 改名为 ARR ARR a; // int a[6];结构体
typedef struct { int i; float f; }NODE, *NODEP;函数
typedef int *FUNC(int)函数指针
typedef int* (*FUNCP)(int)- malloc
- calloc
- realloc
- free 谁申请谁释放
#include <stdlib.h> #include <stdio.h> int main() { int *ip = malloc(sizeof(int)); *ip = 1; printf("%d\n", *ip); free(ip); } 动态数组
#include <stdlib.h> #include <stdio.h> int main() { int *p; int num = 5; p = malloc(num * sizeof(int)); for (int i = 0;i < num;i++) { scanf("%d", p+i); } for (int i = 0;i < num; i++) { printf("%d ", *(p+i)); } printf("\n"); exit(0); } 内存申请与函数传值
#include <stddef.h> #include <stdlib.h> #include <stdio.h> void func1(void *p, size_t size) { if(p == NULL) { return; } p = malloc(size); } void func2(int **p, size_t size) { if(*p == NULL) { return; } *p = malloc(size); } void *func3(void *p, size_t size) { if(p == NULL) { return NULL; } p = malloc(size); return p; } int main() { int num = 100; int *p = NULL; func1(p, num); // 内存会泄露 func2(&p, num); // 传递二级指针 p = func3(p, num); // 将申请的内存返回 free(p); exit(0); } #include <stddef.h> #include <stdlib.h> #include <stdio.h> void func2(int **p, size_t size) { if(*p == NULL) { return; } *p = malloc(size); } int main() { int num = 100; int *p = NULL; func2(&p, num); // 传递二级指针 free(p); // p = NULL; *p = 123; printf("%d\n", *p); // 这个指针已经是野指针了 exit(0); }- free代表着变量p不再拥有原来指向内存空间的引用权限
- free后最好马上将指针置NULL
工程管理 依赖管理
- makefile(用户自定义 更高优先级)
- Makefile(默认)
mytool:main.o tool1.o tool2.o gcc main.o tool1.o tool2.o -o mytool main.o:main.c gcc main.c -c -Wall -g -o main.o tool1.o:tool1.c gcc tool1.c -c -Wall -g -o tool1.o tool2.o:tool2.c gcc tool2.c -c -Wall -g -o tool2.o OBJS=main.o tool1.o tool2.o CC=gcc mytool:$(OBJS) $(CC) $(OBJS) -o mytool main.o:main.c $(CC) main.c -c -Wall -g -o main.o tool1.o:tool1.c $(CC) tool1.c -c -Wall -g -o tool1.o tool2.o:tool2.c $(CC) tool2.c -c -Wall -g -o tool2.o clean: $(RM) $(OBJS) mytool -r
$^ 表示在上一句依赖关系中被依赖的所有文件 $ @ 表示在上一句依赖关系中依赖项的目标文件
CFLAGS=-Wall -g -c OBJS=main.o tool1.o tool2.o CC=gcc mytool:$(OBJS) $(CC) $^ -o $@ main.o:main.c $(CC) $^ $(CFLAGS) -o $@ tool1.o:tool1.c $(CC) $^ $(CFLAGS) -o $@ tool2.o:tool2.c $(CC) $^ $(CFLAGS) -o $@ clean: $(RM) $(OBJS) mytool -r% 表示同一个名字
CFLAGS=-Wall -g -c OBJS=main.o tool1.o tool2.o CC=gcc mytool:$(OBJS) $(CC) $^ -o $@ %.o:%.c $(CC) $^ $(CFLAGS) -o $@ clean: $(RM) $(OBJS) mytool -r