黑马程序员—C语言的指针

时间:2021-04-15 00:24:00

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
一、指针是C语言中的一个重要概念,也是C语言的一个重要特色,是C语言的灵魂。
1、指针: 1)指针的概念:内存单元的编号也叫地址,内存单元的地址即为指针。
2)指针的重要性和优势:
1>为函数提供修改调用变量的灵活手段
2>使函数可以有一个以上的返回值
3>可以改善某些子程序的效率:在数据传递时,如数据块较大(比如说数据缓冲区或比较大的结构),这时就可以通过指针传递地址,而不是实际数据,即提高传输速度,又可以节省大量内存。
4>为动态数据结构(二叉树、链表)提供支持
5>可以直接访问计算机的硬件:
6>能方便的处理字符串

2、指针变量: 1)指针变量的概念:用一个变量存放另一个变量的地址(或叫指针)。
2)定义指针变量: 类型说明符 * 变量名 如: int *p 是定义了一个指向整型变量的指针
3)注意:必须有” * “字符;只能存放指针(地址):指针变量也是变量,也有全局变量和局部变量之分。
4)指针变量的初始化:分为定义的同时初始化和先定义后初始化。可以完全初始化,也可以部分初始化。
int *p =&a ; 即p指向了a
int *p =NULL ; 或 int *p =0 ; 即指针无指向,p为空指针。
int *p =1000 ; 一定要杜绝,不能赋值,只能赋地址
未初始化的指针里存放的是一个垃圾值,称为野指针。
5)使用: * 指针变量:获取指针变量指向的内存空间的内容。&是取地址运算符。
例:通过指针变量访问整型变量:

#include <stdio.h>
void main()
{
int a,b;
int * pointer_1, * pointer_2;
a=100;
b=10;
pointer_1=&a; //把变量a的地址赋给pointer_1
pointer_2=&b; //把变量b的地址赋给pointer_2
printf("%d,%d,%d,%d\n",a,b,*pointer_1,*pointer_2);
}

运行结果为:100,10 ,100,10

6)指针变量作为函数参数:函数的参数不仅可以是整型、浮点型、字符型等数据,还可以是指针类型。它的作用是将一个变量的地址传送到另一个函数中。
例:输入a和b两个数,按从大到小的顺序输出:

#include <stdio.h>
void main()
{
void swap(int * p1, int * p2); //被调用函数声明
int a,b;
int * pointer_1, * pointer_2;
scanf("%d,%d",&a,&b);
pointer_1=&a;
pointer_2=&b;
if(a<b) swap(pointer_1,pointer_2);
printf("\n%d,%d\n"a,b);
}

void swap(int * p1, int * p2 ){
int temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}

3、二级指针:一个指针变量存放的是另一个指针变量的地址,则称这个指针变量是一个指向指针的指针变量。

4、指针类型: int char double float void(空类型)类型的指针。因为不同指针类型所占内存的字节数不同,所以定义什么类型的指针就应该指向什么类型的变量。否则会出错。

5、数组指针:1)一个变量有地址,一个数组包含若干个元素,每个数组元素都有相应的地址。
2)指针变量可以指向数组元素: int *p ; p=&a[0];
3)所谓数组元素的指针就是数组元素的地址
4)数组名不代表整个数组,只代表数组首元素的地址
6)使用数组指针:在指针指向数组元素时,允许以下运算:
加(减)一个整数: p+1 ; p-1 ; p++ ; p– ; ++p ; –p ;
p1 - p2 : 两个数组指针相减,只有p1和p2都指向同一数组元素时才有意义。
7)如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址。或称它们指向数组a序号为i的元素。
8)数组指针的初始化: int *p ; int a[4]={1,2,3,4}; p=a; p=&a[0] ;
p+1是p指向数组的下一个元素; p-1是p指向数组的上一个元素。
* (p+1)= * (a+1)
9)引用一个数组元素,可用下面两种方法:一是下标法,如a[i]的形式;二是指针法,如* (a+i) 或 * (p+i)
10)学习误区:a是常量(a++的写法错误);p是变量(p++的写法正确)
例:用指针将数组a中的n个整数按逆序存放。

#include <stdio.h>
void nixuArray(int a[], int len){
int * p;
p=a; //数组指针
//定义下标
int i=0,j=len-1;
int temp;
while(i<j){
temp=*(p+i);
*(p+i)=*(p+j);
*(p+j)=temp;
i++,j--; //修改下标
}
}

int main(){
int arr[10]={1,2,3,4,5,6,7,8,9,0};
nixuArray(arr,10);
for(int i=0; i<10; i++){
printf("%d\t",arr[i]);
}
return 0;
}

输出结果为:0 9 8 7 6 5 4 3 2 1

6、一维数组指针:一个数组的元素值为指针则是指针数组。
指针数组是一组有序的指针的集合。指针数组的所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量。指针数组阐明的一般形式为:
类型说明符 * 数组名 [数组长度]
其中类型说明符为指针值所指向的变量的类型。例如:
int *pa[3]
表示pa是一个指针数组,它有三个数组元素,每个元素都是一个指针,指向整型变量。

7、两指针变量减法运算: 只有指向同一数组的两个指针变量之间才能进行运算,否则毫无意义。
1)两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。
实际上是两个指针值(地址)相减之差再除以该数组元素的长度(字节数)。
(pointer2地址值-pointer地址值)/sizeof(所指向数据类型)
2)常见的用法:两个指针都指向同一个数组
1>判断两个指针变量指向的元素是否连续,如果连续结果是1或-1。相同元素结果是0。
2>判断两个指针变量之间相隔几个元素,相隔n个元素结果就是n。
3>没有加法运算,只有减法运算。
3)两个指针变量之间的关系运算:如p1>p,则表示p1在高位,否则表示p在高位,或同一个位置。
8、二维数组:1)用数组名访问二维数组:
黑马程序员—C语言的指针
遍历输出二维数组: for(int i=0; i<3; i++) {
for(int j=0; j<4; j++){
printf(“%d\t”, *(a[i]+j) ); //等价于下面语句
printf(“%d\t”, ( (a+i)+j) );
}
printf(“\n”);
}
2) 二维数组指针变量说明的一般形式为:
数据类型 (*指针变量名)[二维数组列数] ;
其中,“类型说明符”为所指数组的数据类型。 “*”表示其后的变量是指针类型。
如要将二维数组赋给一指针,应这样赋值:
int a[3] [4] ;
int (*p) [4] ; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a ; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]。
p++ //该语句执行过后,也就是p=p+1,p跨过行a[0][ ],指向了a[1][ ]
所以数组指针也称指向一维数组的指针,亦称行指针。
行指针,用来指向二维数组的每一行,存放的是行的首地址。
3)输出二维数组任一行任一列元素的值:

#include <stdio.h>
void main(){
int a[3][4]={1,3,5,7,9.11.13.15,17,19,21,23};
int (*p)[4]; //p是指向具有4个元素的一维数组的指针
int i, j;
p=a;
scanf("i=%d,j=%d",&i,&j);
printf("a[%d,%d]=%d\n",i,j,*(*(p+i)+j));
}

运行情况如下:i=1,j=2 (本行为键盘输入)
a[1,2]=13

4)用指向数组的指针作函数参数:

#include <stdio.h>
void main(){
void average(float *p, int n);
void search(float (*p)[4], int n);
float score[3][4]={{65,67,53,70},{89,78,98,76},{90,95,93,100}};
average(*score,12); //求12个分数的平均值
search(score,2); //求序号为2的学生成绩
}

void average(float *p, int n){
float *p_end;
float sum=0,aver;
p_end=p+n-1;
for(; p<=p_end; p++)
sum=sum+(*p);
aver=sum/n;
printf("average=%5.2f\n",aver);
}

void search(float (*p)[4], int n){ //p是指向具有4个元素的一维数组的指针
int i;
printf("the score of No.%d are:\n",n);
for(i=0; i<4; i++)
printf("%5.2f", *(*(p+n)+i);
}

程序运行结果如下:average=81.16
the score of No.2 are:
90.00 95.00 93.00 100.00

9、字符串与指针:字符串指针指向字符串
char *变量名=“字符串内容”; //字符串内容”是常量
字符串指针变量的定义说明说明与指向字符变量的指针变量说明是相同的。只能按对指针变量赋值不同来区别。对指向字符变量的指针变量应赋予该字符变量的地址。
如: char c, *p=& c ;
注意:1)使用字符指针来保存字符串,它保存的是字符串常量的地址,常量区是只读的,所以我们不可以修改字符串里的内容。2)不能直接赋值,只能赋一个地址,当字符指针有了指向(地址)后,才可以向它指向的地址里赋值。如: char * a, str[100]; a=str; scanf(”%d”,a); 这种赋值是正确的。

10、指向函数的指针:1)指向函数的指针变量的一般定义形式: int (*p)(int, int)
(函数返回值的类型的数据类型) 数据类型 (* 指针变量名)(函数参数表列);
2)用指向函数的指针作函数参数:
例如: void sub(int (* x1) (int), int (* x2) (int, int) ){
int a, b, i, j;
a=(* x1)(i); //调用f1函数
b=(* x2)(i, j); //调用f2函数
}
11、返回指针值的函数:函数返回的值的类型是指针类型。
一般的定义形式为: 类型名 *函数名 (参数表列); 如: int *a(int x, int y);

12、指针数组:一个数组,其元素均为指针类型数据,每个元素都相当于一个指针变量。
一般定义形式为: 类型名 *数组名[数组长度]; 如: int *p[4];
注意: int (*p)[4] 是指向一维数组的指针变量。
例:指针数组的元素指向整型数据。

#include <stdio.h>
void main(){
int a[5]={1,3,5,7,9};
int *num[5]={&a[0],&a[1],&a[2],&a[3],&a[4]};
int **p,i;
p=num;
for(i=0; i<5; i++){
printf("%d", **p);
p++;
}
printf("\n");
}

运行结果为:1 3 5 7 9