文章目录
前言 :建议加上宏定义:允许使用过时的函数和不安全的函数,去掉警告
#define _CRT_SECURE_NO_WARNINGS
此篇文章为笔记型文章。方便笔者日后快速复习~~
导包语法:#include <xxx.h>
常用包:
- stdio 标准函数库
- stdlib 标准函数库(system、sizeof等等…)
- Windows windows系统的api
函数入口:
//导入标准函数库(类似于java的导包)
#include <stdio.h>
//导入标准函数库(system、sizeof等等......)
#include <stdlib.h>
//windows系统的api
#include <Windows.h>
void main(){
//输出函数
printf("hello world!");
//这是一个命令(在window里面特有的)
system("pause");
}
基本数据类型(int char float double long int short)
占位符:
%d - int
%ld – long int
%c - char
%x - short
%f - float
%u – 无符号数
%hd – 短整型
%lf – double
%x – 十六进制输出 int 或者long int 或者short int
%#X - 显示十六进制
%o - 八进制输出
%s – 字符串
输入输出函数:
//输出函数
printf("请输入一个整数:");
//输入函数(注意:输入函数赋值需要的是地址,而不是值)
//scanf("%d",&i);
//注意:不同编译工具,对函数支持不一样
scanf_s("%d", &i);
内存地址&指针:
int i = 100;
//输出i的值
printf("i的值:%d\n",i);
//输出i的地址
//&i:取出i变量的地址
//注意:输出地址,占位符%p
printf("i的地址:%p\n",&i);
//根据地址获取值:*(&i)
//总结:&表示取地址 *表示根据地址取值
printf("根据i的地址取出i的值%d\n",*(&i));
//什么是变量名:就是对内存的一段空间里的数据的抽象
//0x001CFDD0
多级指针
int i= 99;
//打印i的值
printf("%d\n",i);
//打印i的地址
printf("%p\n", &i);
//定义一个指针变量
//int* 是一个int类型的指针变量,可以存储一个int类型的变量地址
int* p = &i;
printf("指针变量p的值:%p\n",p);
//输出指针的地址
printf("指针变量p的地址:%p", &p);
//直接赋值
i = 200;
printf("变量i的值:%d\n", i);
//间接赋值(通过指针变量)
*p = 500;
printf("指针变量赋值后的i的值:%d\n", i);
通过指针学习,可以在方法中传递指针地址,在方法内部通过指针地址变量操作真实数据。
方法形参若为真实变量形参,例如:int a; 则会在方法内为a开辟一个新内存。修改a的值并不能修改原始值。
指针和地址的区别:指针有类型,地址没有类型。
指针需要通过类型判断开辟多大内存空间,例如int 开辟4字节。char开辟两字节。
指针必须要有初始值。可以为NULL,但是不能不赋值。否则会报错
多级指针另一种写法:int** p2 = &p1;
p2保存的数据是p1地址,*p2上的数据是p1上面数据, p1保存的数据是a的地址,**p2实际上就是a的值
数组
数组遍历
总结一:ids常量指针,存储的是数组的首地址
总结二:数组在内存中排列是连续不断(线性排列)
int ids[] = {23,15,67,38,99,70};
//打印数组
//printf("数组:%#x\n", ids);
//数组第一个元素地址
//printf("数组第一个元素地址:%#x\n",&ids[0]);
//总结:p++每次向前移动sizeof(数据类型)个字节
int* p = ids;
//p++;
//printf("地址:%#x 值:%d", p, *p);
//遍历数组
//传统方式
//int i = 0;
//for (; i < 6; i++){
//printf("值:%d\n", ids[i]);
//}
//通过指针遍历
for (; p < ids + 6; p++){
printf("地址:%#x 值:%d\n",p, *p);
}
//注意:让指针递增或者递减,一般情况下只有在数组遍历的时候才有意义,
//基于数组在内存中是线性排列的方式(连续不断)
数组赋值
int ids[6];
int* p = ids;
//传统写法(高级写法)
//赋值
//int i = 0;
//for (; i < 6; i++)
//{
//ids[i] = i;
//}
//指针方式赋值
int i = 0;
for (; p < ids + 6; p++){
*p = i;
i++;
}
数组遍历于指针关系分析
int ids[] = { 23, 15, 67, 38, 99, 70 };
int i= 0;
for (; i < 6; i++){
//输出-高级写法(常规写法)
//printf("%d %#x\n", ids[i], &ids[i]);
//之前分析了ids就是常量指针,就是一个首地址
//以下分析是取地址
//分析:ids是首地址
//ids+0 等价于 &ids[0]
//ids+1 等价于 &ids[1]
//以此类推......
//总结:ids+i 等价于 &ids[i]
//分析取值
//*(ids+0) 等价于 ids[0]
//*(ids+1) 等价于 ids[1]
//以此类推......
//总结:*(ids+i) 等价于 ids[i]
printf("%d %#x\n",*(ids+i),ids+i);
}
二维数组
//两行三列-二维数组
int ids[2][3] = { 23, 15, 67, 38, 99, 70 };
//遍历二维数组:外层循环控制行,内层循环控制列
int i = 0;
for (; i < 2; i++){
int j = 0;
for (; j < 3; j++){
printf("值:%d 地址:%#x ", ids[i][j],&ids[i][j]);
}
printf("\n");
}
二维数组推理分析:
//打印第二行,第二列
printf("%d\n",ids[1][2]);
//推导
//ids + 0 代表第一行 第一行的第一个值
//ids + 1 第二行(下一行) 第二行的第一个值
//以此类推:ids + i
//取每一行第一个元素的指针:*(ids+i)
//第一行第一个元素指针:*(ids+0)
//第二行第一个元素指针:*(ids+1)
//以此类推:*(ids+i)
//继续推理
//取第一行第一个指针:*ids + 0
//取第一行第二个指针:*ids + 1
//取第一行第三个指针:*ids + 2
//以此类推:*ids + j
//继续推理
//取第一行第二个指针:*(ids+0)+1
//取第二行第二个指针:*(ids+1)+1
//以此类推:*(ids+i)+j(这个公式获取的是地址,是一个指针变量)
//*(*(ids+i)+j)
printf("%d", *(*(ids+1)+2));
指针比较
指针运算-指针大小比较
指针大小比较:都是在遍历数组的时候运用,其他情况下没什么用
指针相等比较:指针地址相等,并不代表值相等,因为指针有类型,例如:
int* p1 = (int*)0x006578;
double* p2 = (double*)0x006578;
函数指针
void showMessage(){
}
void add(int a, double b){
}
void main(){
//传统的写法
//直接调用
//showMessage();
//采用函数指针的方式调用
//void(*p)() = showMessage;
//p();
//带返回值和参数列表的函数指针
double(*p1)(double a, double b) = add;
double d = p1(2,3);
printf("%lf", d);
//补充:函数指针-不仅仅是地址,必须明确的指定函数的指针类型,以及返回值和参数列表
//回想Java语言:方法重载(方法名相同,参数列表不同,或者参数类型不同)
getchar();
}
动态内存分配
malloc 分配内存
calloc 分配内存(更方便)
int* p = (int*)malloc(5 * sizeof(int));
//第一个参数:数组长度
//第二个参数:每个元素大小
int* p = (int*)calloc(5,sizeof(int));
free 回收内存
free(p)
realloc 重新分配内存
int* p1 = (int*)realloc(p,sizeof(int)*(len+addLen));
注:新的=老的+增加的
realloc更改已经配置的内存空间
- 缩小:会导致一部分数据丢失
- 扩大(连续不断-线性排列)
- 情况一:如果当前的内存段后面有需要的内存空间,就会直接追加(注意:返回原指针)
- 情况二:如果当前内存段后面空闲的字节空间不够,就会重新再堆内存中寻找能够容纳该数据大小的内存区域(注意:返回值新的内存地址,原来的被释放)
- 情况三:如果没有容身之处,申请内存失败,将返回NULL,而且原来的指针有效
内存分配注意事项:
1、不能够多次释放内存
2、释放内存之后,给原来的指针设置NULL
3、内存泄漏
int* p = (int*)calloc(5, sizeof(int));
...一顿操作...
//释放内存
free(p);
//标记内存已经被释放
p = NULL;
如下会导致内存泄漏
//在堆区开辟了一块40M的内存空间
int* p = (int*)malloc(1024 * 1024 * 10 * 4);
p = NULL;
//回收内存
free(p);