一 、 指针是什么?
1.指针是C语言中非常重要的数据类型,如果你说C语言中除了指针,其他你都学得很好,那你干脆说没学过C语言。
2.指针和内存联系很紧密。
2.1.一个bit就是一个二进制位。一个字节=8bit
3.关于内存相关(baidu)
3.1.编译时分配内存 : 编译时是不分配内存的。
此时只是根据声明时的类型进行占位,到以后程序执行时分配内存才会正确。
所以声明是给编译器看的,聪明的编译器能根据声明帮你识别错误。
3.2.运行时分配内存 :这是对的,运行时程序是必须调到“内存”的。
因为CPU(其中有多个寄存器)只与内存打交道的。
程序在进入实际内存之前要首先分配物理内存。
3.3.编译过程: 只能简单说一下,因为如果要详细的话,就是一本书了《编译原理》。
编译器能够识别语法,数据类型等等。
然后逐行逐句检查编译成二进制数据的obj文件,然后再由链接程序将其链接成一个EXE文件。
此时的程序是以EXE文件的形式存放在磁盘上。
3.4.运行过程: 当执行这个EXE文件以后,此程序就被加载到内存中,成为进程。
此时一开始程序会初始化一些全局对象,然后找到入口函数(main()或者WinMain()),就开始按程序的执行语句开始执行。
此时需要的内存只能在程序的堆上进行动态增加/释放了。
4.通常的机器都有一系列连续编号或编制的存储单元,通过这些存储单元可以单个进行操纵,可以以连续成组的方式操纵。
不同类型所占用的存储空间如下图
二 、 指针有什么用?
指针是一种保存地址的变量,在C语言中,指针常常是表达某个计算的唯一途径,另外指针通常可以生成更高效、更紧凑的代码。
三 、 指针怎么用?
- 定义的格式
- 类名标识符 *指针变量名;
- int *p;//声明
- 先定义后赋值
- 简单取值
int a = 10;
int *p;
p = &a;
printf(“%d”, *p); - 简单改值
*p = 9;
- 简单取值
- 定义的同时赋值
int a = 10;
int *p = &a; - 清空指针
- p = 0;
- p = NULL;
四 、 使用指针要注意什么?
1.指针只能指向某种特定类型的数据,也就是说,每个指针都必须指向某种特定的数据类型
2.地址运算符&只能应用于内存中的对象,即变量与数组元素,它不能作用于表达式、常量或register类的变量
3.一元运算符*是间接寻址或间接引用运算符。当它作用于指针时,将访问指针所指向的对象那
五 、 指针应用代码举例
- 例1 指针声明与引用
int x=1,y=2,z[10];
int *ip;//ip是一个指向整型int的指针
ip=&x;//ip指向x
y=*ip;//y现在的值是1
*ip=0;//x现在的值是0
ip=&z[0];//ip现在指向z[0]
- 例2 指针与函数参数:交换两个次序颠倒的主元素
*由于C语言是以传值的方式将参数值传递给被调用函数,因此,别调用函数不能直接修改主调函数中变最的值。
如:
void swap(int x,int y)
{
int temp;
tmep = x;
x = y;
y = temp;
}
则下列语句无法达到目的。
swap(a,b);
*这时,可以使主调程序将指向所要交换的变量的指针传递给被调用函数 即
swap(&a,&b);
函数定义如下
void swap(int *px,int *py)
{
int temp;
tmep = *px;
*px = *py;
*py = temp;
}//可以达到目标
- 例3 指针与数组
// 初始化数组
int numbers[5] = {1,2,3,4,5};
// 标准数组表示法
int *ptr1 = numbers;
int val1 = numbers[0];
// 数组表示法取地址
int *ptr2 = &numbers[0];
int val2 = *(&numbers[0]);
// 指针加偏移表示法
int *ptr3 = numbers + 0;
int val3 = *(numbers + 0);
// 输出指针中的地址
printf("*ptr1 = %p\n", (void *)ptr1); //结果:0x7fff6be1de60
printf("*ptr2 = %p\n", (void *)ptr2); //结果:0x7fff6be1de60
printf("*ptr3 = %p\n", (void *)ptr3); //结果:0x7fff6be1de60
// 输出地址指向的int值
printf("val1 = %d\n", val1); //结果:1
printf("val2 = %d\n", val1); //结果:1
printf("val3 = %d\n", val1);//结果:1
//所有值都是相同的
- 例4 地址算术运算
// 初始化数组
intnumbers[5] = {1,2,3,4,5};
inti = 0;
// 用数组表示法输出元素
for(i = 0; i < 5; i++ ) {
intvalue = numbers[i];
printf("numbers[%d] = %d\n", i, value);
}
// 用指针加偏移输出元素
for(i = 0; i < 5; i++ ) {
intvalue = *(numbers + i);
printf("*(numbers + %d) = %d\n", i, value);
}
// 仅用一个指针输出元素
int*ptr = numbers;
for(i = 0; i < 5; i++ ) {
intvalue = *ptr++;
printf("%d, *ptr++ = %d\n", i, value);
}
/*结果:
numbers[0] = 1
numbers[1] = 2
numbers[2] = 3
numbers[3] = 4
numbers[4] = 5
*(numbers + 0) = 1
*(numbers + 1) = 2
*(numbers + 2) = 3
*(numbers + 3) = 4
*(numbers + 4) = 5
0, *ptr++ = 1
1, *ptr++ = 2
2, *ptr++ = 3
3, *ptr++ = 4
4, *ptr++ = 5
//所有过程得到了相同的结果*/
- 例5 字符指针与函数:复制字符串
//用数组实现
void strcpy(char *s, char *t)
{
int i;
i = 0;
while ((s[i] = t[i]) != '\0')
i++;
}
//用指针实现
void strcpy(char *s, char *t)
{
int i;
i = 0;
while ((*s = *t) != '\0') {
s++;
t++;
}
}
- 例6 指针数组以及指向指针的指针
int main()
{
int a[5]={1,3,5,7};
int *n[4]={&a[0],&a[1],&a[2],&a[3]};//指针数组
int **p,i;//**p为指向指针的指针
p=n;
for(i=0;i<4;i++)
{
printf("%d\t",**p);p++;
}
return 0;
}
结果:1 3 5 7
- 例7 指向函数的指针:比较大小
#include <stdio.h>
#include <stdlib.h>
int main()
{
int max(int,int);
int (*p)(int,int);//定义函数指针
int a,b,c;
p = max;//指向函数
scanf("%d,%d",&a,&b);
c = (*p)(a,b);//用指针调用函数
printf("a=%d,b=%d,max=%d\n",a,b,c);
return0;
}
int max(int x,int y)
{
int z;
if(x>y)
z = x;
else
z = y;
return(z);
}
六 、指针总结
- **主题 1:指针**
1.将地址形象化地称为“指针”。意思是通过它能找到以它为地址的内存单元。
2.而地址指向内存单元
3.在程序中一般通过变量名来引用变量的值,因为通过变量名能找到相应存储单元的地址。
4.所有含有变量的语句,都是通过变量先找到其值,再去操作的。这种直接按变量名进行的访问,叫直接访问。
5.间接访问:将变量i的地址存放在另一个变量中,然后通过该变量来找到变量i的地址,从而访问i变量。
6.指向就是通过地址来体现的。
7.一个变量的地址就是叫做该变量的“指针”。
8.一个专门用来存放另一个变量的地址(即指针)则称它为指针变量。于是指针变量的值是地址。地址对应一个数,变量对应一个名。
9.指针变量,用来指向另一个对象(如变量,数组,函数等)。
- **主题 2:定义指针**
1.定义指针变量:int * p;基类型 指针运算符 变量名
2.关于基类型
2.1.不同类型的数据在内存中所占的字节数和存放方式是不同的。就是说类型控制了数据在内存占的字节数
2.2.当取数据时,知道地址,然后还知道数据所占的字节数,才能准确把要的数据去出来。不然后有可能取多或取少。
2.3.类型和地址共同决定了数据位置信息。
2.4.于是指针移动一个位置,就是移动了其对应类型占用字节数,整型就是跨4个字节,字符就是跨1个字节,跳到了第二个变量的地址。
2.5.整型:4个字节 字符型:1个字节
2.6.一个变量的指针有两个方面:表示地址;指向类型 比如 int * p; p是一个指向整型数据的指针变量。
- **主题 3:引用指针**
1.引用指针变量
1.1.给指针赋值 p=&a;
1.2.引用地址
2.指针变量作为函数参数
2.1.作用是将一个变量的地址传送到另一个函数中
3.通过指针引用数组
4.数组的指针和数组元素的指针不同的
4.1.指针变量指向一个数组元素:int *p=&a[0];
5.现在引用数组元素:1)下标法2)指针法
6.使用指针法能使目标程序质量高(占内存少,运行速度快)。
7.在C语言,数组名代表数组中首元素的地址,所以数组名可以直接赋给指针变量。
8.在引用数组元素时指针的运算。
9.在引用数组元素时指针的运算。
9.1在指针指向数组元素时,可以对指针进行以下运算:1)加减一个整数2)自加自减3)同数组指针相减
10.通过指针引用数组元素
1)下标法2)指针法 *(a+i)
10.1)a[i]
10.2)通过数组名和元素序号计算数组元素地址,找出元素的值。 *(a+i)
10.3)用指针变量指向数组元素
for(p=a;p<(a+10);p++)
printf("%d",*p);
11.注意
111.在使用指针变量指向数组元素时,应切实保证指向数组中有效的元素
11.2.指向数组的指针变量也可以带下标,但是必须弄清楚该指针变量的当前值是什么?
11.3.*p++:++和*同优先级 结合方向是自右向左。将先引用p的值,实现*p的运算,然后再使p自增1.
12.用数组名作函数参数
12.1.C语言调用函数时虚实结合的方法都是采用“值传递”方式,当用变量名作为函数参数时传递的是变量的值,
当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量。
12.2.在C语言中用下标法和指针法都可以访问一个数组,用下标法表示比较直观,便于理解。因此许多人愿意用数组名
作形参。
13.形参数组名实际上是一个指针变量,并不是真正地开辟一个数组空间。
14.如果有一个实参数组,想要在函数中改变此数组的元素的值,实参与形参的对应关系如下:实质是地址传递
14.1.形参和实参都用数组名
14.2.实参用数组名,形参用指针变量
14.3实参形参都用指针变量
14.4.实参是指针变量,形参是数组名
15.通过指针引用多维数组
15.1多维数组元素的地址
16.关于a[i]性质说明
16.1.从形式上看是a数组中序号为i的元素。
16.2.当a是一维数组名,则代表a数组序号为i的元素的存储单元,这时它是有物理地址的,是占存储单元的
16.3.当a是二维数组,则它是一维数组名,只是一个地址,并不代表某一元素的值(如同一维数组名只是一个指针常量一样)。
-**主题4:指向函数的 指针**
1.返回指针的函数
指针也是C语言中的一种数据类型,因此一个函数的返回值肯定可以是指针类型的
返回指针的函数的一般形式为:类型名 * 函数名(参数列表)
2.指向函数的指针: 为什么指针可以指向一个函数?
2.1.函数作为一段程序,在内存中也要占据部分存储空间,它也有一个起始地址,即函数的入口地址。
函数有自己的地址,那就好办了,我们的指针变量就是用来存储地址的。因此,可以利用一个指针指向一个函数。
其中,函数名就代表着函数的地址。
2.2.指向函数的指针的定义:定义的一般形式:函数的返回值类型 (*指针变量名)(形参1, 形参2, ...);
3. 使用注意
3.1由于这类指针变量存储的是一个函数的入口地址,所以对它们作加减运算(比如p++)是无意义的
3.2指向函数的指针变量主要有两个用途:1)调用函数2)将函数作为参数在函数间传递