数据类型本质 :固定大小内存的别名
数据类型取别名 typdede
变量的本质:连续内存块的别名,变量是一个标号。
主调函数分配的内存(在堆区,栈区、全局区)都可以在被调用函数里使用。
如果在被调用函数里面的临时区(栈)分配内存,主调用函数是不能使用的。
对参数的指针类型应该怎么理解:
理解角度需要从两个角度出发
第一个角度:站在c/c++编译器的角度 对形参,如果是指针类型,c编译器只会把它当作一个指针变量来看。(配四个字节的内存)。
char *p7 形参 是变量
第二个角度:我们只有在使用指针所指向的内存空间的时候,我们才去关心内存是一维的,还是二维的
有关[] *
buf[i]-->buf[0+i];--->*(p+i) ---> p[i]
//站在c++编译器的角度,*p
相当于我们程序员手工(显示)利用间接赋值,去操作内存
//[]怎么理解,只不过是c++编译器帮我们程序员做了一个*p的操作
内存模型:
//1在c中没有字符串这种类型,是通过字符串数组(char buf[100])去模拟
//2 字符串和字符串数组的区别 是不是 带有\0
//print函数是c库函数,它就是按照C风格字符串进行输出数据
//通过字符串常量初始化字符串数组
//通过这种方法它会自动给你\0
char buf4[] = “abcdefg”;
printf("%s\n", buf4);
//strlen() 是一个函数 求字符串的长度(不是求字符数组的长度),它的长度不包括\0
//sizeof() 是一个操作符,求数据类型(实体)的大小
int main()
{
const int a; //
int const b;
const int *c;
int * const d;
const int const *e ;
return 0;
}
Int func1(const )
初级理解:const是定义常量==》const意味着只读
含义:
//第一个第二个意思一样 代表一个常整形数
//第三个 c是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本身可以修改)
//第四个 d 常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)
//第五个 e一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)
const好处
//合理的利用const,
//1指针做函数参数,可以有效的提高代码可读性,减少bug;
//2清楚的分清参数的输入和输出特性
结论:
//指针变量和它所指向的内存空间变量,是两个不同的概念。
//看const 是放在*的左边还是右边 看const是修饰指针变量,还是修饰所指向的内存空变量
数组
数组名
二维数组也是线性排列的
本质分析
//int a[5] 一维数组名代表数组首元素的地址
//int a[5] —> a的类型为int*
//二维数组名同样代表数组首元素的地址
//int b[2][5]—>b的类型为int(*)[5]
指针方式访问二维数组:int a[3][5];
题目 | 方法 | 方法 | 方法 |
---|---|---|---|
第0行第1列元素地址: | &a[0][1] | a[0]+1 | *(a+0)+1 |
第1行第2列元素地址: | &a[1][2] | a[1]+2 | *(a+1)+2 |
第i行第j列元素地址: | &a[i][j] | a[i]+j | *(a+i)+j |
第1行第2列元素值: | a[1][2] | *(a[1]+2) | *(*(a+1)+2) |
第i行第j列元素值: | a[i][j] | *(a[i]+j) | *(*(a+i)+j) |
数组首元素的地址和数组地址是两个不同的概念
2)数组名代表数组首元素的地址,它是个常量。
解释如下:变量本质是内存空间的别名,一定义数组,就分配内存,内存就固定了。所以数组名起名以后就不能被修改了。
3)数组首元素的地址和数组的地址值相等
数组的地址&数组名
结论:二维数组名 char cc[10][30] 是一个数组指针,char (*)[30]
不管是几维数组,都是线性存储的。多维数组做函数参数,二维数组能勉强表达出来,三四维数组表达不出来。
多维数组做函数参数退化
1 C语言中只会以机械式的值拷贝的方式传递参数(实参把值传给形参)
原因1:高效
原因2:C语言处理a[n]的时候,它没有办法知道n是几,它只知道&a[0]是多少,它的值作为参数传递进去了
虽然c语言可以做到直接int fun(char a[20]),然后函数能得到20这个数字,但是,C没有这么做。
2二维数组参数同样存在退化的问题
二维数组可以看做是一维数组
二维数组中的每个元素是一维数组
二维数组参数中第一维的参数可以省略
void f(int a[5]) ----》void f(int a[]); ----》 void f(int* a);
void g(int a[3][3])----》 void g(int a[][3]); ----》 void g(int (*a)[3]);
3、等价关系
数组参数 | 等效的指针参数 |
---|---|
一维数组 char a[30] | 指针 char* |
指针数组 char *a[30] | 指针的指针 char **a |
二维数组 char a[10][30] | 数组的指针 char(*a)[30] |
char buf[10][30] buf+1 常量指针
buf本质—》数组指针
4野指针
//1初始化的是定义指针变量的时候,指针变量赋值成null
//2 释放的时候,判断是不是null
//3 释放完毕以后再赋值成null
数组指针
1)通过数组类型定义数组指针:
typedef int(ArrayType)[5];
ArrayType* pointer;
2) 声明一个数组指针类型 typedef int (*MyPointer)[5];
MyPointer myPoint;
3)直接定义:int (*pointer)[n];
pointer 为数组指针变量名
type 为指向的数组的类型
n 为指向的数组的大小
注意这个地方是type类型(比如 int (*pointer)[10])
结构体基础
//1自己定义一个数据类型 数据类型的本质是固定大小内存块的别名
//2.c cpp 注意结构体类型定义变量的时候,c和c++编译器的处理行为不一样。
//3 结构体类型typedef
//结构体定义变量的方法3种
//结构体变量的初始化方法3种
//两个结构体变量之间可以copy数据,这个是c编译器给我提供的行为,我们要顺从,提供的是浅copy。
注意浅copy和深copy之间的关系
铁律1:指针是一种数据类型
1)指针也是一种变量,占有内存空间,用来保存内存地址
测试指针变量占有内存空间大小
2)*p
操作内存
在指针声明时,*
号表示所声明的变量为指针
在指针使用时,*
号表示 操作 指针所指向的内存空间中的值
*p
相当于通过地址(p变量的值)找到一块内存;然后操作内存
*p
放在等号的左边赋值(给内存赋值)
*p
放在等号的右边取值(从内存获取值)
3)指针变量和它指向的内存块是两个不同的概念
//含义1 给p赋值p=0x1111; 只会改变指针变量值,不会改变所指的内容;p = p +1; //p++
//含义2 给p赋值p=‘a’; 不会改变指针变量的值,只会改变所指的内存块的值
//含义3 =左边p 表示 给内存赋值, =右边p 表示取值 含义不同切结!
//含义4 =左边char *p
//含义5 保证所指的内存块能修改
4)指针是一种数据类型,是指它指向的内存空间的数据类型
含义1:指针步长(p++),根据所致内存空间的数据类型来确定
p++=(unsigned char )p+sizeof(a);
结论:指针的步长,根据所指内存空间类型来定。
建立指针指向谁,就把把谁的地址赋值给指针。
不断的给指针变量赋值,就是不断的改变指针变量(和所指向内存空间没有任何关系)。
铁律2:通过*p/*p++
来改变变量的值是指针存在的最大意义
1)两码事:指针变量和它指向的内存块变量
2)条件反射:指针指向某个变量,就是把某个变量地址否给指针
3)*p
间接赋值成立条件:3个条件
a)2个变量(通常一个实参,一个形参)
b) 建立关系,实参取地址赋给形参指针
c) *p形参去间接修改实参的值
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,来改变实参,把运算结果传出来。
//指针作为函数参数的精髓。
铁律3:理解指针必须和内存四区概念相结合
1)主调函数 被调函数
a)主调函数可把堆区、栈区、全局数据内存地址传给被调用函数
b)被调用函数只能返回堆区、全局数据
2)内存分配方式
a)指针做函数参数,是有输入和输出特性的。
铁律4:应用指针必须和函数调用相结合(指针做函数参数)
指针做函数参数,问题的实质不是指针,而是看内存块,内存块是1维、2维。
1)如果基础类int变量,不需要用指针;
2)若内存块是1维、2维。
铁律5:一级指针典型用法(指针做函数参数)
一级指针做输入
int showbuf(char *p)
int showArray(int *array, int iNum)
一级指针做输出
int geLen(char *pFileName, int *pfileLen);
理解
主调函数还是被调用函数分配内存
被调用函数是在heap/stack上分配内存
铁律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上分配内存
铁律7: 三级指针输出典型用法
三级指针做输出
int getFileAllLine(char ***content, int *pLine);
int getFileAllLine_Free(char ***content, int *pLine);
理解
主调函数还是被调用函数分配内存
被调用函数是在heap/stack上分配内存
铁律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程序书写结构
商业软件,每一个出错的地方都要有日志,日志级别
铁律9:一般应用禁用malloc/new