# C语言
# 换行符
- windows: /r/n
- mac:/r
- linux:/n
# printf(参数1,参数2)
- 参数1(必填):输出样式最终体现形式
- 参数2(选填):输出格式
- 整形:%d
- 实型:%f
- 字符:%c
- 字符串:%s
- 无符号:%u
- 内存地址:%p
- 内存大小 %zu
#include <stdio.h>
int main() {
int sum = 100 + 200;
int ab = 1E2;
printf("最终计算结果 %d",sum);
//无符号打印
unsigned int ser = 123;
printf("%u", ser);
//实型,保留两位
float c = 1.123;
printf("%.2f", c);
//双精度
double d = 1.123123;
printf("\n %lf", d);
return 0;
}
# 进制
- 二进制:2位一个整体:0b开头
ob10
ob11
- 八进制:三位数一个整体,0-7组成,0开头
010
011
- 十六进制:四位一个整体,0-9加油a-f(a堪称10,b看成11,以此类推)组成,0x开头
0x10
0x11
# 数据类型
隐式转换规则:
- 取值范围小的,和取值范围大的计算,小的会自动提升为大的,再进行运算
- short cha r类型的数据在运算的时候,先提升为int,再进行运算
取值范围
double > float > long long > long > int > short > char
触发条件
在进行计算,赋值等操作,会触发
符号表示
- signed 有符号整数
- unsigned 无符号整数
整数
- short 2 字节
- int 4 字节
- long 4(window | linux 32位) | 8(linux 64位) 字节
- longlong(c99) 8 字节
小数
- float
- double
字符
- char
# 数组长度计算
- 公式:总长度 / 数据类型占用的字节个数
int arr[] = {1,2,3,4,5,6,2,1,1};
arr[0] = 100;
int len = sizeof(arr) / sizeof(arr[0]);
printf("%d", len);
# 数组常见问题
- 数组作为参数传递
根据上面的计算数组长度,如果在参数中传递,进行计算会有问题
数组在定义处表示的是完整地址,但是传为参数传递是的首地址,如果需要遍历则需要把长度传递- 错误情况1
int main() {
int arr[] = {1,2,3,4,5,6,2,1,1};
printArr(arr);
return 0;
}
void printArr(int arr[]) {
int size = sizeof(arr) / sizeof(arr[0]);
printf("数组的长度 %d", size); //长度为2
}
- 正确情况
int main() {
int arr[] = {1,2,3,4,5,6,2,1,1};
int size = sizeof(arr) / sizeof(arr[0]);
printf("数组的长度 %d", size);
printArr(arr, size);
return 0;
}
void printArr(int arr[], int length) {
for (int i = 0; i < length; ++i) {
printf("数组的内容 %d\n", arr[i]);
}
}
- 使用数组名进行计算的时候,退化为只想第一个元素的指针,此时不再表示那个整体了
# 二维数组
语法:数据类型 变量名[m][n] =
- m:二维数组长度
- n:一维数组长度
#include <stdio.h>
int main() {
int arr[2][2] = {
{1, 2},
{3, 4}
};
// 遍历
int rows = sizeof(arr) / sizeof(arr[0]);
int cols = sizeof(arr[0]) / sizeof(arr[0][0]);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
//遍历方式2
#include <stdio.h>
int main() {
int arr1[3] = {1, 2, 3};
int arr2[4] = {1, 2, 3, 4};
int arr3[5] = {1, 2, 3, 4, 5};
//在之前先计算数组长度,不然再后面计算的是错误的
int arrIndex = sizeof(arr1) / sizeof(int);
int arr2Index = sizeof(arr2) / sizeof(int);
int arr3Index = sizeof(arr3) / sizeof(int);
//将长度放在数组中
int lengthArr[3] = {arrIndex, arr2Index, arr3Index};
int* arr[3] = {
arr1,
arr2,
arr3
};
//遍历
for (int i = 0; i < 3; i++) {
for (int j = 0; j < lengthArr[i]; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
//通过指针遍历
#include <stdio.h>
int main()
{
int arr[2][5] =
{
{1, 2},
{1, 2, 3, 4, 5}
};
//二位数组的指针索引
int (*p) [5] = arr;
for (int i = 0; i < 2; i++)
{
//遍历一维数组
for (int j = 0; j < 5; j++)
{
printf("%d ", *(*p + j));
}
printf("\n");
//二维数组指针+1(+1的意思就是一个字节,一个字节是4个bit)
//二维数组里面的一维数组是5大小,内容是int,一个int是4个字节
//4 * 5 一共就占用了20个字节,当指针+1的时候就是 20个字节 + 4,就到下一个数组了
p++;
}
}
#include <stdio.h>
int main()
{
int a[2] = {1, 2};
int b[2] = {3, 4};
int* arr[2] =
{
a,
b
};
//二位数组的指针索引
int **p = arr;
for (int i = 0; i < 2; i++)
{
//遍历一维数组
for (int j = 0; j < 2; j++)
{
printf("%d ", *(*p + j));
}
printf("\n");
//二维数组指针+1(+1的意思就是一个字节,一个字节是4个bit)
//二维数组里面的一维数组是5大小,内容是int,一个int是4个字节
//4 * 5 一共就占用了20个字节,当指针+1的时候就是 20个字节 + 4,就到下一个数组了
p++;
}
}
# 命名规范
- 变量名:全部小写
- 代码文件名:全部小写,单词用下划线分开
# 跳转 goto
#include <stdio.h>
int main()
{
printf("第一行");
goto five;
printf("第二行");
printf("第三行");
printf("第四行");
five:char str[20] = "第五行";
printf("%s", str);
return 0;
}
# 函数的注意事项
函数的申明往往需要在main函数的上方,不然调用的话会报错,找不到函数,如果一定要在别的下方申明需要在顶部申明
#include <stdio.h>
int getMonth();
void printYear();
int main()
{
printf("%d", getMonth());
return 0;
}
int getMonth() {
printYear();
return 2;
}
void printYear() {
printf("7月份");
}
# 常见函数库
使用别的库需要导入
#include <stdio.h>
#include <time.h>
int main()
{
long long res = time(NULL);
printf("%d", res);
return 0;
}
获取随机数
#include <stdio.h>
#include <stdlib.h>
int main()
{
//种子
srand(2);
for (int i = 0; i < 10; ++i) {
//获取随机数
int number = rand();
printf("%d\n", number);
}
return 0;
}
获取随机数在某个范围内 公式:rand() % ((尾数 + 1) - 开头) + 开头
- 7 - 23
# 指针
- 语法:属性类型* 指针名称 = &变量名称
- 内存占用:与类型无关,跟编译器有关:32位 4字节 64位 8字节
int main() {
int a = 11;
int* p = &a;
printf("%d\n", *p);
*p = 22;
printf("%d", *p);
return 0;
}
细节
函数中的变量的生命周期跟函数相关,函数结束了,变量也回收了此时其他函数中,就无法使用了。 如果希望不被回收,可以增加static,增加了之后这个变量,会随着程序一直结束
#include <stdio.h>
int* getStaticNumber();
int main() {
int *lastingNumber = getStaticNumber();
printf("获取到的number %d", *lastingNumber);
return 0;
}
int* getStaticNumber() {
static int a = 10;
return &a;
}
- 二级指针语法:指针类型** 指针名
#include <stdio.h>
int main() {
int a = 30;
int b = 20;
int *p = &a;
int** pp = &p;
*pp = &b;
printf("%d", **pp);
return 0;
}
# 指针的作用
- 操作其他函数的变量
意思就是在值传递的时候,别的修改只是当前作用域的没有修改原数据内容
#include <stdio.h>
void swap(int *a, int *b);
int main() {
int a = 10;
int b = 20;
printf("修改前的内容 %d ,%d \n", a, b);
swap(&a, &b);
printf("修改后的内容 %d ,%d", a, b);
return 0;
}
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
- 函数返回多个值(伪返回)
#include <stdio.h>
void getMaxAndMin(int arr[], int len, int *max, int *min);
int main() {
int arr[5] = {1, 1231, 321, 0};
int len = sizeof(arr) / sizeof(arr[0]);
int max = arr[0];
int min = arr[0];
getMaxAndMin(arr, len, &max, &min);
printf("最大值 %d, 最小值 %d", max ,min);
return 0;
}
void getMaxAndMin(int arr[], int len, int *max, int *min) {
for (int i = 0; i < len; ++i) {
if(arr[i] > *max) {
*max = arr[i];
}
if(arr[i] < *min) {
*min = arr[i];
}
}
}
# 函数指针
#include <stdio.h>
void method1();
int method2(int num1, int num2);
int main()
{
//无参就是空的
int (*p)() = method1;
//类型根据函数申明的来
int (*p2)(int, int) = method2;
p();
int sum = p2(1, 2);
printf("计算的结果 %d ", sum);
}
void method1() {
printf("method \n");
}
int method2(int num1, int num2) {
printf("%d %d \n", num1, num2);
return num1 + num2;
}
# 案例
#include <stdio.h>
int add(int num1, int num2);
int subject(int num1, int num2);
int mult(int num1, int num2);
int deiv(int num1, int num2);
int main()
{
int (*p[4])(int, int) = {
add,
subject,
mult,
deiv
};
printf("请输入两个数\n");
fflush(stdout);
int num1, num2;
scanf("%d%d", &num1, &num2);
printf("第一个数 %d\n", num1);
printf("第二个数 %d\n", num2);
printf("请选择一个数字来进行计算\n 1 加法 \n 2 减法 \n 3 乘法 \n 4 除法\n");
fflush(stdout);
int chose;
scanf("%d", &chose);
int sum = p[chose - 1](num1, num2);
printf("计算的结果 %d", sum);
return 0;
}
int add(int num1, int num2) {
return num1 + num2;
}
int subject(int num1, int num2) {
return num1 / num2;
}
int mult(int num1, int num2) {
return num1 * num2;
}
int deiv(int num1, int num2) {
return num1 / num2;
}
# 字符串
- 语法1:char 变量[字符长度] = “内容”;
- windows中一个中文占用2个字节
char str[4] = "str";
- 注意点
- 在底层时还是以字符数组存储的
- 数组长度要么不写,要么预留一个结束标记 (结束标记是 \0 ) 的空间,不然会出现错误的字符
- 如:char str [3] = "str"; 这里就会出现问题,应该多加一位
int main() {
//错误的
char str[3] = "str";
printf("%s\n", str);
//正确的
char str2[4] = "str";
printf("%s\n", str2);
return 0;
}
- 语法2:char* 变量名 = ""
注意点,这种定义的字符,底层才会放到只读的常量区
int main() {
char *str = "str1";
printf("%s", str);
return 0;
}
# 常见函数
需要先导入头文件,#include <string.h>
- strlen:获取字符串长度
- strlen:获取字符串的长度
- strcat:拼接两个字符串
- strcpy:复制字符串
- strcmp:比较两个字符串
- strlwr:将字符串变成小写
- strupr:将字符串变成大写
# 结构体
语法:struct 结构体名称 { 变量类型 变量名称; }
#include <stdio.h>
#include <string.h>
struct Student {
int age;
char name[100];
};
int main() {
struct Student student;
student.age = 18;
strcpy(student.name, "张三");
printf("年龄 %d", student.age);
printf("姓名 %s", student.name);
return 0;
}
# 结构体嵌套
#include <stdio.h>
#include <string.h>
typedef struct {
char address[100];
} Address;
typedef struct {
int age;
char name[100];
Address address;
} S;
int main() {
S student;
strcpy(student.name, "张三");
student.age = 18;
strcpy(student.address.address, "上海市");
printf("姓名 %s \n", student.name);
printf("年龄 %d\n", student.age);
printf("地址 %s\n", student.address.address);
return 0;
}
# 结构体作为函数的参数传递
如果想在函数中修改结构体需要以内存地址传递
#include <stdio.h>
//struct 可省略
typedef struct {
int age;
int sex;
} S;
//声明的方法,如果用到结构体,需要在结构体下方
//不然编译的时候找不到
void updateAge(S* s);
int main() {
S student = {
1,
1
};
printf("初始年龄 %d", student.age);
updateAge(&student);
printf("修改后的年龄 %d", student.age);
return 0;
}
void updateAge(S* s) {
s->age = 30;
}
# 别名
语法:typedef struct { 变量类型 变量名称; } 别名名称;
//struct 可省略
typedef struct {
int age;
int sex;
} s;
int main() {
s student = {
1,
1
};
printf("年龄 %d",student.age);
return 0;
}clipo
# 内存对齐
- 规则:内存地址 / 占用字节 = 结果可以整除
- 结构体的内存对齐
- 结构体的总大小,是最大类型的整数倍
# 共同体
语法:
union 变量名称 {
数据类型 变量名称;
}
特点:
- 共用体,也叫联合体,共同体
- 所有的变量都使用同一个内存空间
- 所占的内存大小=最大成员的长度(也受内存对齐影响)
- 每次只能给一个变量进行赋值,因为第二次赋值时会覆盖原有的数据
用例:
#include <stdio.h>
#include <string.h>
union Money {
int moneyoi;
char moneyStr[10];
double moneyd;
};
int main() {
union Money money;
// money.moneyoi = 100;
// money.moneyd = 1.11;
strcpy(money.moneyStr, "100元");
printf("%s", money.moneyStr);
return 0;
}
# 动态内存
malloc:申请连续控件
calloc:申请空间+数据初始化(用的少)
realloc:修改空间大小
free:释放空间
细节
- malloc创建空间的单位是字节
- malloc返回的是void类型的指针,没有步长的概念,也无法获取空间中的数据,需要强转
- malloc返回的仅仅是首地址,没有总大小,最好定义一个变量记录总大小
- malloc申请的空间不会自动消失,如果不能正确释放,会导致内存泄露
- malloc申请的空间过多,会产生虚拟内存
- malloc申请的空间没有初始化值,需要先赋值才能使用
- free释放完空间之后,空间中数据叫做脏数据,可能被清空,可能被修改为其他值
- calloc就是在malloc的基础上多一个初始化的动作
- realloc修改之后的空间,地址值有可能发生变化,也有可能不会改变,但是原本的数据不会丢失
- realloc修改之后,无需释放原来的空间,函数底层会进行处理
# malloc
#include <stdio.h>
#include <stdlib.h>
int main() {
//单位是字节,填写100的话,就是申请100个连续的字节地址
int *p = malloc(100 * sizeof(int));
printf("%p\n", p);
for (int i = 0; i < 100; i++) {
// *(p + i) = (i + 1) * 10;
//第二种写法
//p[i] 会解析成 p + 1
p[i] = (i + 1) * 10;
}
for (int i = 0; i < 100; i++) {
printf("%d\n", *(p + i));
}
return 0;
}
# calloc
#include <stdio.h>
#include <stdlib.h>
int main() {
//单位是字节,填写100的话,就是申请100个连续的字节地址
int *p = calloc(100, sizeof(int));
for (int i = 0; i < 100; ++i) {
printf("%d\n", *(p + 1));
}
return 0;
}
# realloc
#include <stdio.h>
#include <stdlib.h>
int main() {
//单位是字节,填写100的话,就是申请100个连续的字节地址
int *p = malloc(100 * sizeof(int));
for (int i = 0; i < 100; ++i) {
printf("%d\n", *(p + 1));
}
//修改之后不会改变原有数据,会拷贝到一个新的
realloc(p, 20 * sizeof(int));
return 0;
}
# free
#include <stdio.h>
#include <stdlib.h>
int main() {
//单位是字节,填写100的话,就是申请100个连续的字节地址
int *p = malloc(100 * sizeof(int));
for (int i = 0; i < 100; ++i) {
printf("%d\n", *(p + 1));
}
//修改之后不会改变原有数据,会拷贝到一个新的
realloc(p, 20 * sizeof(int));
free(p);
return 0;
}
# IO
- 打开文件:fopen
- 读取:
- fgetc:一次读一个字符
- fgets:一次读一行
- fread:一次读多个
- 写
- fputc
- fputs
- fwrite
- 关闭:fclose
# 操作模式
- r:只读
- w:只写
- 文件不存在,创建新文件
- 文件已存在,清空文件
- a:追加写
- 文件不存在,创建新文件
- 文件已存在,不清空文件,续写
- rb:只读模式(操作二进制文件)
- wb:只写
- 文件不存在,创建新文件
- 文件已存在,清空文件
- ab:追加写(操作二进制文件)
- 文件不存在,创建新文件
- 文件已存在,不清空文件,续写
# 案例:
- 读
int main() {
FILE* file = fopen("D:/clion/workspace/helloworld/news/a.txt", "r");
char arr[1024];
char* str;
while ((str = fgets(arr, 1024, file)) != NULL) {
printf("%s", arr);
}
fclose(file);
return 0;
}
- 指定一次读多少
#include <stdio.h>
int main() {
FILE* file = fopen("D:/clion/workspace/helloworld/news/a.txt", "r");
char arr[4];
int n;
/**
* 传入一个数组,存放读取到的数据
* 数组每个数据占用多少字节
* 数组长度
* file
*/
while ((n = fread(arr,1,4,file)) != 0) {
for (int i = 0; i < n; ++i) {
printf("%c", arr[i]);
}
}
fclose(file);
return 0;
}
- 写出
#include <stdio.h>
int main() {
FILE* file = fopen("D:/clion/workspace/helloworld/news/b.txt", "w");
int c = fputc(97, file);
printf("%c", c);
//写出成功返回一个非负数
fputs("你好", file);
/**
* 写出的内容
* 元素占用的字节
* 写出数据总长度
* file
*/
char arr[] = {101, 102};
fwrite(arr, 1, 2, file);
fclose(file);
return 0;
}
# 枚举 enum
#include <stdio.h>
enum Status {
LOWER = 0 ,
MIDDLE = 1,
HIGH = 2,
};
int main(void) {
enum Status lower = MIDDLE;
printf("%d \n", lower);
switch (lower) {
case LOWER : {
printf("低档位");
break;;
}
case MIDDLE : {
printf("中档位");
break;
}
case HIGH: {
printf("高档位");
break;
}
}
return 0;
}