当年刚开始接触C语言看郝斌的视频做的笔记,好歹也是自己辛苦敲出来的,所以一定要发出来。都是一些最基础的C语言指针知识。
一、认识指针
# include <stdio.h>
int main(void)
{
int * p; //p是变量的名字,int * 表示p存放的是int类型变量的地址
int i = 3;
int j;
p = &i;
/*
1.p保存了i的地址,因此p指向i
2.p不是i,i也不是p,更准确的说,修改p的值不影响i的值,修改i的值不影响p的值
3.如果一个指针变量指向了某个普通变量,则
*指针变量 将完全等同于 普通变量
例子:
如果p是个指针变量,并且p存放了普通变量i的地址
则p指向了普通变量i
*p 就完全等同于 i
或者说:在所有出现*p的地方都可以替换成i
在所有出现i的地方都可以替换成*p
*p就是以p的内容为地址的变量
*/
j = *p;
printf("i = %d, j = %d\n", i, j);
return 0;
}
/*
指针的重要性
表示一些复杂的数据结构
快速的传递数据,减少内存的耗用
使函数返回一个以上的值
能直接访问硬件
能够方便的处理字符串
是理解面向对象语言中的引用基础
指针是C语言的灵魂
*/
二、基本类型指针常见错误
# include <stdio.h>
int main(void)
{
int i = 5;
int * p;
int * q;
p = &i;
//*q = p; //error
//*q = *p; //error
p = q; //q是垃圾值,q赋给p,p也变成垃圾值
printf("%d\n", *q); //13行
/*
q的空间属于本程序的,所以本程序可以读写q的内容
但是如果q内部是垃圾值,则本程序不能读写*q的内容
因为此时*q所代表的内存单元的控制权限没有分配给本程序
所以本程序运行到13行是就会立即出错
*/
return 0;
}
三、经典指针程序_互换两个数字
# include <stdio.h>
void huhuan_1(int, int);
void huhuan_2(int *, int *);
void huhuan_3(int *, int *);
int main(void)
{
int a = 3;
int b = 5;
huhuan_3(&a, &b);
printf("a = %d, b = %d\n", a, b);
return 0;
}
//不能完成互换功能
void huhuan_1(int a, int b)
{
int t;
t = a;
a = b;
b = t;
return;
}
//不能完成互换功能
void huhuan_2(int * p, int * q)
{
int * t;
t = p;
p = q;
q = t;
return;
}
//可以完成互换功能
void huhuan_3(int * p, int * q)
{
int t;
t = *p;
*p = *q;
*q = t;
return;
}
/*
如何通过被调函数修改主调函数普通变量的值
1. 实参必须为该普通变量的地址
2. 形参必须为指针变量
3. 在被调函数中通过
*形参名 = ......
的方式就可以修改主调函数相关变量的值
*/
四、一维数组与指针
# include <stdio.h>
int main(void)
{
int a[5]; //a是数组名,5是数组元素的个数,元素就是变量,a[0]--a[4]
int b[4];
//a = b; //error,因为a是常量
printf("%#X\n", &a[0]); //%#X 是一十六进制输出
printf("%#X\n", a);
return 0;
}
/*
在vc++6.0中的显示结果为:
-------------------------
0X19FF2C
0X19FF2C
-------------------------
总结:
一维数组名
一维数组名是一个指针常量
她存放的是一维数组第一个元素的地址
*/
五、确定一个一维数组需要2个参数
# include <stdio.h>
void f(int * pArr, int len)
{
int i;
for (i=0; i<len; i++)
printf("%d\t", *(pArr + i)); //*(pArr + i) 等价于 pArr[i]
printf("\n");
return;
}
int main(void)
{
int a[5] = {1,2,3,4,5};
int b[6] = {-1,-2,-3,-4,-5,-6};
int c[100] = {23,4,4,6,66,43};
f(a, 5);
f(b, 6);
f(c, 100);
return 0;
}
/*
确定一个一维数组需要两个参数
数组第一个元素的地址
数组的长度
*/
五、确定一个一维数组需要2个参数_2
# include <stdio.h>
void f(int * pArr, int len)
{
pArr[3] = 88;
return;
}
int main(void)
{
int a[6] = {1,2,3,4,5,6};
printf("%d\n", a[3]);
f(a, 6);
printf("%d\n", a[3]);
return 0;
}
/*
一定要明白 pArr[3] 和 a[3] 是同一个变量
在vc++6.0中的显示结果为:
------------------------
4
88
------------------------
*/
六、动态内存分配【重难点】_传统数组的缺点
动态内存分配
传统数组的缺点:
1.数组长度必须事先制定,且只能是常整数,不能是变量
例子:
int a[5]; //ok
int len; int a[len]; //error
2.传统形式定义的数组,该数组的内存程序员无法手动释放,
在一个函数的运行期间,系统为该函数中数组所分配的空间会一直存在,
直到该函数运行完毕时,数组的空间才会被系统释放。
3.数组的长度一旦定义,其长度就不能再改变
数组的长度不能再函数的运行过程中动态的扩充或缩小
4.A函数定义的数组,在A函数运行的期间可以被其他函数使用,
但A函数运行完毕后,A函数中的数组将无法再被其他函数使用
传统方式定义的数组不能跨函数使用
为什么需要动态分配内存
动态数组很好的解决了传统数组的这4个缺陷
传统数组也叫静态数组
七、malloc函数使用的简单介绍_1
/*
malloc 是 memory(内存)allocate(分配)的缩写
*/
# include <stdio.h>
# include <malloc.h>
int main(void)
{
int i = 5; //10行
int * p = (int * )malloc(4); //11行
/*
1.要使用malloc函数,必须添加malloc.h这个头文件
2.malloc函数只有一个形参,并且形参是整型
3.4表示请求系统为本程序分配4个字节
4.malloc函数只能返回第一个字节的地址
5.11行分配了8个字节,p变量占4个字节,p所指向的内存也占4个字节
6.p本身所占的内存是静态分配的,p所指向的内存是动态分配的
*/
*p = 5; //*p代表的就是一个int变量,只不过*p这个整型变量的内存分配方式和10行的i变量分配方式不同
free(p);
/*free(p)表示把p所指向的内容给释放掉,p本身的内存是静态分配的,本能由程序员手动释放,p本身的内存只能在p变量所在的函数运行终止时由系统自动释放*/
printf("哈哈!\n");
return 0;
}
八、malloc函数使用的简单介绍_2
# include <stdio.h>
# include <malloc.h>
void f(int * q)
{
//*p = 200; //error
//q = 200; //error
//**q = 200;//error
*q = 200;
//free(q); //把q所指向的内存释放掉,本语句必须注释掉,否则会导致第23行代码出错
}
int main(void)
{
int * p = (int * )malloc(sizeof(int)); //sizeof(int)返回值是int所占的字节数
*p = 10;
printf("%d\n", *p);
f(p
); //p是int * 类型
printf("%d\n", *p);
return 0;
}
九、动态内存分配举例_动态一维数组的构造
# include <stdio.h>
# include <malloc.h>
int main(void)
{
int a[5]; //如果int占4个字节的话,则本数组总共占了20个字节,每4个字节被当做了一个int变量来使用
int len;
int * pArr;
int i;
//动态的构造一维数组
printf("请输入你要存放的元素个数:");
scanf("%d", &len);
pArr = (int * )malloc(4 * len); //本行动态的构造了一个一维数组,该一维数组的长度是len,该数组的数组名为pArr,该数组的每个元素是int类型,类似于int pArr[len]
//对一维数组进行操作, 如对动态一维数组进行赋值
for (i=0; i<len; ++i)
scanf("%d", &pArr[i]);
//对一维数组进行输出
for (i=0; i<len; ++i)
printf("%d\n", pArr[i]);
free(pArr); //释放掉动态分配的数组
return 0;
}
十、多级指针
# include <stdio.h>
int main(void)
{
int i = 10;
int * p = &i;
int ** q = &p;
int *** r = &q;
//r = &i; //error 因为r是int *** 类型,只能存放int **类型变量的地址
printf("i = %d\n", ***r);
return 0;
}
/*
i p q r
----- ----- ----- -----
10 1000h 2000h 3000h
----- ----- ----- -----
1000h 2000h 3000h 4000h
int int * int ** int ***
*/
十一、动态内存可以跨函数使用详解
# include <stdio.h>
# include <malloc.h>
void f(int ** q)
{
*q = (int *)malloc(sizeof(int)); //等价于 p = (int *)malloc(sizeof(int));
**q = 5; //**q = *p;
}
int main(void)
{
int * p;
f(&p);
printf("%d\n", *p);
return 0;
}
十二、指针变量不能跨函数使用详解
# include <stdio.h>
void f(int ** q) //q是个指针变量,无论q是什么类型的指针变量,都只占4个字节
{
int i = 5;
*q = &i; //*q等价于p,p = &i;
}
int main(void)
{
int * p;
f(&p);
printf("%d\n", *p); //本语句语法没有问题,但逻辑上有问题
return 0;
}
/*
f终止之后,为f分配的静态变量全部释放
*/