Skip to content

目录

C语言笔记

视频

https://www.bilibili.com/video/BV1vs411n7TH

http://c.biancheng.net/c/105/

编译全过程

主要是分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+"以“写入/更新”方式打开文件,相当于wr+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。
"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输出一个字符串

取值范围

类型名称字节数取值范围
char1-128~127
short int 或 short2-32768~32767
int4-2147483648~2147483647
long int 或 long4-2147483648~2147483647