1,指针铁律
1.1,指针是一种数据类型
1, 指针也是一种变量,占有内存空间,用来保存内存地址。
指针既然是变量也要占用内存大小。一级指针的大小于其类型有关,二级以及二级以上的指针的大小是四个字节(32位机)
2,*p操作内存
在指针声明时,*号表示所声明的变量为指针
在指针使用时,*号表示 操作 指针所指向的内存空间中的值
*p 相当于通过地址(p 变量的值)找到一块内存;然后操作内存
在指针使用时,*号表示 操作 指针所指向的内存空间中的值
*p 相当于通过地址(p 变量的值)找到一块内存;然后操作内存
*p 放在等号的左边赋值(给内存赋值) 如 *p = 10;
*p 放在等号的右边取值(从内存获取值) int a = *p;
3,指针变量和它指向的内存块是两个不同的概念
//含义 1 给 p 赋值 p=0x1111; 只会改变指针变量值(改变其指向), 不会改变所指的内容; p = p +1;p++加的是其步长而不是真正的1
//含义 2 给*p 赋值*p='a'; 不会改变指针变量的值,只会改变所指的内存块的值
//含义 3 =左边*p 表示 给内存赋值, =右边*p 表示取值 含义不同切结!
//含义 4 =左边 char *p
//含义 3 =左边*p 表示 给内存赋值, =右边*p 表示取值 含义不同切结!
//含义 4 =左边 char *p
//含义 5 保证所指的内存块能修改
4,指针是数据类型,是指 它指向内存空间的数据类型
含义 1:指针步长(p++) ,根据所致内存空间的数据类型来确定
p++= ->(unsigned char )p+sizeof(a);
结论:指针的步长,根据所指内存空间类型来定。
注意: 建立指针指向谁,就把把谁的地址赋值给指针.
不断的给指针变量赋值,就是不断的改变指针变量(和所指向内存空间没有任何关
1.2:间接赋值(*p)是指针存在的最大意义
1)两码事:指针变量和它指向的内存块变量
2)条件反射:指针指向某个变量,就是把某个变量地址否给指针
3)*p 间接赋值成立条件:3 个条件
a)2 个变量(通常一个实参,一个形参)
b) 建立关系,实参取地址赋给形参指针
c) *p 形参去间接修改实参的值
案例
Int iNum = 0; //实参
int *p = NULL;
p = &iNum;
iNum = 1;
*p =2 ; //通过*形参 == 间接地改变实参的值
*p 成立的三个条件:
4)引申: 函数调用时,用 n 指针(形参)改变 n-1 指针(实参)的值。
//改变 0 级指针(int iNum = 1)的值有 2 种方式
//改变 1 级指针(eg char *p = 0x1111 )的值,有 2 种方式
//改变 2 级指针的(eg char **pp1 = 0x1111 )的值,有 2 种方式
//函数调用时,形参传给实参,用实参取地址,传给形参,在被调用函数里面用*p,来改变实参,把运算结果传出来。
//指针作为函数参数的精髓。
1.3:理解指针必须和内存四区概念相结合
1)主调函数 被调函数
a) 主调函数可把堆区、栈区、全局数据内存地址传给被调用函数
b) 被调用函数只能返回堆区、全局数据,不能返回栈上数据
2)内存分配方式
a) 指针做函数参数,是有输入和输出特性的。
1.4:应用指针必须和函数调用相结合(指针做函数参数)
指针做函数参数,问题的实质不是指针,而是看内存块,内存块是 1 维、2 维。
1)如果基础类 int 变量,不需要用指针;
2)若内存块是 1 维、2 维,需要指针。
1.5:一级指针典型用法(指针做函数参数)
一级指针做输入int showbuf(char *p)int showArray(int *array, int iNum)
一级指针做输出int geLen(char *pFileName, int *pfileLen);
一级指针做输出int geLen(char *pFileName, int *pfileLen);
理解
主调函数还是被调用函数分配内存
被调用函数是在 heap/stack 上分配内存
1..6:二级指针典型用法(指针做函数参数)
二级指针做输入
int main(int arc ,char *arg[]); 字符串数组
int shouMatrix(int [3][4], int iLine);
二级指针做输出
int Demo64_GetTeacher(Teacher **ppTeacher);
int Demo65_GetTeacher_Free(Teacher **ppTeacher);
int getData(char **data, int *dataLen);
Int getData_Free(void *data);
Int getData_Free2(void **data); //避免野指针
理解
主调函数还是被调用函数分配内存
主调函数还是被调用函数分配内存
被调用函数是在 heap/stack 上分配内存
1.7: 三级指针输出典型用法
三级指针做输出
int getFileAllLine(char ***content, int *pLine);
int getFileAllLine_Free(char ***content, int *pLine);
理解
主调函数还是被调用函数分配内存
被调用函数是在 heap/stack 上分配内存
1.8: 杂项,指针用法几点扩充
1)野指针 2 种 free 形式
int getData(char **data,
int *dataLen);int getData_Free(void *data);
int getData_Free2(void **data);
2)2 次调用
主调函数第一次调用被调用函数求长度;根据长度,分配内存,调用被调用
函数。
3)返回值 char */int/char **4)C 程序书写结构
商业软件,每一个出错的地方都要有日志,日志级别
1. 9:一般应用禁用 malloc/new 因为使用malloc/new就会伴随着内存泄漏
二级指针与多级指针
使用指针申请二维空间
#include <stdio.h>
#if 0
//利用返回值进行申请不连续的二维空间
void ** Shenqin(int base,int row,int line)
{
void **p = (void **)malloc(sizeof(void*)*row);
for(int i = 0;i<row;i++)
{
p[i] = (void *)malloc(base*line);
}
return p;
}
#endif
#if 0
//利用三级指针 参数进行申请不连续的二维空间
void Shenqin(void ***p,int base,int row,int line)
{
*p = (void**)malloc(sizeof(void*)*row);//*p可以把***p降级为**p
for(int i =0;i<row;i++)
{
(*p)[i] = (void*)malloc(base*line);
}
}
#endif
#if 1
//申请连续的二维空间
void ** Shenq(int base,int row,int line)
{
void **p = (void**)malloc(sizeof(int*)*row);
void *p1 = (void *)malloc(base*row*line);
int (*PA)[line] =p1;
for(int i =0;i<row;i++)
{
p[i]=PA++;
}
return p;
}
//连续的二维空间的释放
void Free(void **p)
{
free(p[0]);
free(p);
}
#endif
#if 1
//不连续的二维空间的释放
void FreeKongjian(void **p,int row)
{
for(int i = 0;i<row;i++)
{
free(p[i]);
}
free (p);
}
#endif
int main(void)
{
// int **p = Shenqin(sizeof(int),3,4);
//int **p=NULL;
// Shenqin(&p,sizeof(int),3,4);
int **p =Shenq(sizeof(int),3,4);
for(int i = 0;i<3;i++)
{
for(int j = 0;j<4;j++)
p[i][j]=i+j;
}
for(int i = 0;i<3;i++)
{
for(int j = 0;j<4;j++)
printf("%d ",p[i][j]);
putchar(10);
}
Free(p);
// FreeKongjian(p,3);
return 0;
}
//利用返回值进行申请不连续的二维空间
void ** Shenqin(int base,int row,int line)
{
void **p = (void **)malloc(sizeof(void*)*row);
for(int i = 0;i<row;i++)
{
p[i] = (void *)malloc(base*line);
}
return p;
}
#endif
#if 0
//利用三级指针 参数进行申请不连续的二维空间
void Shenqin(void ***p,int base,int row,int line)
{
*p = (void**)malloc(sizeof(void*)*row);//*p可以把***p降级为**p
for(int i =0;i<row;i++)
{
(*p)[i] = (void*)malloc(base*line);
}
}
#endif
#if 1
//申请连续的二维空间
void ** Shenq(int base,int row,int line)
{
void **p = (void**)malloc(sizeof(int*)*row);
void *p1 = (void *)malloc(base*row*line);
int (*PA)[line] =p1;
for(int i =0;i<row;i++)
{
p[i]=PA++;
}
return p;
}
//连续的二维空间的释放
void Free(void **p)
{
free(p[0]);
free(p);
}
#endif
#if 1
//不连续的二维空间的释放
void FreeKongjian(void **p,int row)
{
for(int i = 0;i<row;i++)
{
free(p[i]);
}
free (p);
}
#endif
int main(void)
{
// int **p = Shenqin(sizeof(int),3,4);
//int **p=NULL;
// Shenqin(&p,sizeof(int),3,4);
int **p =Shenq(sizeof(int),3,4);
for(int i = 0;i<3;i++)
{
for(int j = 0;j<4;j++)
p[i][j]=i+j;
}
for(int i = 0;i<3;i++)
{
for(int j = 0;j<4;j++)
printf("%d ",p[i][j]);
putchar(10);
}
Free(p);
// FreeKongjian(p,3);
return 0;
}
2, 指针和数组的巨大区别
3,字符与一级指针的内存模型
4,
const基础
int main()
{
const int a; //
int const b;
const char *c;
char * const d; char buf[100]
const char * const e ;
const int a; //
int const b;
const char *c;
char * const d; char buf[100]
const char * const e ;
return 0;
}
int func1(const )
初级理解:const 是定义常量==》const 意味着只读
含义:
//第一个第二个意思一样 代表一个常整形数
//第三个 c 是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本身可以修改)
//第四个 d 常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)
//第五个 e 一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)
Const 好处
//合理的利用 const,
//1 指针做函数参数,可以有效的提高代码可读性,减少 bug;
//2 清楚的分清参数的输入和输出特性
结论:
//指针变量和它所指向的内存空间变量,是两个不同的概念。 。 。 。 。 。
初级理解:const 是定义常量==》const 意味着只读
含义:
//第一个第二个意思一样 代表一个常整形数
//第三个 c 是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本身可以修改)
//第四个 d 常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)
//第五个 e 一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)
Const 好处
//合理的利用 const,
//1 指针做函数参数,可以有效的提高代码可读性,减少 bug;
//2 清楚的分清参数的输入和输出特性
结论:
//指针变量和它所指向的内存空间变量,是两个不同的概念。 。 。 。 。 。
//看 const 是放在*的左边还是右边 看 const 是修饰指针变量,还是修饰所指向的内存空变
二位数组图解