指针就是地址。
指针变量:是个变量,用来存放一个地址编号。(32位下,指针变量是4个字节)
对应类型的指针变量,只能存放对应类型的变量的地址。
扩展:char ch; ch占一个字节,它有一个地址编号,这个地址编号就是ch的地址。
int a; a占4个字节,它占有4个字节的储存单元,有4个地址编号。
指针的定义:
数据类型 * 指针变量名;
int *p; (*用来修饰变量的,说明p是个指针变量)。
关于指针的运算符
&:取地址(获取变量的地址)。*:取值(在定义指针变量时起标识作用,除此之外表示获取一个指针变量保存的地址里面的内容)。
代码1:
#include <stdio.h>
int main(int argc,char *argv[])
{
int a = 100;
int* p;
p = &a;
printf("a=%d\n", a);
printf("*p=%d\n", *p);
printf("&a=%d\n", &a);
printf("p=%d\n", p);
system("pause");
return 0;
}
结果:
a=100
*p=100
&a=1898480
p=1898480
扩展:如果在一行中定义了多个指针变量,每个指针变量前面都需要加*来修饰。
int *p,*q;说明定义了两个指针。
int *p,q;说明只定义了一个指针。
指针和变量的关系:
int p;
int a;
p=&a;
就可得到p与a是一样的,改变其中任意一个的值,另一个也会跟着改变。
注:1.指针变量可以在定义的时候初始化
int a;
int *p=&a;
2.指针变量只能保存开辟好空间的地址,不能随意保存地址。
代码2:
#include <stdio.h>
#pragma warning(disable:4996)
int main(int argc, char* argv[])
{
int* p1, * p2, temp, a, b;
p1 = &a;
p2 = &b;
printf("请输入:a,b的值:\n");
scanf("%d,%d", &a, &b);//p1和p2指向的变量赋值
temp = *p1;
*p1 = *p2;
*p2 = temp;
printf("a=%d,b=%d\n",a ,b );
printf("*p1=%d,*p2=%d\n", *p1, *p2);
return 0;
}
结果:
请输入:a,b的值:
34,65
a=65,b=34
*p1=65,*p2=34
指针与数组元素之间的关系:
数组:多个相同类型的变量的集合,每个变量都占内存空间,都有地址编号。
指针变量可以存放数组元素的地址。
例:
int a[10];
int *p;
p=&a[0];//指针变量p保存了数组a中第0个元素的地址,即a[0]的地址。
数组元素的引用方法:
方法1:数组名[下标]
int a[10];
a[2]=100;
方法2:指针名加下标
int a[10];
int *p;
p=a;
p[2]=100; //因为p和a等价
补充:c语言规定:数组的名称就是数组的首地址,即第0个元素的地址,是一个常数。
注:p和a不同,p是指针变量,而a是常量。所以可用等号给p赋值,但不能给a赋值。(int a[10];a++是错误的)
方法3:通过指针运算加取值的方法来引用数组的元素:
int a[10];
int *p;
p=a;
*(p+2)=100; //相当于p[2]=100; p是第0个元素的地址,p+2是a[2]这个元素的地址。
代码3:
#include <stdio.h>
#pragma warning(disable:4996)
int main(int argc, char* argv[])
{
int a[5] = { 0,1,2,3,4 };
int* p;
p = a;
printf("a[2]=%d\n", a[2]);
printf("p[2]=%d\n", p[2]);
printf("*(p+2)=%d\n", *(p + 2));
printf("*(a+2)=%d\n", *(a + 2));
return 0;
}
结果:
a[2]=2
p[2]=2
*(p+2)=2
*(a+2)=2
指针的运算:
1.指针可以加一个整数,以下指几个方向指向的变量,结果还是地址。
2.两个相同类型的指针可以比较大小。(指向前面元素的指针小于指向后面元素的指针)。
3.两个相同类型的指针可以做减法(结果:两个指针指向的中间有多少个元素)。
指针数组:
1.指针和数组的关系:
指针可以保存数组元素的地址
可以定义一个数组,数组中有若干个相同类型的指针变量,这个数组被称为指针数组。
指针数组的概念:
指针数组本身是个数组,是个指针数组,是若干个相同类型的指针变量构成的集合。
(注:一般遇到这样的叠词,本质就是后者)
定义方法:
int *p[10]; //定义了一个整型的指针数组p,有10个元素p[0]-p[9];
每个元素都是int *类型的变量。
例:
int *p[10];
int a;
p[1]=&a;
int b[10];
p[2]=&b[3];
p[2]和*(p+2)是等价的,都是指针数组中的第2个元素。
代码4:
#include <stdio.h>
#pragma warning(disable:4996)
int main(int argc, char* argv[])
{
//大多数情况下,指针数组都用来保存多个字符串
char* name[5] = { "Follow me","BASIC","Greatwall","FORTRAN","Com" };
int i;
for(i=0;i<5;i++)
{
printf("%s\n", name[i]);
}
return 0;
}
结果:
Follow me
BASIC
Greatwall
FORTRAN
Com
指针的指针:即指针的地址。
int a=0x1234;
int *p=&a; *p=a
int **q=&p; **q=*P=a
代码5:
#include <stdio.h>
#pragma warning(disable:4996)
int main(int argc, char* argv[])
{
int a = 100;
//定义一个一级指针
//一级指针用于保存普通变量的地址
int* p = &a;
//定义一个二级指针
//二级指针用于保存一级指针的地址
int** q = &p;
printf("a=%d %d %d\n", a, *p, **q);
printf("&a=%d %d %d\n", &a, p, *q);
printf("&p=%d %d ", &p, q);
return 0;
}
结果:
a=100 100 100
&a=19921140 19921140 19921140
&p=19921128 19921128
字符串和指针:
字符串概念:以“\0”结尾的若干个字符的集合。
字符串存储形式:数组(在内存区开辟了一段空间存放字符串),字符串指针(在文字常量区开辟了一段空间存放字符串,将字符串的首地址赋值给str),堆(使用malloc函数在堆区申请空间,将字符串拷贝到堆区)。
1.char string[100]="I love C!"
str[0]='y';//正确可以修改
2.char *str="I love C" (几乎不用,str只能存放字符地址编号,所以说I love C不能存放在str指针变量中)
*str='y';//错误 存放在文字常量区,不可修改
3.char *str=(char *)malloc(10*sizeof(char));//动态申请了10个字节的储存空间(必须先开辟空间 再初始化)
strcpy(str,"I love C");
*str='y';//正确 因为堆区内容可修改
注:str指针指向的内容不能把修改,要看str指向哪里
str指向文字常量区的时候,内存里的内容不可修改;
str指向栈,堆,静态全局区的时候,内容的内容可以修改;
注:
char buf_aver[128];
buf_aver="helllo kitty"; //错误 因为字符数组的名字是个常量
strcpy(buf_aver,"hello kitty"); //正确
char *buf_aver;
buf_aver="helllo kitty"; //正确
strcpy(buf_aver,"hello kitty"); //错误
数组指针:
二维数组:可看成多个一维数组构成,可认为二维数组的每一个元素都是一个一维数组。
例:int a[3][5]; 定义了一个3行5列的二维数组,可认为由3个一维数组构成,每个元素是一个一维数组
回顾:数组的的名字是数组的首地址,是第0个元素的地址,是个常量,数组名字加1指向下个元素
二维数组a中,a+1指向下一个元素,即下一个一位数组,即下一行。
代码6:
#include<stdio.h>
int main(int arg, char* argv[])
{
int a[3][5];
printf("a=%p\n", a);
printf("a+1=%p\n", a + 1);
return 0;
}
结果:
a=0021F774
a+1=0021F788
数组指针本身是个指针,指向一个数组,加1跳一个数组,即指向下个数组。
数组指针的作用:保存二维数组的首地址。
定义方法:
int (*p)[5]; //定义了一个数组指针变量p,p指向的是整型的有5个元素的数组。
p+1往下指5个整型,跳过一个有5 个整型元素的数组。
代码7:
#include<stdio.h>
int main(int arg, char* argv[])
{
int a[3][5];
int(*p)[5];//定义了一个数组指针变量p,p+1跳一个有5个元素的整型数组
printf("a=%p\n", a);
printf("a+1=%p\n", a + 1);
p = a;
printf("p=%p\n", p);
printf("p+1=%p\n", p+1);
return 0;
}
结果:
a=00CFFA78
a+1=00CFFA8C
p=00CFFA78
p+1=00CFFA8C
数组指针用法:可以将二维数组的首地址传递给另一个函数里面,此时函数的形参就需要定义为数组指针。
代码8:
#include<stdio.h>
void fun(int(*p)[5], int x, int y)
{
p[0][1] = 101;
}
void test2()
{
int i, j;
int a[3][5] = {0};
fun(a, 3, 5);
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
printf("%d ", a[i][j]);
printf("\n");
}
}
int main(int arg, char* argv[])
{
test2();
return 0;
}
结果:
0 101 0 0 0
0 0 0 0 0
0 0 0 0 0
各种数组指针的定义:
1.一维数组指针,加1后指向下一个一维数组
2.二维数组指针,加1后指向下一个二维数组
…
注意:
1.指针数组:是个数组,由若干个相同类型的指针构成集合
int *p[10]; //数组p有10个int *类型的指针变量构成,分别为p[0]-p[9];
2.数组指针:本身是指针,指向一个数组,加1跳一个数组。
int (*p)[10];
p是个数组指针
3.指针的指针:
int **P;
int *q;
p=&q;
8.数组名字区地址:变成数组指针。
一维数组名字取地址,变成一维数组指针,加1跳一个一维数组。
int a[10];
a+1跳一个整型元素,是a[1]的地址。
a和a+1相差一个元素,4个字节,
&a就变成了一个一维数组指针,是int (*p)[10]类型的。
(&a)+1就和&a相差一个数组即10个元素即40个字节。
9.数组名字与指针变量的区别:
int a[10];
int p;
p=a;
相同点:
a是数组名,是a[0]的地址,p=a;即p也保存了a[0]的地址,即a和p都指向a[0],所以在引用数组元素时,
a和p等价。
a[2],(a+2),p[2],*(p+2)都是对数组a中a[2]元素的引用。
不同点:
1.a是常量,p是变量,可以用等号"="给p赋值,但不能给a赋值。
2.对a取地址,和对p取地址结果不同。
1.指针与函数的关系(传地址):
代码9:
#include<stdio.h>
void fun(int *p, int *q)
{
int temp = *p;
*p = *q;
*q = temp;
printf("in fun:*p=%d ", *p);
printf("*q=%d \n", *q);
}
void test1()
{
int a, b;
a = 10;
b = 20;
printf("before fun:a=%d ", a);
printf("b=%d\n ", b);
fun(&a, &b);
printf("after fun:a=%d ", a);
printf("b=%d \n", b);
}
int main(int arg, char* argv[])
{
test1();
return 0;
}
结果:
before fun:a=10 b=20
in fun:*p=20 *q=10
after fun:a=20 b=10
2.指针与函数的关系(传数组)
代码10:
#include<stdio.h>
//传一维数组
//void fun1(int p[])
void fun1(int *p)
{
printf("%d\n", p[2]);
printf("%d\n", *(p + 2));
}
void test1()
{
int a[10] = { 1,2,3,4,5,6,7,8 };
fun1(a);
}
//传二维数组
//void fun1(int p[][])
void fun2(int(*p)[4])
{
printf("%d\n", p[0][2]);
printf("%d\n", *(*(p+1) + 2));
}
void test2()
{
int a[2][4] = { 1,2,3,4,
5,6,7,8 };
fun2(a);
}
int main(int arg, char* argv[])
{
test2();
return 0;
}
结果:
3
7
3.指针与函数的关系:指针函数
指针作为函数的返回值。
代码11:
#include<stdio.h>
char* fun()
{
static char str[100] = "hello world";
return str;
}
void test()
{
char* p;
p = fun();
printf("p=%s\n", p);
}
int main(int arg, char* argv[])
{
test();
return 0;
}
结果:
p=hello world
注:栈区开辟的空间会随着当前代码段的结束而释放空间
静态区的空间不会随着当前代码段的结束而释放空间。
4.指针与函数的关系:函数指针
指针保存函数的地址。
c语言规定:函数的名字就是函数的首地址,即函数的入口地址,我们可以定义一个指针变量,来存放函数的地址。这个指针变量就是函数的指针变量。
定义方法: int (*p)(int,int); //定义了一个函数指针变量p,p指向的函数
必须有一个整型的返回值,有两个整型参数。
函数指针数组:本质是一个数组,数组里面的每个元素都是一个函数指针。
定义:
int (*p10;
定义一个函数指针数组,有10个元素p[0]-p[9],每个元素都是函数指针变量,。。。
函数指针最常用的地方:给函数传参。
函数指针最常用的地方就是将一个函数作为参数传递给另一个函数的时候要使用函数指针
将一个函数作为参数传递给另一个函数,将这个函数称为回调函数。
代码12:
#include<stdio.h>
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mux(int x, int y)
{
return x * y;
}
int dive(int x, int y)
{
return x / y;
}
int process(int (*p)(int, int), int a, int b)
{
int ret;
ret = (*p)(a, b);
return ret;
}
int main(int arg, char* argv[])
{
int num;
num = process(add, 1, 8);
printf("num=%d", num);
return 0;
}
结果:
num=9