指针
引入地址概念
int a = 10;
C语言中通过变量名a来访问变量的值10,就是说变量名a是一个地址,地址上放了值10
变量可以通过变量名访问,也可以通过存放10的地址,即变量的地址来访问10这个值
变量名访问
int a = 10;
int b = 9;
printf("a=%d\n",a);
printf("b=%d\n",b);
打印变量地址
int a = 10;
int b = 9;
printf("a地址=%p\n",&a);
printf("b地址=%p\n",&b);
// & 取地址运算符,取变量名所代表的变量的内存地址
通过地址取得变量的值
int a = 10;
int b = 9;
printf("通过地址取值a=%d\n",*(&a));
printf("通过地址取值b=%d\n",*(&b));
//此时 * 代表的是一个运算符,跟 + - * / 运算符类似,该运算符的功能是:取出内存地址中数据的值(取内容)
指针就是操控地址的方式
指针变量引入
int a = 10; //整型变量 存放的是整数
char c = 'c'; //字符变量 存放的是字符
int array[3] = {1,2,3}; //数组 存放的是一串数据
a、c、array都是变量
不同是变量类型不同
a=整数
c=字符
array=数组
指针变量存放的是地址
定义指针变量
int a = 10;
int *p; //指针变量 存放的是地址
int* p;
p = &a; //给指针变量赋值
int *p2 = &a; //定义并初始化一个指针变量
&a(0x660cffC0); 取地址
*p = &a;
*p取了地址a的值10,而&a是取的是变量a的地址,=是把a的地址赋值给10,所以此种写法错误
这是错误的写法,只有在定义一个指针变量的时候,才是指针的标识符,其它情况,都是一个运算符,取地址中的内容
指针变量是存放指针的变量
指针变量是存放地址的变量
通过指针来访问变量
#include <stdio.h>
int main()
{
int a = 10;
int b;
b = 9;
int *p1;
p1 = &a;
int *p2 = &b;
printf("a=%d\n",a);
printf("b=%d\n",b);
printf("通过指针获取a地址=%p\n",p1);
printf("通过指针获取b地址=%p\n",p2);
printf("通过指针取值a=%d\n",*p1);
//此时 * 代表的是一个运算符,跟 + - * / 运算符类似,该运算符的功能是:取出内存地址中数据的值(取内容)
//只有在定义一个指针变量的时候,才是指针的标识符,其它情况,都是一个运算符,取地址中的内容
printf("通过指针取值b=%d\n",*p2);
指针的分类
int *p1; //整型的指针只能指向整数的地址
char *p2; //字符型的指针只能指向字符型的地址
#include <stdio.h>
int main()
{
int *p1;
char *p2;
int a = 10;
char c = 'A';
p1 = &a;
p2 = &c;
printf("a=%d\n",*p1);
printf("c=%c\n",*p2);
system("pause");
return 0;
}
指针偏移量
int *p;
整型数指针位置移动一下是4个字节,且指针的移动地址是连续的
char *p;
字符型指针位置移动一下是1个字节,且指针的移动地址是连续的
#include <stdio.h>
int main()
{
int *p1;
char *p2;
int a = 10;
char c = 'A';
p1 = &a;
p2 = &c;
printf("a=%d\n",*p1);
printf("c=%c\n",*p2);
//指针++
printf("a的地址=%p\n",p1);
printf("a++的地址=%p\n",++p1);
printf("c的地址=%p\n",p2);
printf("c++的地址=%p\n",++p2);
system("pause");
return 0;
}
执行结果:
a=10
c=A
a的地址=0060FF04
a++的地址=0060FF08
c的地址=0060FF03
c++的地址=0060FF04
int array[3] = {1,2,3};
整型数组每个元素大小是4个字节,且地址是连续的
打印数组的地址
#include <stdio.h>
int main()
{
int array[3] = {1,2,3};
printf("array[0]的地址=%p\n",&array[0]);
printf("array[1]的地址=%p\n",&array[1]);
printf("array[3]的地址=%p\n",&array[2]);
int *parray;
parray = &array[0];
printf("数组array[0]指针的地址=%p\n",parray++);
printf("数组array[1]指针的地址=%p\n",parray++);
printf("数组array[2]指针的地址=%p\n",parray);
system("pause");
return 0;
}
执行结果:
array[0]的地址=0060FEF0
array[1]的地址=0060FEF4
array[3]的地址=0060FEF8
数组array[0]指针的地址=0060FEF0
数组array[1]指针的地址=0060FEF4
数组array[2]指针的地址=0060FEF8
历遍获取数组的值
#include <stdio.h>
int main()
{
int array[3] = {1,2,3};
int i;
for(i=0;i<3;i++){
printf("array[%d]=%d\n",i+1,array[i]);
}
int *parray;
parray = &array[0];
//parray = array;
for(i=0;i<3;i++){
printf("指针array[%d]=%d\n",i+1,*parray++);
//printf("指针array[%d]=%d\n",i+1,*parray[i]);
}
system("pause");
return 0;
}
执行结果:
array[1]=1
array[2]=2
array[3]=3
指针array[1]=1
指针array[2]=2
指针array[3]=3
数组赋值给指针:
int array[3] = {1,2,3};
int *parray;
parray = &array[0];
parray = array;
指针取值:
*parray
*parray[i]
加入指针优化成绩录入程序:
#include <stdio.h>
int initPeople(int n)
{
printf("需要录入几个学生分数:");
scanf("%d",&n);
return n;
}
void initScores(int *array,int n)
{
int i;
for(i=0;i<n;i++){
printf("请输入第%d个学生的分数:",i+1);
scanf("%d",array);
if(*array > 100 || *array < 0){
printf("非法分数!\n强制退出!\n");
system("pause");
exit(-1);
}
array++;
}
}
void scoresPrint(int *array,int n)
{
int i;
for(i=0;i<n;i++){
printf("第%d个学生的分数是:%d\n",i+1,*array++);
}
}
int getMax(int *array,int n)
{
int i;
int max;
max = *array;
for(i=0;i<n;i++){
if(max < *array){
max = *array;
}
array++;
}
return max;
}
int getMin(int *array,int n)
{
int i;
int min;
min = *array;
for(i=0;i<n;i++){
if(min > *array){
min = *array;
}
array++;
}
return min;
}
float getAverage(int *array,int n)
{
int i;
int total = 0;
float average;
for(i=0;i<n;i++){
total += *array;
array++;
}
average = (float)total/n;
return average;
}
void printRet(int a, int b, float f)
{
printf("最高分:%d\n最低分:%d\n平均分:%f\n",a,b,f);
}
int main()
{
int len = 0;
int scores[len];
int max;
int min;
float average;
len = initPeople(len);
int *p;
p = scores;//可以通过数组名把数组的第一个元素的地址赋值给指针变量
p = &scores[0];//可以通过数组首地址赋值给指针变量
initScores(&scores[0], len);
//函数调用过程中,传参就是一个赋值的过程。实际参数的值,给形式参数
//函数调用可以传数组第一个元素的地址
//函数传参就是把变量的地址到函数,让函数基于地址来控制,运算地址上的值
scoresPrint(p, len);//函数可以通过传指针变量名给函数形参
max = getMax(scores, len);
min = getMin(scores, len);
average = getAverage(scores, len);
printRet(max, min, average);
system("pause");
return 0;
}
指针初略总结
1、变量访问的两种方式
- 变量名
- 指针(地址)
2、指针
- 指向谁(注意类型) p = &a;
- 偏移后指向谁 p++
- int *p; p++偏移了4个字节
- char *p; p++偏移了1个字节
#include <stdio.h>
int main()
{
int a;
a = 10;
char c;
c = 'A';
int *p1;
p1 = &a;
char *p2;
p2 = &c;
printf("变量名a=%d\n",a);
printf("地址a=%d\n",*(&a));
printf("指针a=%d\n",*p1);
printf("指针c=%c\n",*p2);
int array[3] = {1,2,3};
int *p3;
p3 = array;
int i;
printf("====================数组下标访问数组====================\n");
for(i=0;i<3;i++){
printf("array[%d]=%d\n",i,array[i]);
}
printf("====================指针访问数组====================\n");
for(i=0;i<3;i++){
printf("array[%d]=%d\n",i,p3[i]);
//通过下标方式访问数组指针的值
}
for(i=0;i<3;i++){
printf("array[%d]=%d\n",i,*p3);
p3++;
}
system("pause");
return 0;
}
使用指针的理由
int a;
一般变量a的地址是系统随机分配的
C语言是可以让a的地址指定为自己想要的地址
指针可以强制某个地址存放想要的值
#include <stdio.h>
int main()
{
int a; //a的地址是系统随机分配的
a = 10;
int *p;
p = &a;
printf("a address is %p\n",p);
int *p2 = (int *)0x0060ff00;
//因为指针变量是存放地址的变量,所以可以写一个地址,强转为整型,在把地址给到指针变量
*p2 = 20;
printf("在内存%p位置,存放的值是%d\n",p2,*p2);
volatile int *p3 = (volatile int *)0x0060ff05;//类似修饰符
system("pause");
return 0;
}
volatile关键字1
作用:
1、确保本条指令不会因编译器的优化而省略
2、要求每次直接读值。
在多线程编程中,会因为线程在编程过程中对内存变量的值做了修改,在复制一份给寄存器, 而多线程的话,在另一个线程会对使用这个变量的值上存在一定的时效性,会对运行结果存在误差。
所以volatile关键字就是程序禁止去用寄存器上的变量的值,每次运行都是去调用内存上的值
有时编译器会对指定的值做优化修改,所以有这个关键是就的避免编译器优化
指针对地址的控制变化下,变量的值的变化:
对变量的值做交换
#include <stdio.h>
int main()
{
int a = 10;
int b = 5;
int tmp;
tmp = a;
a = b;
b = tmp;
printf("a=%d\nb=%d\n",a,b);
system("pause");
return 0;
}
执行结果:
产生了交换
a=5
b=10
用函数做变量值的交换
#include <stdio.h>
void swap(int a, int b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 10;
int b = 5;
swap(a,b);
printf("a=%d\nb=%d\n",a,b);
system("pause");
return 0;
}
执行结果:
没有产生交换
a=10
b=5
当传地址给到函数交换
#include <stdio.h>
void swap(int *a, int *b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int main()
{
int a = 10;
int b = 5;
swap(&a,&b);
printf("a=%d\nb=%d\n",a,b);
system("pause");
return 0;
}
执行结果:
产生了交换
a=5
b=10
指针是存放地址的变量
指针可以通过地址来控制变量名地址里的值
修改变量的值,一般通过是函数传参,是把变量的值在内存中复制一份给到函数形参,在由函数执行方法运算,处理,后面再把处理的结果通过不同的数据类型区分,返回出去,给到调用函数程序,最后释放掉函数
#include <stdio.h>
void jia(int a)
{
a = a + 1;
printf("jia:a=%d\n",a);
}
int main()
{
int a = 10;
jia(a);
printf("a=%d\n",a);
system("pause");
return 0;
}
执行结果:
jia:a=11
a=10
通过指针修改变量的值,是通过把变量的地址给到处理函数,这样函数控制修改的是变量地址上的值,函数结束释放后可以不用返回修改后的数据值,而调用函数程序里变量的值已经发生了改变。
因为函数直接修改的是调用函数程序里变量的地址上面的值,而不是复制一份值给函数自己,再在内存中生成一份数据,在做修改
#include <stdio.h>
void jia(int *a)
{
*a = *a + 1;
printf("jia:a=%d\n",*a);
}
int main()
{
int a = 10;
jia(&a);
printf("a=%d\n",a);
system("pause");
return 0;
}
执行结果:
jia:a=11
a=11
指针数组
1、很多指针,用数值的方式,放在一起
2、它是一个数组,数组中每一个元素都是指针
- 好多变量的地址放一起的集合
#include <stdio.h>
int main()
{
int a = 3;
int b = 8;
int c = 5;//三个毫无关系的整型变量
int array[3]; //多个整数,叫做整数数组
int* p;
//定义指针数组
int* parray[3];
//多个指针,叫做指针数组,数组中的每个元素都是一个指针变量
//指针变量是存放地址的变量
parray[0] = &a;
parray[1] = &b;
parray[2] = &c;
//三个普通没有任何关系的整型变量的地址存入指针数组
int i;
for(i=0;i<3;i++){
printf(" %d ",*(parray[i]));
}
putchar('\n');
system("pause");
return 0;
}
数组指针
1、一个指针变量
2、真正指向某个类型数组的指针
3、一般指针是指向数组的首地址,即数组的第一个元素地址。&array[0]
4、明确规定了指针要指向数组的类型及大小
整型数指针指向数组
#include <stdio.h>
int main()
{
int a[3] = {1,2,3};
int *p;
//此指针不是数组指针,仅仅是一个普通的整型数的指针,只不过指针刚好指向了数组的首元素地址
p = a;
//类于如此
int b;
int *p1;
p1 = &b;
int i;
for(i=0;i<3;i++){
printf(" %d ",*p++);
}
system("pause");
return 0;
}
-
int (*p)[3];
-
数组的指针的定义方式
-
p = a;
-
这是数组指针的赋值方式
-
p = &a;
-
这是错误的复制赋值方式
数组指针指向数组
#include <stdio.h>
int main()
{
int a[3] = {1,2,3};
int (*p)[3];
//数组的指针的定义方式
//数组的指针强调的是类型,数组的个数,偏移值是偏移了整个数组的大小
p = a;
int *p2;
p2 = a;
printf("数组a的地址是%p\n",a);
printf("数组a的地址是%p\n",&a[0]);
printf("p数组的地址是%p\n",p);
printf("p2数组的地址是%p\n",p2); //0060FEEC
printf("=====================区别======================\n");
printf("p++的结果是:%p\n",++p); //0060FEF8 - 0060FEEC = 12
printf("p2++的结果是:%p\n",++p2); //0060FEF0 - 0060FEEC = 4
system("pause");
return 0;
}
执行结果:
数组a的地址是0060FEEC
数组a的地址是0060FEEC
p数组的地址是0060FEEC
p2数组的地址是0060FEEC
=====================区别======================
p++的结果是:0060FEF8
p2++的结果是:0060FEF0
数组指针不同定义方式的差别
会因为偏移量的不同,有不同的定义方式
int array[3] = {1,2,3};
整型数指针指向数组:
定义方式
- int *p;
赋值方式
- p = array;
- p = &array[0];
取值方式
- *p
- p[0]
偏移值是移动一个数组元素的大小
int array[3] = {1,2,3};
数组指针指向数组:
定义方式
- int (*p)[3];
赋值方式
- p = array;
取值方式
- (*p)[i]
偏移值是移动整个数组的大小
数组指针的使用
#include <stdio.h>
int main()
{
int a[3] = {1,2,3};
int (*p)[3];
p = a;
int i;
for(i=0;i<3;i++){
printf(" %d ",(*p)[i]);
}
system("pause");
return 0;
}
函数指针
1、一个指针变量
- void (*p)();
- int (*a)(int a,int b);
2、存放的是函数的地址
- p = printWelcome;
定义方式:
- void (*p)();
- 表示指针:*
- 表示函数:()
- 函数指针是专用的,格式要求很强(参数类型,个数,返回值),就像数组指针一样
函数指针赋值:
- p = printWelcome;
- 函数名就是地址,就像数组一样,数组名就是地址
通过指针调用函数:
- p();
- 直接通过指针名字+()
- (*p)();
- 通过取内容:*指针名()
示例代码
#include <stdio.h>
void printWelcome()
{
printf("hello welcome to you!\n");
}
int main()
{
int a = 10;
printf("a=%d\n",a); //通过变量名来访问一个变量
int *p = &a;
printf("a=%d\n",*p); //通过指针来访问一个变量
printWelcome();//通过函数名来调用函数
//定义函数指针
void (*p2)();
//表示指针:*
//表示函数:()
//函数指针是专用的,格式要求很强(参数类型,个数,返回值),就像数组指针一样
p2 = printWelcome;
//函数名就是地址,就像数组一样,数组名就是地址
//通过函数指针调用函数
p2();//直接通过指针名字+()
(*p2)();//取内容:*指针名()
system("pause");
return 0;
}
整型带参数函数指针
#include <stdio.h>
int add(int a,int b)
{
return a+b;
}
int main()
{
int (*padd)(int a,int b);
padd = add;
int ret = padd(1,2);
//int ret = (*padd)(1,2);
printf("ret=%d\n",ret);
system("pause");
return 0;
}
无类型指针malloc
void *malloc(size_t size);
开辟连续内存地址空间函数
void free(void *ptr);
释放掉开辟的地址空间
malloc函数用法
#include <stdio.h>
int main()
{
//int a[3];
int *a = (int *)malloc(3*sizeof(int)); //也是数组
int i;
for(i=0;i<3;i++){
a[i] = i+1;
}
for(i=0;i<3;i++){
printf("%d ",a[i]);
}
system("pause");
return 0;
}
函数原型:
void *malloc(size_t size);
指针用法:
int *a = (int *)malloc(3 * sizeof(int));
- size_t int
- 需要开辟空间的大小
- 只能为正整数
- 开辟的空间是无类型的空间
- (int *)
- 把开辟的无类型的地址空间强转为整数类型
- int *a
- 把开辟的地址空间赋值给到整型指针变量a
- 类似于数组赋值给到整型指针
优化示例
#include <stdio.h>
int main()
{
int n;
printf("请输入录入成绩的个数:\n");
scanf("%d",&n);
//int a[n];
int *a = (int *)malloc(n*sizeof(int)); //也是数组
int i;
for(i=0;i<n;i++){
printf("请输入第%d个学生成绩:\n",(i+1));
scanf("%d",&a[i]);
}
for(i=0;i<n;i++){
printf("第%d学生成绩为:%d\n",(i+1),a[i]);
}
free(a);
a = NULL;
system("pause");
return 0;
}
内存泄漏
现象:
程序刚跑起来,很好,跑几个小时,或者几天,或者几周,程序崩溃
while(1){
sleep(1);
int *p = malloc(1024);
}
malloc 申请空间,程序不会主动释放,linux中会在程序结束后,系统内存管理会回收这个空间
避免:
- 注意循环中有没有一直申请内存空间
- 及时合理的释放
释放:
free§;
p = NULL;
指针总结
定义整型变量
int a;
定义p为指向整型数据的指针变量
int *p;
定义整型数组a,它有5个元素
int array[5];
定义指针数组p,它由4个指向整型数据的指针元素组成
int *p[4]; //p++是偏移一个元素
p为指向包含4个元素的一维数组的指针变量
int (*p)[4]; //p++是偏移整个数组
f为返回整型数值的函数
int f();
p为返回一个指针的函数,该指针指向整型数据
int* p();
p为指向函数的指针,该函数返回一个整型值
int (*p)();
p是一个指针变量,它指向一个指向整型数据的指针变量
int **p;
p是一个指针变量,基类型为void(空类型),不指向具体的对象
void *p = NULL;