C语言学习笔记
本人不是计算机相关专业,专业选修C讲的内容不多,更多的是我的课外学习,如有问题欢迎指出
C介绍
c语言一种通用高级语言,有丹尼斯·里奇在贝尔实验室为开发UNIX操作系统而设计
c的优点
执行效率高,可移植性好,可用来开发应用软件,驱动,操作系统
其他高级语言的底层
环境
C语言是编译型语言,编写的代码是源码,计算机无法直接执行,需要使用编译器进行编译才能被计算机执行
编写运行C程序则需要编辑器和编译器,如vscode和mingw
大学常见的vc6则是IDE(集成开发环境),将编辑器,编译器,调试器,图形化界面进行了集成 (vc6不好使,可以去试试codeblock,或者喜欢轻量化试试vscode)
C结构
-
头文件
- 预处理命令,通知编译器在编译之前的预处理工作
-
主函数 有且只有一个
-
函数 实现代码逻辑的最小单元
最新的C标准中,main函数前的类型为int而不是void
-
语句
-
注释
#include <stdio.h> //头文件
int main(){ //主函数
print("Hello Word!") //函数体
return 0;
}
基础
变量
实质是内存中的一块区域,变量名中存储地址值,内存地址中储存真正的数据
int a=0;
//a --> OxF98A
//OxF98A --> 0
定义方式 数据类型 变量名
不允许连续赋值
int a ;
int b=1;
int a,b,c=3;
//int a=b=2; 不合法
数据类型
- 基本类型
- int
- char ‘’
- float/double
- 构造类型
- 枚举
- 数组
- 指针类型
-
- 空类型
- null
C没有字符串变量,字符串只能存在字符数组中
格式化输出
printf()格式化输出函数,在stdio头文件中
占位符
- %d 带符号的十进制数
- %c 单个字符
- %s 字符串
- %f 6位小数
转义字符:
- \b 退格
- \n
- ?
- \0 空字符null
流程控制
略
数组
存储数据的容器,C中是一块连续的,大小固定并且数据类型一致的内存空间,其中元素按照一定顺序放置
声明:
/*
类型 数组名[元素个数]
int a[]; 4个字节
char b[]; 1个字节
double c[]; 8个字节
*/
int arr[5];
int arr[ ]={2,45,43,2};
注:数组无法动态定义,数组的大小和初始值同时定义主义匹配
int arr[2]={1,2,3};
int n=2;
int arr[n];
//这两种情况均不合法
访问方式
数组名[下标]
int arr[ ]={1,24,54,1223,22};
//遍历
for(int i=0;i<5;i++){
printf("%d\t",arr[i]);
}
需要避免出现数组越界的错误,C没有检测机制可能会编译通过但结果会出error
C数组长度固定无法改变,C没有提供计算数组长度的方法
可以用如下方式获取长度
int length=sizeof(arr)/sizeof(arr[0]);
//sizeof可查看数占用多大的内存块
数组的初始化
- 将数组所欲元素初始化为0
- 直接赋值
- 部分赋值,未赋值的元素自动初始化0
字符串
C中没有直接的字符串数据类型,需要使用字符数组去实现
例 字符串 Hello
char str[6]={'H','e','l','l','0','\0'};
// 结尾的 \0 代表结尾,读到此处停止
根据数组初始化规则,上面语句还可以改写为
char str[]="Hello"
操作字符的函数 string头文件中
- strcpy(s1,s2); 复制字符串s2到s1中
- strcat(s1,s2)链接字符串s2到s1中
- strlen(s1) 字符串s1的长度
例
#include <stdio.h>
#include <string.h>
int main(){
char a[]="hello";
printf("%s\n",a);
char b[]="world";
strcat(a,b);
printf("%s\n",a);
printf("%s\n",b);
return 0;
}
/*
结果为
hello
helloworld
world
*/
自定义函数
定义方式 [ ]内容可以不写
[数据类型] 函数名称 ([参数]){
//函数体
return ;
}
数据类型省略时默认int
尽量放在main之前,如果在main之后需要在使用前声明
[数据类型] 函数名称 ([参数]);
变量存储类别
根据生命周期划分,静态存储方式和动态存储方式
-
静态:是指在程序运行期间分配固定的存储空间的方式。静态存储区中存放了在整个程序执行过程中都存在的变量
-
动态:是指在程序运行期间根据需要进行动态的分配存储空间的方式。动态存储区中存放的变量是根据程序运行的需要而建立和释放的
存储类型
- auto
- static
- register(寄存器)
- 为提高效率允许把局部变量放在cpu寄存器
- extern(外部)
- 脱离函数依然可以使用
静态局部变量在整个程序运行期间不会被释放,定义时不赋值时会自动赋值为0或null
C的内存模型
四大区域
代码区:存在函数程序的二进制代码
数据区:全局变量;静态变量;常量
堆heap:有程序员分配和释放,C语言可通过动态内存分配(malloc)操控
栈stack:栈内存的大小一般由操作系统设定好,存放函数的参数值,局部变量值等,操作方式同数据结构中栈相同
指针
指针(pointer):变量地址,可以用取地址运算符&得到一个变量的地址
声明方式
数据类型 *name
例
int a=3;
int *p=&a;
// a变量名储存一个内存地址,内存地址内为真正的数据:整形数字3
// 通过&取址符将这个地址传递给*p
// 此时*p变量名的内存地址指向的内存空间中储存的是a变量的内存地址
注:变量名储存一个内存地址,内存地址内存放的才是真正的数据
当对指针操作时会操作指针指向的变量
int a=3;
int *p=&a;
*p=6;
print("%d %d %p",*p,a,*p);
// 6 6 0061FF24
操作数组
int arr[]={1,23,4,5,2};
int *p=arr //等价于 int *p=&(arr[0])
//arr[i] == p[i] ==*(p+i)
解引用运算符 * 的到一个指针变量指向的变量,
指针与数组
C语言中指针与数组关系非常密切,数组名就是指向数组首地址的指针变量,在访问数组时就有两种方法
- 数组名[下标]
- *(指针+N)
int c[]={1,2,4,5};
printf("%d,%d",c[0],*c);
上例的结果为 1,1
指针与二维数组
以int 数组为例
二维数组就是数组的数组,数组的元素是数组,根据指针和数组的关系可以理解为 数组名是指向二维数组首地址的指针,但他指向的不再是一个int数据,而是一个另一个数组。他的类型也不再是int类型指针,而是int类型数组指针。
例
int arr[3][4];
arr,arr[0]都是指向首地址的指针,对其解引用都可以获得第一个元素的值
*arr[0] == **arr == arr[0][0]
如果递增arr,arr的类型为二维指针(int数组指针),其大小是一维数组的大小,arr+1 便指向第二个一位数组
arr+1 == arr[1]
如果递增arr[0],arr的类型类一维指针(int指针),其大小是一个int的大小,arr[0]+1 便指向第一个一维数组的第二个元素
*(arr[0]+1) == arr[0][1]
结构体struct
用户定制类型,由程序员组合基本类型,形成所需的数据类型,
C
struct User{
char name[10];
int password;
int uid;
};
//一条语句,主义最后;
struct User user;
user.uid=22;
strcpy(user.name,"jerry");
//此处不能直接对字符串赋值,需要str中copy进行赋值
与指针配合时,用()提高 解引用优先级保证程序正常,也可使用 直接访问运算符 ->访问
struct *p;
p=&user;
(*p).name
p->name
//两者相同效果
共同体union
union User{
int id;
char name[256];
char password[256];
}
多个类型共存,但实际只储存其中一个,一般是最长的哪一个,节省空间,使用场景不多
声明,使用和结构体相同
枚举enum
enum Color{
RED,
BLUE,
WHITE
}
//约定枚举使用全大写命名
typedef 类型定义
通过typedef定义自己的类型,实际作用更贴近给变量自定义名称
例如
typedef int id;
id就是定义的类型,实际上就是int,用于增强代码的可读性
使用方法与基本类型相同
typedef int id;
id i =1;
动态内存分配
常见的定义形式
struct User user[30];
为静态内存分配,数组空间大小确定不可更改
为节省空间出现动态内存分配
其函数在malloc头文件中
例
int *p=(int *)malloc(sizeof(int)*5); //向计算机操作系统申请堆内存空间
//
p = (int *)realloc(sizeof(int)*10); //重新分配内存,有失败的可能
free(p) //使用完之后需要释放资源,否则可能会内存泄漏
头文件提供常用的函数
void *calloc(int num,int siez);
//内存中分配 num 个长度为 size 字节的内存空间,进行初始化,初始值为0
void *malloc(int num);
//在堆内存中分配 num 个字节的空间,会不初始化,其值是为指导
void *realloc(void *address,int newsize);
//重新分配 newsize 字节的内存,并拷贝address的内容
void free(void *address);
//释放 address 的内存空间
of(int)*5); //向计算机操作系统申请堆内存空间
//
p = (int *)realloc(sizeof(int)*10); //重新分配内存,有失败的可能
free§ //使用完之后需要释放资源,否则可能会内存泄漏
头文件提供常用的函数
void *calloc(int num,int siez);
//内存中分配 num 个长度为 size 字节的内存空间,进行初始化,初始值为0
void *malloc(int num);
//在堆内存中分配 num 个字节的空间,会不初始化,其值是为指导
void *realloc(void *address,int newsize);
//重新分配 newsize 字节的内存,并拷贝address的内容
void free(void *address);
//释放 address 的内存空间