[TOC]
# 定义
指针0-255是系统保留的,不能读也不能写,NULL是指向0的

无论什么类型的指针,存的都是地址
内存地址都是无符号整形的,所以都是4个字节大小
10是四个字节
`int*` 表示我如果要改的话,对应那边数据的四个字节
`char*` 的话那就是对应那边数据的1个字节
**指针里面只是地址,指针也是个变量**
~~~
int main(int argc, char *argv[])
{
int a = 10;
int b = 100;
//指针是一种数据类型,p是指针类型变量用来指向一个变量的地址
int * p = &a;
p = &b;
printf("%p\n", &b); //001CF878
printf("%X\n", p); //1CF878
//通过指针修改变量的值
*p = 200;
printf("%d\n", b); //200
printf("%d\n", *p); //200
//sizeof()指针类型在内存中的大小,所有指针在32位是4个字节大小
//按照unsigned int为每一个内存分配编号 2的32次方-1 所以32位操作系统最多用4g内存
printf("%d\n", sizeof(p)); //4
printf("%d\n", sizeof(int *)); //4
printf("%d\n", sizeof(char *)); //4
system("pause");
return EXIT_SUCCESS;
}
~~~
# 万能指针指定类型
~~~
int main(int argc, char *argv[])
{
int a = 10;
//指针里面存的是首地址
void * p = &a;
//转成指定类型,如果是char就转成char
*(int *)p = 100;
printf("%d\n", a); //100
printf("%d\n", *(int *)p); //100
system("pause");
return EXIT_SUCCESS;
}
~~~
如果是数组
~~~
int main(int argc, char *argv[])
{
int arr[10];
//首地址
void * p = arr;
//改变首地址的值也就是arr[0]
*(int *)p = 100;
//改变arr[1],因为里面存的是int,每个元素占用4个字节,但是已经转为int了,int+1就过了4个字节
*((int *)p + 1) = 200; //就是arr[1]
for (int i = 0; i < 10; i++)
{
printf("%d\n", arr[i]);
}
system("pause");
return EXIT_SUCCESS;
}
~~~
# const
`int * p`
`int * `是指针的指向
p是指针的地址值
## const修饰指针
const修饰变量是可以被修改的
~~~
int main()
{
//这种方式不安全,可以通过指针修改
const int a = 10;
printf("%d\n", a);
int * p = &a;
*p = 100;
printf("%d\n", a);
printf("%d\n", *p);
getchar();
return EXIT_SUCCESS;
}
~~~
建议用define
~~~
#define LVL 100
~~~
## const修饰指针类型
~~~
int main()
{
int a = 10;
int b = 20;
//如果const修饰int * 不能改变指针变量指向的内存地址的值
//但是可以改变指针存的地址
const int * p;
p = &a;
p = &b;
printf("%d\n", *p);
getchar();
return EXIT_SUCCESS;
}
~~~
## const修饰指针变量
~~~
int main()
{
int a = 10;
int b = 20;
//const修饰指针变量
int * const p = &a;
//能改变指针变量指向地址的值,但不能改变指针指向的地址
*p = 100;
printf("%d\n", *p);
getchar();
return EXIT_SUCCESS;
}
~~~
## const同时修饰指针类型和指针变量
~~~
int main()
{
int a = 10;
int b = 20;
//const修饰指针类型并且指针变量,那么都不能改变
const int * const p = &a;
getchar();
return EXIT_SUCCESS;
}
~~~
## const和非const类型转换
当一个指针变量 str1 被 const 限制时,并且类似`const char *str1`这种形式,说明指针指向的数据不能被修改;如果将 str1 赋值给另外一个未被 const 修饰的指针变量 str2,就有可能发生危险。因为通过 str1 不能修改数据,而赋值后通过 str2 能够修改数据了,意义发生了转变,所以编译器不提倡这种行为,会给出错误或警告。
也就是说,`const char *`和`char *`是不同的类型,不能将`const char *`类型的数据赋值给`char *`类型的变量。但反过来是可以的,编译器允许将`char *`类型的数据赋值给`const char *`类型的变量。
这种限制很容易理解,`char *`指向的数据有读取和写入权限,而`const char *`指向的数据只有读取权限,降低数据的权限不会带来任何问题,但提升数据的权限就有可能发生危险。
C语言标准库中很多函数的参数都被 const 限制了,但我们在以前的编码过程中并没有注意这个问题,经常将非 const 类型的数据传递给 const 类型的形参,这样做从未引发任何副作用,原因就是上面讲到的,将非 const 类型转换为 const 类型是允许的
# 指针和数组
指针遍历数组
~~~
int main()
{
//数组名是数组的首地址,这是个常量
int arr[10] = { 0 };
//指向数组的指针,当操作指针的时候,间接操作了数组
int* p = arr;
for (int i = 0; i < 10; ++i) {
// printf("%d\n", p[i]);
//这样也可以
printf("%d\n", *(p+i));
}
getchar();
return EXIT_SUCCESS;
}
~~~
指针操作数组
~~~
int main() {
//数组名是数组的首地址,这是个常量
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
//指向数组的指针,当操作指针的时候,间接操作了数组
int *p = arr;
//指针操作数组
*p = 100;
*(p + 1) = 200;
p[5] = 300;
*(p + 3) = 700;
for (int i = 0; i < 10; ++i) {
// printf("%d\n", p[i]);
//这样也可以
printf("%d\n", *(p + i));
}
getchar();
return EXIT_SUCCESS;
}
~~~
# 多级指针
~~~
int main() {
int a = 10;
int* p = &a;
int** pp = &p;
int*** ppp = &pp;
//所以 ***ppp = a
getchar();
return EXIT_SUCCESS;
}
~~~

# 字符常量
~~~
int main() {
//字符数组,创建位置在栈区
char arr[] = "hello world";
//字符串常量,会在程序运行时,常量区,不能被修改的
// char * arr = "hello world";
printf("%p\n", arr);
printf("%s\n", arr);
getchar();
return EXIT_SUCCESS;
}
~~~
- c语言
- 基础知识
- 变量和常量
- 宏定义和预处理
- 随机数
- register变量
- errno全局变量
- 静态变量
- 类型
- 数组
- 类型转换
- vs中c4996错误
- 数据类型和长度
- 二进制数,八进制数和十六进制数
- 位域
- typedef定义类型
- 函数和编译
- 函数调用惯例
- 函数进栈和出栈
- 函数
- 编译
- sizeof
- main函数接收参数
- 宏函数
- 目标文件和可执行文件有什么
- 强符号和弱符号
- 什么是链接
- 符号
- 强引用和弱引用
- 字符串处理函数
- sscanf
- 查找子字符串
- 字符串指针
- qt
- MFC
- 指针
- 简介
- 指针详解
- 案例
- 指针数组
- 偏移量
- 间接赋值
- 易错点
- 二级指针
- 结构体指针
- 字节对齐
- 函数指针
- 指针例子
- main接收用户输入
- 内存布局
- 内存分区
- 空间开辟和释放
- 堆空间操作字符串
- 内存处理函数
- 内存分页
- 内存模型
- 栈
- 栈溢出攻击
- 内存泄露
- 大小端存储法
- 寄存器
- 结构体
- 共用体
- 枚举
- 文件操作
- 文件到底是什么
- 文件打开和关闭
- 文件的顺序读写
- 文件的随机读写
- 文件复制
- FILE和缓冲区
- 文件大小
- 插入,删除,更改文件内容
- typeid
- 内部链接和外部链接
- 动态库
- 调试器
- 调试的概念
- vs调试
- 多文件编程
- extern关键字
- 头文件规范
- 标准库以及标准头文件
- 头文件只包含一次
- static
- 多线程
- 简介
- 创建线程threads.h
- 创建线程pthread
- gdb
- 简介
- mac使用gdb
- setjump和longjump
- 零拷贝
- gc
- 调试器原理
- c++
- c++简介
- c++对c的扩展
- ::作用域运算符
- 名字控制
- cpp对c的增强
- const
- 变量定义数组
- 尽量以const替换#define
- 引用
- 内联函数
- 函数默认参数
- 函数占位参数
- 函数重载
- extern "C"
- 类和对象
- 类封装
- 构造和析构
- 深浅拷贝
- explicit关键字
- 动态对象创建
- 静态成员
- 对象模型
- this
- 友元
- 单例
- 继承
- 多态
- 运算符重载
- 赋值重载
- 指针运算符(*,->)重载
- 前置和后置++
- 左移<<运算符重载
- 函数调用符重载
- 总结
- bool重载
- 模板
- 简介
- 普通函数和模板函数调用
- 模板的局限性
- 类模板
- 复数的模板类
- 类模板作为参数
- 类模板继承
- 类模板类内和类外实现
- 类模板和友元函数
- 类模板实现数组
- 类型转换
- 异常
- 异常基本语法
- 异常的接口声明
- 异常的栈解旋
- 异常的多态
- 标准异常库
- 自定义异常
- io
- 流的概念和类库结构
- 标准io流
- 标准输入流
- 标准输出流
- 文件读写
- STL
- 简介
- string容器
- vector容器
- deque容器
- stack容器
- queue容器
- list容器
- set/multiset容器
- map/multimap容器
- pair对组
- 深浅拷贝问题
- 使用时机
- 常用算法
- 函数对象
- 谓词
- 内建函数对象
- 函数对象适配器
- 空间适配器
- 常用遍历算法
- 查找算法
- 排序算法
- 拷贝和替换算法
- 算术生成算法
- 集合算法
- gcc
- GDB
- makefile
- visualstudio
- VisualAssistX
- 各种插件
- utf8编码
- 制作安装项目
- 编译模式
- 内存对齐
- 快捷键
- 自动补全
- 查看c++类内存布局
- FFmpeg
- ffmpeg架构
- 命令的基本格式
- 分解与复用
- 处理原始数据
- 录屏和音
- 滤镜
- 水印
- 音视频的拼接与裁剪
- 视频图片转换
- 直播
- ffplay
- 常见问题
- 多媒体文件处理
- ffmpeg代码结构
- 日志系统
- 处理流数据
- linux
- 系统调用
- 常用IO函数
- 文件操作函数
- 文件描述符复制
- 目录相关操作
- 时间相关函数
- 进程
- valgrind
- 进程通信
- 信号
- 信号产生函数
- 信号集
- 信号捕捉
- SIGCHLD信号
- 不可重入函数和可重入函数
- 进程组
- 会话
- 守护进程
- 线程
- 线程属性
- 互斥锁
- 读写锁
- 条件变量
- 信号量
- 网络
- 分层模型
- 协议格式
- TCP协议
- socket
- socket概念
- 网络字节序
- ip地址转换函数
- sockaddr数据结构
- 网络套接字函数
- socket模型创建流程图
- socket函数
- bind函数
- listen函数
- accept函数
- connect函数
- C/S模型-TCP
- 出错处理封装函数
- 多进程并发服务器
- 多线程并发服务器
- 多路I/O复用服务器
- select
- poll
- epoll
- epoll事件
- epoll例子
- epoll反应堆思想
- udp
- socket IPC(本地套接字domain)
- 其他常用函数
- libevent
- libevent简介