目录
C语言笔记
视频
https://www.bilibili.com/video/BV1vs411n7TH
编译全过程
主要是分4步
1.预处理
宏定义展开,头文件展开,去掉注释,条件编译
bash
gcc -E main.c -o main.i
main1.c
文件内中就是包含我们所有导入的库的代码
2.编译,
检查语法,把语言编译成汇编语言
bash
gcc -S main.i -o main.s
3.汇编
这一步是把main2.s 汇编代码转换成二进制文件
bash
gcc -c main.s -o main.o
4.链接
bash
gcc main.o -o main
会生成一个main.exe
文件
一次编译
bash
gcc -o main.exe main.c main .c
基本语法
c
// 常量 第一种
const int i = 10;
// 注意,在c语言中,const 不安全
// 常量第二种
// 定义宏变量
//在主函数外面定义
#include <stdio.h>
#define PI 3.12323
// 不需要加等号,和分号
void main(){
// f 会保留6位小数.并且四舍五入
printf("%f",PI);
}
输入
c
#include <stdio.h>
void main(){
int a;
scanf("%d",&a);
printf("%d",a);
}
易错点
c
float a = 4.5e0;
double c = .288;
double c = 288.;
科学表示法
分为三部分
() e ()
📌第一部分
()
为一个浮点型, 第二个部分是e ,第三部分()
为一个十进制的整数,可以是正数和负数,并且如果是正数,+
号可以省略
格式化输出
%3.2f
标识保留2位小数,并且会四舍五入 ,3表示整体的宽度,不足3个宽度空格填充,小数点也算一个宽度
%s
输出字符串
注意字符串末尾自带一个\0 而 %s 会一直输出字符串的字符,直到找到\0为止
%6f
代表小数整体宽度为6个,包含小数点,不足6位,在最后填0
%6.3f
代表整体宽度为6,包含小数点,如不足6位,在开头填充空格,为啥填充0,是因为小数点后的位数被锁定了
字符串
字符串输入
c
void main(){
//gets 可以接收空格
char buff[100];
gets(buff);
printf("%s\n",buff);
// scanf 的%s 默认是不能接收空格 但是可以使用正则表达式达到接收空格的效果
scanf("%s",&buff);
printf("%s\n",buff);
// fgets 是安全的 不会出现溢出错误,如果超出数组长度,则只接收数组长度-1的字符串
fgets(buff,sizeof(buff),stdin);
printf("%s",buff);
}
字符串输出
c
void main(){
// puts 自带一个\n 并且也是读取到\0 后停止
char string[] = {"dsadsa"};
puts(string);
printf("%s\n",string);
fputs(string,stdout);
}
字符串长度
c
#include <string.h>
char string[] = "hello";
char string1[100] = "hello";
int size = sizeof(string);
int size1 = sizeof(string1);
unsigned int len = strlen(string);//注意要导包 <string.h>
printf("%d\n",size);// 6 因为如果是没有声明长度的就是字符串数组,默认末尾有一个\0 所以长度是6
printf("%d\n",size1); //100 因为声明的长度是100 所以就是100
printf("%d\n",len); // 计算的是字符串的长度
关于布尔型
只有0
才是false
其他都是true
指针
指针类似于Java的对象内存地址
声明指针
c
int *num = 10;
printf("%d",*num);
通过使用*变量名 可以获取指针所指向的值
c
int i = 10;
int *num = &i;
// & 表示取地址值,把i变量的地址值赋予给num 指针,这样,num指针 指向得就是10
printf("%s",*num); //10
数组
c
int array[3] = {12,23,45};
int *num = array; // 此时num 为指向数组的一个数组指针 默认指向第一个值
printf("%d",*num);// 输出 12 第一个值
printf("%d",*array);// 输出12 数组默认就是一种指针类型 默认指向第一个
printf("%d",*(num+1)); // 指针计算 单独的没有加* 号的数组指针 表示的是数组的第一个值得地址,也就是索引值,加1 并不是地址加1,而是内存地址+所占用字节数,int 为4个字节,所以会加4 最后输出得值为 23
指针可以为任意内容,
下标越界可能不会报错 但是可能会读取到其他内存上的值
指针数组
指数组里全是指针
c
int a = 16, b = 932, c = 100;
//定义一个指针数组
int *arr[3] = {&a, &b, &c};
int **p = arr; // 2个* 代表二级指针,
printf("%d, %d, %d\n", **(parr+0), **(parr+1), **(parr+2)); // 16, 932, 100
补充 指针数组的scan赋值操作
c
int ids[10];
char *names[10]={"","","","","","","","","",""};
for (int i = 0; i < 10; ++i){
scanf("%d,%s",&ids[i],(names+i));//names+i 就是数组里的每一个指针
}
for (int i = 0; i < 10; ++i){
printf("%d,%s\n" ,ids[i],(names+i));
}
二维指针
指向二维数组的指针
c
int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
函数指针
就是讲函数以指针的类型保存起来,方便后续调用 实用场景就是 回调函数
c
int max(int a,int b){
return a>b?1:0;;
}
void main(){
//返回值类型 指针名称 参数类型
int (*pmax)(int, int) = max;
// 括号不能省略
}
返回值为指针类型
c
char *strlong(char *str1, char *str2){
if(strlen(str1) >= strlen(str2)){
return str1;
}else{
return str2;
}
}
结构体
类似于对象的操作
c
typedef struct user{
int id;//4
char name[1];//10
int age;//4
} User; // User 相当于取一个别名
void main(){
// 赋值操作,相当于new 了一个新的user
User user = {1,{},1};
// 调用属性
int id = user.id;
}
使用关键字 typedef 可以为类型起一个新的别名。typedef 的用法一般为:
c
typedef oldName newName;
typedef int INTEGER;
INTEGER a, b;
a = 1;
b = 2;
📌 如果想要在方法中去改变结构体的值,需要使用 结构体指针的操作
例如
c
int delete(Array *array,int index){
// 获取结构体指针中的值
array->next; // 类似于array.next
}
void main(){
....
// 需要传递地址过去
delete(&array,1);
}
文件
"r" | 以“只读”方式打开文件。只允许读取,不允许写入。文件必须存在,否则打开失败。 |
---|---|
"w" | 以“写入”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。 |
"a" | 以“追加”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。 |
"r+" | 以“读写”方式打开文件。既可以读取也可以写入,也就是随意更新文件。文件必须存在,否则打开失败。 |
"w+" | 以“写入/更新”方式打开文件,相当于w 和r+ 叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。 |
"a+" | 以“追加/更新”方式打开文件,相当于a和r+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。 |
运算顺序
优先级 | 运算符 | 结合律 | |
---|---|---|---|
1 | 后缀运算符:[] () · -> ++ --(类型名称) | 从左到右 | |
2 | 一元运算符:++ -- ! ~ -(负号运算符) * & sizeof_Alignof | 从右到左 | |
3 | 类型转换运算符:(类型名称) | 从右到左 | |
4 | 乘除法运算符:* / % | 从左到右 | |
5 | 加减法运算符:+ - | 从左到右 | |
6 | 移位运算符:<< >> | 从左到右 | |
7 | 关系运算符:<<= >>= | 从左到右 | |
8 | 相等运算符:== != | 从左到右 | |
9 | 位运算符 AND:& | 从左到右 | |
10 | 位运算符 XOR:^ | 从左到右 | |
11 | 位运算符 OR: | 从左到右 | |
12 | 逻辑运算符 AND:&& | 从左到右 | |
13 | 逻辑运算符 OR: | 从左到右 | |
14 | 条件运算符:?: | 从右到左 | |
15 | 赋值运算符: | ||
= += -= *= /= %= &= ^= | = | ||
<<= >>= | 从右到左 | ||
16 | 逗号运算符:, | 从左到右 |
格式化输出
格式控制符 | 说明 |
---|---|
%c | 输出一个单一的字符 |
%hd、%d、%ld | 以十进制、有符号的形式输出 short、int、long 类型的整数 |
%hu、%u、%lu | 以十进制、无符号的形式输出 short、int、long 类型的整数 |
%ho、%o、%lo | 以八进制、不带前缀、无符号的形式输出 short、int、long 类型的整数 |
%#ho、%#o、%#lo | 以八进制、带前缀、无符号的形式输出 short、int、long 类型的整数 |
%hx、%x、%lx | |
%hX、%X、%lX | 以十六进制、不带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字也小写;如果 X 大写,那么输出的十六进制数字也大写。 |
%#hx、%#x、%#lx | |
%#hX、%#X、%#lX | 以十六进制、带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字和前缀都小写;如果 X 大写,那么输出的十六进制数字和前缀都大写。 |
%f、%lf | 以十进制的形式输出 float、double 类型的小数 |
%e、%le | |
%E、%lE | 以指数的形式输出 float、double 类型的小数。如果 e 小写,那么输出结果中的 e 也小写;如果 E 大写,那么输出结果中的 E 也大写。 |
%g、%lg | |
%G、%lG | 以十进制和指数中较短的形式输出 float、double 类型的小数,并且小数部分的最后不会添加多余的 0。如果 g 小写,那么当以指数形式输出时 e 也小写;如果 G 大写,那么当以指数形式输出时 E 也大写。 |
%s | 输出一个字符串 |
取值范围
类型名称 | 字节数 | 取值范围 |
---|---|---|
char | 1 | -128~127 |
short int 或 short | 2 | -32768~32767 |
int | 4 | -2147483648~2147483647 |
long int 或 long | 4 | -2147483648~2147483647 |