C语言
# hello word
// std 标准(standard)
// i 输入
// o 输出
#include <stdio.h>
//main 函数,约定程序入口,有且只能有一个
int main() {
//printf 是一个库函数,不带换行需要自己使用换行符
printf("Hello, World!\n");
//如果正常返回0,异常则非0
return 0;
}
# 输入/输出
# 输出
#include <stdio.h> // 包含标准输入输出库
int main() {
// 打印 整形
printf("%d \n", 1);
// 打印 字符
printf("%c \n", 'h');
// 打印 字符串(注意字符串要用双引号)
printf("%s \n", "hello world");
// 打印 float
printf("%f \n", 1.1f);
// 打印 double(注意格式说明符应为 %lf)
printf("%lf \n", 1.1); // 不需要加 lf 后缀,1.1 默认为 double 类型
return 0;
}
# 输入
需要在控制台输入
#include <stdio.h>
int a = 21;
int main(){
//在函数内
int a = 19;
printf("在控制台输入一个数:\n");
// &a 表示 变量a的地址
scanf("%d",&a);
//如果输入类型错误,将会打印出原来的值
printf("%d \n",a);
return 0;
}
# 示例
#include <stdio.h>
int main( )
{
int c;
printf( "示例1->请输入 :");
//读取一个字符
c = getchar( );
//输出用户输入的第一个字符。
putchar( c );
printf( "\n==================\n");
//循环读取并输出用户输入的字符,直到用户按下回车键('\n')或输入结束(EOF)。
while (((c = getchar()))!= '\n' && c != EOF){
printf( "%c \n",c);
}
char str[100];
printf( "示例2->请输入 :\n");
gets( str );
printf( "打印内容: ");
puts( str );
printf( "示例3->请输入 :\n");
// fgets 会保留换行符
fgets(str, sizeof(str), stdin);
printf( "打印内容: ");
puts( str );
printf( "示例3->请输入 :\n");
int i;
// 遇到空格停止读取
// 示例输入:ABC 1 -> 读取结果: ABC 和 1
scanf("%s %d", str, &i);
printf( "您输入了: %s %d ", str, i);
return 0;
}
# 数据类型
类型 | 描述 | 大小 | 空间 |
---|---|---|---|
char | 字符型 | 1 | 8 |
shore | 短整型 | 2 | 16 |
int | 整型 | 2 | 16 |
long | 长整型 , 4 (32 位)或 8 (64 位) | 4 | 32 (32 位)或 64 (64 位) |
long long | 长长整型 | 8 | 64 |
float | 单精度浮点型 | 4 | 32 |
double | 双精度浮点型 | 8 | 64 |
# 关键字
关键字 | 用途 |
---|---|
auto | 声明自动变量 |
extern | 声明变量或函数是在其它文件或本文件的其他位置定义 |
enum | 声明枚举类型 |
const | 声明只读变量 |
static | 声明静态变量 |
volatile | 说明变量在程序执行中可被隐含地改变 |
void | 声明函数无返回值或无参数,声明无类型指针 |
goto | 无条件跳转语句 |
struct | 声明结构体类型 |
union | 声明共用体类型 |
register | 声明寄存器变量 |
sizeof | 计算数据类型或变量长度(即所占字节数) |
typedef | 用以给数据类型取别名 |
signed | 声明有符号类型变量或函数 |
unsigned | 声明无符号类型变量或函数 |
char | 声明字符型变量或函数返回值类型 |
short | 声明短整型变量或函数 |
int | 声明整型变量或函数 |
long | 声明长整型变量或函数返回值类型 |
double | 声明双精度浮点型变量或函数返回值类型 |
float | 声明浮点型变量或函数返回值类型 |
switch | 用于开关语句 |
case | 开关语句分支 |
default | 开关语句中的"其它"分支 |
if | 条件语句 |
else | 条件语句否定分支(与 if 连用) |
do | 循环语句的循环体 |
while | 循环语句的循环条件 |
for | 一种循环语句 |
continue | 结束当前循环,开始下一轮循环 |
break | 跳出当前循环 |
return | 子程序返回语句(可以带参数,也可不带参数) |
# static
# 不使用static
下面代码打印10个2,因为局部变量在离开作用域的时候会被销毁
#include <stdio.h>
void test(){
int a= 1;
a ++;
printf("%d \n",a);
}
int main() {
int n =0;
while (n <10)
{
n++;
test();
}
}
# static 修饰局部变量
提示
static修饰局部变量的时候,不会在域被销毁
所以如下代码打印2~11
#include <stdio.h>
void test(){
//修饰局部变量
static int a= 1;
a++;
printf("%d \n",a);
}
int main() {
int n =0;
while (n <10)
{
n++;
test();
}
}
# static 修饰全局变量
示例1
const.c
int g_val = 2025;
main.c
#include <stdio.h>
extern int g_val;
void test(){
int a= g_val;
a++;
printf("%d \n",a);
}
int main() {
printf("全部变量 %d ",g_val);
int n =0;
while (n <10)
{
n++;
test();
}
}
打印 2026~2035
修改 const.c
如下
static int g_val = 2025;
则编译报错,因为:static在全局变量中的作用是限制该变量的链接属性,使得它只能在定义它的文件中使用,其他文件无法通过extern来引用
# typedef
提示
简化使用不方便的类型,可以理解为别名
示例1
#include <stdio.h>
#include <string.h>
typedef unsigned char str;
int main() {
str a [] = "Hello, World!";
printf("|%s| 的长度是: %d\n", a, strlen(a));
return 0;
}
示例2
#include <stdio.h>
typedef struct Node
{
int a;
struct Node* next;
} MytNode;
int main() {
struct Node a;
MytNode b;
printf("end");
return 0;
}
# register
::: node 建议编译器将某个变量存储在CPU的寄存器中,而不是存储在主内存中。
寄存器是CPU内部的高速存储单元,访问速度远远快于访问主内存(RAM)。
因此,使用 register 可以提高变量的访问速度,尤其是在频繁访问的情况下。 :::
#include <stdio.h>
int main() {
register int n =0;
while (n <10)
{
n++;
printf("%d \n",n);
}
}
# 表达式
用于计算变量、数据类型或表达式所占用的内存大小,单位是 字节(byte)
编译时计算,不会影响运行时的性能
# sizeof
#include <stdio.h>
int main() {
char str[50] = "hello";
printf("char =>%zu\n",sizeof(str));// 打印50
printf("%d \n",sizeof 1);
printf("char =>%zu\n",sizeof(char));
printf("short => %zu\n",sizeof(short));
printf("int => %zu\n",sizeof(int));
printf("long =>%zu\n",sizeof(long));
printf("long long =>%zu\n",sizeof(long long));
printf("float =>%zu\n",sizeof(float));
printf("double =>%zu\n",sizeof(double));
return 0;
}
# ,
#include <stdio.h>
int main() {
int a = 1;
int b = 2;
int c = 3;
printf("%d \n", (c=a+b,b=c+10,a=b-4));
return 0;
}
# ~
二进制取反
#include <stdio.h>
int main() {
printf("%d \n",~1); //输出结果 -2
printf("%d \n",~2); //输出结果 -3
printf("%d \n",~3); //输出结果 -4
printf("%d \n",~11); //输出结果 -12
printf("%d \n",~-11); //输出结果 10
printf("%d \n",~-4); //输出结果 3
return 0;
}
0001 取反 1110
有符合整数的表现形式是补码,正数的补码与原码相同,而负数的补码是其原码取反后加1
1110 最高位为1 是负数
1110 取反-> 0001 -> +1 -> 0010 = 2
所以 1110 为 -2的补码1110 = -2 0001 取反 //有符号值:0 为正数1 得到结果 ~-2 = 1
# 变量
# 全局变量
作用域,整个工程,生命周期为整个程序的生命周期
#include <stdio.h>
//在函数外
int a = 20;
int main(){
printf("%d \n",a); //输出结果 20
return 0;
}
# 局部变量
作用域,变量所在的局部区域,生命周期为:进入作用域开始,离开作用域结束
#include <stdio.h>
int main(){
//在函数内
int a = 20;
printf("%d \n",a); //输出结果 20
return 0;
}
注意
全局变了和局部变量相同是局部优先
#include <stdio.h>
int a = 21;
int main(){
//在函数内
int a = 19;
printf("%d \n",a); //输出结果 19
return 0;
}
# 常量
笔记
定义:
常变量是一个 不可修改的变量,在定义时必须初始化,且之后不能更改其值。
常变量本质上是一个变量,但它的值在运行时是固定的。
特点:
不可变性:常量值在定义后不能被修改。
直接替换:在编译时,常量的值通常会被直接替换到代码中(某些编程语言中)。
无存储空间:常量通常在编译时被处理,可能不会占用运行时的内存。
#include <stdio.h>
int main(){
printf("%d \n",1); //1 就是常量
//用常量声明
char arr [4] = {"123"};
printf("字符输出 : %s\n", arr);
return 0;
}
# const
定义:
常变量是一个 不可修改的变量,在定义时必须初始化,且之后不能更改其值。
常变量本质上是一个变量,但它的值在运行时是固定的。
特点:
- 不可变性:常变量的值在定义后不能被修改。
- 存储空间:常变量在内存中有存储空间。
- 运行时定义:常变量的值在运行时生效(与常量不同)
#include <stdio.h>
int main(){
//被 const 本质是变量,但是不能直接修改
const int b = 1;
//用常量声明
//char arr [b] = {2};//代码异常
printf("输出 =>%d ",b);
return 0;
}
# define 标识符常量
#include <stdio.h>
#define NAME "小明"
#define PLAY(name,event) ( name "在" event)
int main() {
printf("%s\n", PLAY(NAME, "写代码"));
return 0;
}
注意
注意最后不要带
;
宏定义中如果涉及字符串拼接,需要使用 "" 直接连接,而不是 +。
# 枚举常量
注意
枚举值本质上是整数,默认从 0 开始递增。
#include <stdio.h>
enum Color {
RED,
GREEN,
BLUE,
};
int main(){
printf("color %d \n",RED);
printf("color %d \n",GREEN);
printf("color %d \n",BLUE);
return 0;
}
打印出
color 0
color 1
color 2
# 字符串
#include <stdio.h>
#include <string.h>
int main(){
char str [] = "12345";
char str2 [] = {'1','2','3','4','5','\0'};
//字符串本质是个数组 ,末尾用\0结束,\0不计入长度
printf("打印字符串1:%s => %d \n",str,strlen(str));
printf("打印字符串2:%s => %d \n",str2,strlen(str2));
//用常量声明
char arr [4] = {"123"};
printf("字符输出 : %s\n", arr);
char arr2 [3] = {"123"};
//因为123 长度是3 ,而arr2 最大长度也是3 ,不能正常存储\0字符,所以一下输出会错乱读取内存中的字符
printf("字符输出 : %s\n", arr2);
return 0;
}
# 数组
整数数组:必须使用 花括号 {} 进行初始化,不能使用方括号 []
数组下班从0开始
#include <stdio.h>
int main(){
//声明
int arr [4] = {1,2,3,4};
//遍历
for (int i = 0; i < 4; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
return 0;
}
# 选择结构 IF
#include <stdio.h>
int main(){
printf("1: C\n");
printf("2: Python\n");
printf("3: JAVA\n");
printf("请选择: \n");
int cmd =0;
scanf("%d",&cmd);
if(cmd == 1){
printf("开始学习C");
}else if (cmd == 2)
{
printf("开始学习Python");
}else{
printf("开始学习Java");
}
return 0;
}
# 循环结构 While
#include <stdio.h>
int main(){
int line = 0;
while (line < 10){
printf("开始学习 %d \n",line);
line++;
}
printf("学习结束\n");
return 0;
}
# 指针
指针是一个变量,用来存储另一个变量的内存地址。它的类型决定了它指向的变量类型。
- 一个 int 指针用来存储 int 类型变量的地址。
- 一个 char 指针用来存储 char 类型变量的地址。
# 指针的声明
声明指针时,需要在变量名前加上一个星号 *,语法如下:
#include <stdio.h>
type *pointer_name;
# 指针的使用
指针的主要用途是通过它访问或修改其指向的变量的值。
- &:取地址运算符,用于获取变量的地址。
- *:解引用运算符,用于访问指针指向的变量的值。
#include <stdio.h>
int main() {
//定义变量
int a = 10;
//取地址
printf("%s %p \n","a的内存地址",&a);
//指针p记录a内存的内存地址
int *p = &a;
// printf %p 打印指针地址
printf("%s %p \n","指针p地址",*p);
// *:解引用运算符
// 通过指针 p 修改 a 的值
*p = 20;
printf("修改后,变量 a 的值: %d\n", a);
return 0;
}
# 指针的类型
指针的类型必须与其指向的变量的类型一致。例如:
- int 指针只能指向 int 变量。
- char 指针只能指向 char 变量。
#include <stdio.h>
int main() {
int a = 10;
int *p = &a; // 正确:p 是 int 指针,指向 int 变量 a
float b = 3.14;
float *q = &b; // 正确:q 是 float 指针,指向 float 变量 b
// 这里编译错误:p 是 int 指针,不能指向 float 变量
p = &b;
return 0;
}
# 指针的运算
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // p 指向 arr[0]
int val = *p;
printf("%s %d \n", "目前指针*p的值",val);
p++; // p 现在指向 arr[1]
val = *p;
printf("%s %d \n", "变化指针后*p的值",val);
return 0;
}
# 多级指针
指针也可以指向另一个指针,形成多级指针(如二级指针、三级指针等)。
#include <stdio.h>
int main() {
int a = 10;
int *p = &a; // p 是一个 int 指针,指向 a
int **pp = &p; // pp 是一个二级指针,指向 p
printf("a 的值: %d\n", a); // 输出 10
printf("通过 p 访问 a: %d\n", *p); // 输出 10
printf("通过 pp 访问 a: %d\n", **pp); // 输出 10
return 0;
}
# 指针的注意事项
- 野指针:未初始化或释放后未置空的指针,可能导致程序崩溃。
#include <stdio.h>
int main() {
printf("%s \n","开始");
// 未初始化,是野指针
int *p;
// 不可预测行为,可能破坏其他内存,这里不会崩溃
*p = 10;
int a = *p;
printf("%s %d \n","打印:",a);
return 0;
}
- 空指针:使用 NULL 来表示指针未指向任何有效地址。
#include <stdio.h>
int main() {
printf("%s \n","开始");
// p 是一个空指针
int *p = NULL;
//解空指针,程序崩溃
int a = *p;
printf("%s %d \n","打印:",a);
return 0;
}
# struct
结构体的内存大小是其所有成员的大小的总和,但可能会因为内存对齐而有所不同。
#include <stdio.h>
#include <string.h>
struct Person {
char name[50];
int age;
float height;
} ;
int main() {
struct Person person1;
strcpy(person1.name, "John");
person1.age = 25;
person1.height = 1.75;
}
可以在声明时直接初始化结构体变量。
#include <stdio.h>
struct Person {
char name[50];
int age;
float height;
} ;
int main() {
struct Person person2 = {"Alice", 30, 1.65};
}
结构体可以作为函数的参数传递。
#include <stdio.h>
struct Person {
char name[50];
int age;
float height;
} ;
void printPerson(struct Person p) {
printf("Name: %s\n", p.name);
printf("Age: %d\n", p.age);
printf("Height: %.2f\n", p.height);
}
int main() {
struct Person person = {"Alice", 30, 1.65};
printPerson(person);
}
可以使用指针来访问结构体变量,通过指针访问结构体成员时需要使用 -> 运算符。
#include <stdio.h>
struct Person {
char name[50];
int age;
float height;
} ;
void printPerson(struct Person p) {
printf("Name: %s\n", p.name);
printf("Age: %d\n", p.age);
printf("Height: %.2f\n", p.height);
}
int main() {
struct Person person1 = {"John", 25, 1.75};
struct Person *p = &person1;
printf("Name: %s\n", p->name);
printf("Age: %d\n", p->age);
printf("Height: %.2f\n", p->height);
//通过指针赋值
p-> height = 1.80;
printf("Height: %.2f\n", p->height);
}
如果不需要给结构体命名,可以使用匿名结构体。
#include <stdio.h>
#include <string.h>
struct {
char name[50];
int age;
} person1;
int main() {
person1.age = 30;
printf("Age: %d\n", person1.age);
struct {
char name[50];
} person2;
strcpy(person2.name, "小明");
printf("name: %s\n", person2.name);
}
使用 typedef 可以为结构体类型定义一个别名,从而简化代码。
#include <stdio.h>
typedef struct PP {
char name[50];
int age;
} Person;
void printPerson( struct PP p) {
printf("Name: %s\n", p.name);
printf("Age: %d\n", p.age);
}
int main() {
Person person = {"小米", 25};
printPerson(person);
struct PP person2 = {"小红", 25};
printPerson(person2);
}
# 位域
#include <stdio.h>
#include <string.h>
/* 定义简单的结构 */
struct{
unsigned int widthValidated; //不指定位域 长度4 ,32位
unsigned int heightValidated;
} status1;
/* 定义位域结构 */
struct{
// type [member_name] : width ;
// type 整数类型,决定了如何解释位域的值。类型可以是整型、有符号整型、无符号整型。
// member_name 位域的名称。
// width 位域(Bit Field 二进制)中位的数量。宽度必须小于或等于指定类型的位宽度。
unsigned int widthValidated : 1; //int 4字节 32位,设置位域1,及只能存储0和1
unsigned int heightValidated : 1;
} status2;
struct{
unsigned int age : 3; //int 4字节 32位,设置位域,及只能存储0~7
} Age;
int main( )
{
printf( "内存大小 size : %d\n", sizeof(status1));
printf( "内存大小 size : %d\n", sizeof(status2));
Age.age = 4;
printf( "Sizeof( Age ) : %d\n", sizeof(Age) );
printf( "Age.age : %d\n", Age.age );
Age.age = 7;
printf( "Age.age : %d\n", Age.age );
//二进制表示为 1000 有四位,超出
//8 二进制-> 1000
Age.age = 8;
printf( "Age.age : %d\n", Age.age );
return 0;
}
# union
共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( ){
union Data data;
//共用体占用的内存应足够存储共用体中最大的成员
printf( "Memory size occupied by data : %d\n", sizeof(data));
data.i = 66;
data.f = 66.66;
strcpy( data.str, "hello world");
//以下示例只会正常打印最后一个赋值的属性值
printf( "data.i : %d\n", data.i);
printf( "data.f : %f\n", data.f);
printf( "data.str : %s\n", data.str);
//以下示例只会正常打印每个属性值
data.i = 66;
printf( "data.i : %d\n", data.i);
data.f = 66.66;
printf( "data.f : %f\n", data.f);
strcpy( data.str, "hello world");
printf( "data.str : %s\n", data.str);
}
# 库
# string.h
# strlen
#include <stdio.h>
#include <string.h>
int main() {
char str[50] = "hello";
printf("|%s| 的长度是 |%d|\n", str, strlen(str));
return 0;
}
# 分支和循环预计
# 分支语句
# if
#include <stdio.h>
int main() {
int a= 6;
if(a==1){
printf("等于1");
}else if (a>2 && a<6)
{
printf("大于2小于6");
}
else{
printf("不等于1且大于等于6");
}
}
# switch
#include <stdio.h>
int main() {
int a= 1;
switch (a)
{
case 1:
printf("选择了1");
break;
case 2:
printf("选择了2");
break;
case 3:
printf("选择了3");
break;
default:
printf("默认选择: %d \n",a);
break;
}
}
# 循环语句
# while
while 循环:只有满足while里的条件时才会执行
break是用于永久的终止循环
continue 跳过本次循环后面的代码,直接去判断部分,进行下一次循环的判断
示例
#include <stdio.h>
int main() {
int a= 0;
while (a<10)
{
//如果为5跳过后续代码
a++;
if(a == 5){
printf("跳过:%d \n",a);
continue;
}
printf("当前:%d \n",a);
}
}
输出
当前:1
当前:2
当前:3
当前:4
跳过:5
当前:6
当前:7
当前:8
当前:9
当前:10
# do-while
示例1
#include <stdio.h>
int main() {
int a= 0;
do{
a++;
//如果为5跳过后续代码
if(a == 5){
printf("跳过:%d \n",a);
continue;
}
//如果大于10结束循环
if(a > 10){
printf("结束:%d \n",a);
break;
}
printf("当前:%d \n",a);
//不等于0的时候执行
}while (a!=0);
}
输出
当前:1
当前:2
当前:3
当前:4
跳过:5
当前:6
当前:7
当前:8
当前:9
当前:10
结束:11
示例2
更换条件while (a==0);
#include <stdio.h>
int main() {
int a= 0;
do{
a++;
//如果为5跳过后续代码
if(a == 5){
printf("跳过:%d \n",a);
continue;
}
//如果大于10结束循环
if(a > 10){
printf("结束:%d \n",a);
break;
}
printf("当前:%d \n",a);
//不等于0的时候执行
}while (a==0);
}
输出
当前:1
提示
do-while循环:无论是否满足while里的条件都会执行一次
# for
#include <stdio.h>
int main() {
//size_t 是无符号的,这意味着它不能表示负数。它的取值范围从 0 开始,一直到它能表示的最大正整数。
for (size_t i = 0; i < 10; i++)
{
printf("打印:%d \n",i);
}
}
# 函数
# 函数调用
#include <stdio.h>
int data() {
printf("Hello, World!\n");
return 0;
}
int main() {
//printf 是一个库函数,不带换行需要自己使用换行符
printf("Hello, World!\n");
//如果正常返回0,异常则非0
return data();
}
# 函数声明
笔记
如果函数调用在实现之前,需要在调用前申明
#include <stdio.h>
//调用前申明
int data();
int main() {
//printf 是一个库函数,不带换行需要自己使用换行符
printf("Hello, World!\n");
//如果正常返回0,异常则非0
return data();
}
int data() {
printf("Hello, World!\n");
return 0;
}
# 头文件
hello.h
#ifndef HELLO_H // 如果 HELLO_H 未定义
#define HELLO_H // 定义 HELLO_H
int hello(char* name );
#endif // 结束条件编译
hello.c
#include <stdio.h>
int hello(char* name ){
printf("你好啊! %s \n",name);
}
test.c
// <> 只在 标准路径 或 系统路径 中查找头文件。
#include <stdio.h>
// "" 先在 当前源文件所在的目录 中查找,找不到再去 标准路径 查找。
#include "magic.h"
int main() {
hello("小明");
}
# 函数传参
真实传给函数的参数,叫实参。
实参可以是:常量、变量、表达式、函数等
无论实参是何种类型的量,在进行的数调用时,它们都必须有确定的值,以便把这些值传送给形参
形式参数是指函数名后括号中的变量
因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。
形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效
#include <stdio.h>
//当实参传递给形参的时候,形参是实参的一份临时拷贝//对形参的修改不能改变实参
void swap(int a ,int b ){
int c;
c = a;
a = b;
b = c;
}
//指针传递的是内存地址,可以有效变更值
void swap2(int* a ,int* b ){
int c = 0;
c = *a;
*a = *b;
*b = c;
}
int main() {
int a =2,b=1;
printf("初始化 A:%d B:%d \n",a,b);
//传值调用 因为形参的原因,函数内外无联系
swap(a,b);
printf("交换1 A:%d B:%d \n",a,b);
//传址调用 因为形参类型是指针指向真实参数,函数内外有联系
swap2(&a,&b);
printf("交换2 A:%d B:%d \n",a,b);
}
函数传递的是形参
输出
初始化 A:2 B:1
交换1 A:2 B:1
交换2 A:1 B:2
# 函数递归
对于数字 123456,依次打印每一位
#include <stdio.h>
void print_num(unsigned int num){
if(num > 9 ){
print_num(num /10);
}
printf("%u",num%10);
}
int main() {
unsigned int num = 12234;
print_num(num);
}
编写函数不允许创建临时变量,求字符串的长度。
#include <stdio.h>
int my_strlen(char* name){
if(*name == '\0'){
return 0;
}
name++;
return 1+my_strlen(name);
}
int main() {
printf("字符长度 %u",my_strlen("123456789"));
}
# 函数迭代
求第N个 斐波那契数列
#include <stdio.h>
int fib(int n ){
int n1 =0;
int n2 =1;
int sum =0;
for (size_t i = 1; i <=n; i++)
{
// n1 n2
//1 0 1
//2 1 (0+1)
//3 (0+1) (1 + (0+1))
//5 (1 + 0+1) (0+1)+(1 + (0+1))
sum = n1+n2;
n1 = n2;
n2 = sum;
}
return sum;
}
int fib2(int n ){
int n1 =1;
int n2 =2;
if(n == 1){
return n1;
}
if(n == 2){
return n2;
}
int sum =0;
for (size_t i = 3; i <=n; i++)
{
sum = n1+n2;
n1 = n2;
n2 = sum;
}
return sum;
}
int main() {
//1 2 3 5 8 13 21
printf("fib :%u \n",fib(6));
printf("fib2 :%u \n",fib2(7));
}
# 函数指针
#include <stdio.h>
//将指针函数作为参数传递 (回调接口)
int max(int a ,int b,void (*log)(int,int)){
log(a,b);
return a>b?a:b;
}
void print_info(int a,int b){
printf("比较 %u 和 %u \n",a,b);
}
int main() {
int a = 3;
int b = 4;
//指针函数 max 本身指向一个地址 可省略 &
// int (*fun)(int,int,void (*log)(int,int)) = &max;
int (*fun)(int,int,void (*log)(int,int)) = max;
//print_info 同理 可省略 print_info
printf("最大值%u \n",fun(fun(a,10,&print_info),b,print_info));
}
# 函数可变参数
#include <stdio.h>
//提供了一组宏来支持可变参数的功能
#include <stdarg.h>
// 定义一个函数,计算固定数量整数的平均值
/**
* count 可变参数数量 为了方便提取
* param 在示例中为普通固定参数
* ... 可变参数
*/
double print_args(int count,char *param, ...) {
va_list args;
//va_start(va_list ap, last_fixed_arg);
//第一个参数是 va_list
//第二个参数是 最后一位固定参数,这里是 name
va_start(args, param);
int arg_arry [3];
for (int i = 0; i < count; i++) {
//va_arg 提取一个参数,并移动 va_list 指针至下一位
arg_arry[i] = va_arg(args, int);
printf("%s 第%d个参数值为:%d \n",param,i+1,arg_arry[i]);
}
//清理va_list,结束可变参数的获取。
va_end(args);
}
int main() {
print_args(3,"TEST", 10, 20, 30);
return 0;
}
# 数组
# 一维数组
#include <stdio.h>
int main() {
int array [10] = {1,2,3};
printf("占用 %d \n",sizeof(array));
printf("长度 %d \n", sizeof(array) / sizeof(array[0]));
printf("打印 %d \n",array[0]);
printf("打印 %d \n",array[1]);
printf("打印 %d \n",array[2]);
printf("======================\n");
//变长数组(VLA)是 C99 标准引入的特性,确保你的编译器支持 C99 或更高版本。
int n = 3;
//变长数组不能直接初始化
int array2 [n];
array2[0] = 4;
array2[1] = 5;
array2[2] = 6;
printf("占用 %d \n",sizeof(array2));
printf("长度 %d \n", sizeof(array2) / sizeof(array2[0]));
printf("打印 %d \n",*array2);
printf("打印 %d \n",array2[0]);
printf("打印 %d \n",array2[1]);
printf("打印 %d \n",array2[2]);
printf("======================\n");
//如果不指定长度,实际长度为内容长度
char array3 [] = {'A','B','C'};
printf("占用 %d \n",sizeof(array3));
printf("长度 %d \n", sizeof(array3) / sizeof(array3[0]));
printf("打印 %c \n",*array3);
printf("打印 %c \n",array3[0]);
printf("打印 %c \n",array3[1]);
printf("打印 %c \n",array3[2]);
}
# 二维数组
#include <stdio.h>
int main() {
//二维数组创建
int array [3][3] = {1,2,3,4,5,6,7,8,9};
//二维数组理解成一个内容是数组的一维数组,*array 得到第一个数组,**array 得到第一个数组的第一个内容
printf("打印 %d \n",**array);
printf("打印 %d \n",array[1][2]);
printf("======================\n");
int array2 [2][3] = {{1,2,3},{4,5,6},{7,8,9}};
printf("打印 %d \n",**array2);
printf("打印 %d \n",array2[1][2]);
printf("======================\n");
int array3 [3][3] = {1,2,3,4,5,6,7};
printf("打印 %d \n",**array3);
printf("打印 %d \n",array3[1][2]);
printf("======================\n");
int array4 [][3] = {1,2,3,4,5,6,7};
printf("打印 %d \n",**array4);
printf("打印 %d \n",array4[1][2]);
printf("======================\n");
//二维数组可以省略行,不能省略列
// int array5 [2][] = {1,2,3,4,5,6,7};
}
# 三维数组
public class Object extends 余胜军{
}
# IO文件读取
打开
FILE *fopen( const char * filename, const char * mode );
关闭
int fclose( FILE *fp );
写入字符
int fputc( int c, FILE *fp );
写入字符串
int fputs( const char *s, FILE *fp );
int fprintf(FILE *fp,const char *format, ...)
模式 | 描述 |
---|---|
r | 打开一个已有的文本文件,允许读取文件。 |
w | 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。 |
a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。 |
r+ | 打开一个文本文件,允许读写文件。 |
w+ | 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。 |
a+ | 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 |
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
#include <stdio.h>
int main( )
{
FILE *f= fopen("test.txt","w+");
if (f == NULL) {
printf("文件不存在或打开失败\n");
return 1;
}
fprintf(f,"hello world %s","小明");
char *content = "\n这是内容";
fputs( content, f );
//重置指针开始读取
rewind(f);
// char * buffer;
char buffer [100];
//scan形式读取 遇到第一个空格停止读取
fscanf(f, "%s", buffer);
printf("%s\n", buffer );
//重置指针 确保能完整读取
rewind(f);
//每次读取一行,直到全部读完
while (fgets(buffer, sizeof(buffer), f) != NULL) {
printf("%s", buffer);
}
fclose(f);
}
# 预处理器
指令 | 描述 |
---|---|
#define | 定义宏 |
#include | 包含一个源代码文件 |
#undef | 取消已定义的宏 |
#ifdef | 如果宏已经定义,则返回真 |
#ifndef | 如果宏没有定义,则返回真 |
#if | 如果给定条件为真,则编译下面代码 |
#else | #if 的替代方案 |
#elif | 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码 |
#endif | 结束一个 #if……#else 条件编译块 |
#error | 当遇到标准错误时,输出错误消息 |
#pragma | 使用标准化方法,向编译器发布特殊的命令到编译器中 |
预设宏
宏 | 描述 |
---|---|
DATE | 当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。 |
TIME | 当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。 |
FILE | 这会包含当前文件名,一个字符串常量。 |
LINE | 这会包含当前行号,一个十进制常量。 |
STDC | 当编译器以 ANSI 标准编译时,则定义为 1。 |
// 带< 从系统库中获取
#include <stdio.h>
#include <string.h>
// 带 "" 在当前目录(即 .c 文件所在目录)查找,如果找不到:回退到 系统默认路径(和 <header.h> 一样)
#include "magic.h"
// 定义常量。
#define MAX_CONNECT 100
//取消 MAX_CONNECT 定义
#undef MAX_CONNECT
//重新定义
#define MAX_CONNECT 100
#define SYSTEM_NAME "LINUX"
//SYSTEM_NAME 平台
#if !defined (SYSTEM_NAME)
#define SYSTEM_NAME "WINDOWS"
#endif
//windows 平台 1 是
#define PLATFORM_WINDOWS 1
//不是 linux 平台 0 否
#define PLATFORM_LINUX 0
//如果不存在 ENV 变量 ,设置为 dev
#ifndef ENV
#define ENV "dev"
#endif
//设置debug参数
#define DEBUG 1
//如果有Debug参数
#ifdef DEBUG
#define USER "TEST"
#endif
//如果有Debug参数
#if defined (DEBUG)
// \ 宏延续运算符:一个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用(\)
#define my_log(user,msg) \
printf("log [%s]:%s %s %s %d %d : %s\n",user, __FILE__ ,__DATE__,__TIME__,__LINE__,__STDC__,msg);
#else
#define my_log(user,msg)
#endif
// # 字符串常量化运算符: 当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#)
#define env_info(a,b) my_log(USER,(#a ":" b))
// 标记粘贴运算符(##): 会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。例如:
#define os_info(a) \
char buffer[100]; \
snprintf(buffer, sizeof(buffer), "当前系统[%s]: %d\n", #a, PLATFORM_##a); \
my_log(USER, buffer);
int main(){
//打印 ENV:dev
env_info(ENV,ENV);
my_log(USER,"当前是debug环境");
#if PLATFORM_WINDOWS == 2
os_info(WINDOWS);
#elif PLATFORM_LINUX
os_info(LINUX);
#else
my_log(USER,"未知平台");
#endif
printf("当前环境%s \n",ENV);
printf("最大连接数:%d \n",MAX_CONNECT);
return 0 ;
}
# 强制类型转换
int
-> unsigned int
-> long
-> unsigned long
-> long long
> unsigned long long
> float
> double
> long double
#include <stdio.h>
int main()
{
int sum = 17, count = 5;
double mean;
mean = (double) sum / count;
printf("Value of mean : %f\n", mean );
}
# 错误处理
打印错误
//提供了输入输出功能,如fprintf和perror
#include <stdio.h>
int main ()
{
FILE * pf;
pf = fopen ("unexist.txt", "rb");
if (pf == NULL){
perror("通过 perror 输出错误");
}
else
{
fclose (pf);
}
return 0;
}
记录错误
//提供了输入输出功能,如fprintf和perror
#include <stdio.h>
//提供了errno 全局变量,用于存储错误号
#include <errno.h>
// 提供了字符串处理函数,如 strerror
#include <string.h>
extern int errno ;
int main ()
{
FILE * pf;
//声明了一个整数变量errnum,用于存储错误号。
int errnum;
pf = fopen ("unexist.txt", "rb");
if (pf == NULL){
//如果文件打开失败,将errno的值赋给errnum,以便稍后使用。
errnum = errno;
//使用fprintf将错误号输出到标准错误流(stderr)。
fprintf(stderr, "错误号: %d\n", errno);
//使用perror函数输出与errno相关的错误信息。perror会自动在输出信息前加上传入的字符串。
perror("通过 perror 输出错误");
//使用strerror函数将错误号转换为错误信息字符串,并通过fprintf输出到标准错误流。
fprintf(stderr, "打开文件错误: %s\n", strerror( errnum ));
}
else
{
fclose (pf);
}
return 0;
}
# 退出
#include <stdio.h>
#include <stdlib.h>
int main()
{
int dividend = 20;
int divisor = 5;
int quotient;
if( divisor == 0){
fprintf(stderr, "除数为 0 退出运行...\n");
exit(EXIT_FAILURE);
}
quotient = dividend / divisor;
fprintf(stderr, "quotient 变量的值为: %d\n", quotient );
//正常退出 0
exit(EXIT_SUCCESS);
}
# 内存管理
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char name[100];
strcpy(name, "小明");
printf("Name = %s \n", name );
char *description;
/* 动态分配内存 */
description = malloc(3);
if( description == NULL ){
fprintf(stderr, "无法分配需要的内存\n");
}
else{
//写入内存
strcpy( description, "12");
printf("Description: %s \n", description );
//调整内存大小
description = realloc(description,11);
if( description == NULL ){
fprintf(stderr, "无法分配需要的内存\n");
free(description);
exit(1);
}
//重新写入
strcpy( description, "1234567890");
printf("Description: %s \n", description );
}
printf("释放内存\n");
free(description);
//由于内存被释放,这里打印错误的值
printf("Description: %s \n", description );
return 0 ;
}
# 命令行参数
#include <stdio.h>
//argc 是指传入参数的个数,
//argv[] 是一个指针数组,
int main(int argc, char *argv[]) {
// p 指向 argv 数组的首元素(即 argv[0])
char **p = argv;
// *p 是 argv[i] 的字符串(即第 i 个参数的字符串)
// *p + 3 是 argv[i] 字符串的第 4 个字符的地址(索引从 0 开始)
// *(*p + 3) 是 argv[i][3] 的值(即第 4 个字符)
printf("argv[n] = %s -> index[3]:%c\n", *p, *(*p + 3));
// 指针++操作,p 指向 argv[1]
p++;
// *p 是 argv[1] 的字符串
// **p 是 argv[1][0] 的值(即第 1 个参数的首字符),等效写法 *(*p),*p[0]
printf("argv[n] = %s -> index[0]:%c\n", *p, **p);
// 指针++操作,p 指向 argv[2]
p++;
// *p 是 argv[2] 的字符串
// **p 是 argv[2][0] 的值(即第 2 个参数的首字符),等效写法 *(*p),*p[0]
printf("argv[n] = %s -> index[0]:%c\n", *p, **p);
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}