C语言—指针高级

指针引用二维数组元素

在C语言中,一个二维数组可以看成是一个一维数组,其中每个元素又是一个包含若干元素的一维数组。

例如: int a[3][5];

a[0]、a[1]和a[2]分别是包含三个元素的一维数组名,分别代表a数组元素的起始地址(即a[0]是第0行元素的首地址, a[1]是第1行元素的首地址)。
a[i]*(a+i)(无条件等价)都是第i行第0列元素的地址,那么a[i]+j、*(a+i)+j&a[0][0]+5*i+j都是第i行第j列元素的地址。

二维数组的引用的三种方式:

例如:

int a[3][5],(*p)[5];
p = a;

①下标法。如a[i][j]p[i][j]
②指针针法,如*(*(p+i)+j)*(*(a+i)+j)

示例:

#include <stdio.h>
#include <stdlib.h>
void main()
{
    
    
	int aiNum[3][3]=
   {
    
    {
    
    1,2,3},{
    
    4,5,6},{
    
    7,8,9}};
    int(*p)[3];
    int i,j;

	p = aiNum;
	printf("*(*(p +i)+j) = \n");
	for (i =0;i < 3;i++)
    {
    
    
	     for(j=0;j<3;j++)
	     printf("%d\t",*(*(p +i)+j));
    }
	putchar('\n');
	printf("*(p[i]+j) = \n");
	for (i =0;i < 3;i++)
	{
    
    
		 for(j=0;j<3;j++)
		 printf("%d\t",*(p[i]+j));
	}
	putchar('\n');
	printf("*(&p[0][0]+i*3+j) = \n");
	for (i =0;i < 3;i++)
	{
    
    
		 for(j=0;j<3;j++)
		printf("%d\t",*(&p[0][0]+i*3+j));
	}
	putchar('\n');
	printf("(*(p+i))[j] = \n");
	for (i =0;i < 3;i++)
	{
    
    
		 for(j=0;j<3;j++)
		printf("%d\t",(*(p+i))[j]);
	}
	putchar('\n');
}

结果:
*(*(p +i)+j) =
1       2       3       4       5       6       7       8       9
*(p[i]+j) =
1       2       3       4       5       6       7       8       9
*(&p[0][0]+i*3+j) =
1       2       3       4       5       6       7       8       9
(*(p+i))[j] =
1       2       3       4       5       6       7       8       9

二维数组作为函数参数

①当二维数组名作为函数实参时,对应的形参必须是一个行指针变量

书写格式:

int main()
{
    
    
	int iNum[3][4];
	……
	fun(iNum);
	……
}
fun(int (*iNum)[n])
{
    
    
	……
} 

②和一维数组一样,数组名传送给变量的是一个地址值,因此,对应的形参也必须是一个类型相同的指针变量,在函数中引用的将是主函数中的数组元素,系统只为形参开辟一个存放地址的存储单元,而不可能在调用函数时为形参开辟一系列存放数组的存储单元。

指针数据类型

指针数组

①指针数组就是其元素为指针的数组

②每一个元素都是指针变量

说明指针数组的语法格式为:数据类型 *指针数组名[常量表达式];

例如: int *p1[6];

③指针数组主要用于字符串的操作

例如:char *name[3]={
    
    “Rose”, “Smith”, “John”};

注意:与一个指向二维数组的指针变量的区别,int (*p1)[6]和int *p1[6]之间的区别

指针数组示例:

#include <stdio.h>
int main()
{
    
    
	int i;
	char *a[5] ={
    
    "aaa","bbbb","ccc","dd","ee"};
	printf("a = ");
	for (i = 0;i < 5;i++)
	{
    
    
		puts(a[i]);
	}
}

结构体指针

①结构体指针是通过在结构体变量名前放置一个星号(*)来进行声明的

-> 运算符用于通过指针来访问结构体的元素

示例:

#include <stdio.h>

struct stStudent
{
    
    
	char acName[10];
	int iAge;
};
int main()
{
    
    
	struct stStudent stStu = {
    
    "小明",18};
	struct stStudent *pstStu;
	pstStu = &stStu;
	printf("输出结果:%s", pstStu ->acName);
}
结果:
输出结果:小明

结构体指针变量作为参数

示例:(注意结构体作为参数的书写方式以及调用结构体的值的方式)

#include <stdio.h>
#include <string.h>
struct cat
{
    
    
	char bk_name[25],author[20];
	int edn;
	float price; 
};
void UpdateBook(struct cat *book)
{
    
    
	book->edn=12;
	(*book).price=(float)12.4;
	strcpy(book->bk_name,"C Program");
	strcpy((*book).author,"Tom");
}
void PrintBook(struct cat book)
{
    
    
	printf("name:%s\nprice:%f\nauther:%s\nedn:%d\n",book.bk_name,book.price,book.author,book.edn);	
}
void main()
{
    
    
	struct cat book={
    
    "abc","John",14,10.5};
	PrintBook(book);
	UpdateBook(&book);
	PrintBook(book);
}

结果:
name:abc
price:10.500000
auther:John
edn:14
name:C Program
price:12.400000
auther:Tom
edn:12

指针的指针

一种变量专门用来存放指针变量的地址,这种变量我们称之这指针的指针变量
语法定义:type **name;

示例:

#include <stdio.h>
#include <string.h>

void main()
{
    
    	
	int i=6,*p,**dp;
	p=&i;
	dp=&p;   //p保存i的地址,*dp保存p的值,dp保存p的值(p的值即i的地址)
	printf("i = %d, *p = %d, **dp = %d\n",i,*p,**dp);
	printf("&i = %x, p = %x, *dp = %x\n",&i,p,*dp); 
	printf("&p = %x, dp = %x\n",&p,dp);
}

结果:
i = 6, *p = 6, **dp = 6
&i = 19ff2c, p = 19ff2c, *dp = 19ff2c
&p = 19ff28, dp = 19ff28

函数指针(注意与指针函数区别)

函数指针其本质是一个指针变量,该指针变量指向一个函数。C程序在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。

声明一个函数指针(示例):

/*声明一个函数指针 */
int (*fptr) (int, int);
/* 函数指针指向函数func */
fptr = func;  // 或者fptr = &func;

func是一个函数名,那么func与&func都表示的是函数的入口地址。

在函数的调用中可以使用:方式一:func(),也可以使用方式二:(*fun)()。这两种调用方式是等价的,只是我们平时大多都习惯用方式一的调用方法。

提问:至为什么func与&func的含义相同?
答:对于函数func来说,函数的名称就是函数代码区的常量,对它取地址(&func)可以得到函数代码区的地址,同时,func本身也可以视为函数代码区的地址。因此,函数名称和对其取地址其含义是相同的。

示例1 :

#include<stdio.h>
int fun(int a, int b, int (*call)(int, int))
{
    
    
	return (call(a, b));
}
int max(int v1, int v2)
{
    
    
    return (v1 > v2 ? v1 : v2);  //如果v1>v2 成立输出v1 不成立输出v2
}
int main()
{
    
    
    printf("max=%d\n", fun(1, 2, max));
    return 0;
}

结果:
max=2

示例 2 :

#include <stdio.h>
int add(int a, int b);
int main(void)
{
    
    
	int (*fptr)(int, int); //定义一个函数指针
	int res;
	fptr = add;  //函数指针fptr指向函数add
	/* 通过函数指针调用函数 */
	res = (*fptr)(1,2); //等价于res = fptr(1,2);
	printf("a + b = %d\n", res);
	return 0;
}
int add(int a, int b)
{
    
    
	return a + b;
}

结果:
a + b = 3

指针函数(注意与函数指针区别)

指针函数:顾名思义,它的本质是一个函数,不过它的返回值是一个指针。

声明示例:int *pfun(int, int);
由于“*”的优先级低于“()”的优先级,因而pfun首先和后面的“()”结合,也就意味着,pfun是一个函数。接着再和前面的“*”结合,说明这个函数的返回值是一个指针。由于前面还有一个int,也就是说,pfun是一个返回值为整型指针的函数。

示例:

#include <stdio.h>
//这是一个指针函数的声明
int *pfun(int *arr, int n);
//主函数中,把一个数组的首地址与数组长度作为实参传入指针函数pfun里,把指针函数的返回值(即指向数组的指针)赋给整形指针p。最后使用指针p来遍历数组元素并打印输出。
int main(void)
{
    
    
	int array[] = {
    
    0, 1, 2, 3, 4};
	int len = sizeof(array)/sizeof(array[0]); //数组的元素个数
	int *p;
	int i;
	 //指针函数的调用
	p = pfun(array, len);
	for(i = 0; i < len; i++)
	{
    
    
		printf("array[%d] = %d\n", i, *(p+i));
	}
		return 0;
}

//这是一个指针函数,其返回值为指向整形的指针
int *pfun(int *arr, int n)
{
    
    
	int *p = arr;
	return p;
}

结果:
array[0] = 0
array[1] = 1
array[2] = 2
array[3] = 3
array[4] = 4

小结

在这里插入图片描述

无类型指针

①无类型指针可以指向任何类型的数据

②无类型指针定义:void *p;

③可以将任意类型的指针(包括函数指针)直接赋给无类型指针,但不能将无类型指针直接赋给其它类型指针,例如:

int *q,*m;
void *p;
p = q;  //只获得变量/对象地址而不获得大
m = (int *)p;

④不能将无类型指针参与算术运算,例如:

void *p;
p++;//错误,进行算法操作的指针必须是确定知道其指向的数据类型大小 
((int *)p) ++; //正确,能通过

⑤无类型指针主要作用:

  • 对函数返回的限定(malloc函数)
  • 对函数参数的限定 (memcpy函数)

代码示例:

#include<iostream>
#include<string>
using namespace std;
 
typedef struct tag_st
{
    
    
    string id;
    float fa[2];
}ST;
 
int main()
{
    
    
    ST* P = new ST;
    P->id = "hello!";
    P->fa[0] = 1.1;
    P->fa[1] = 2.1;
 
    ST* Q = new ST;
    Q->id = "world!";
    Q->fa[0] = 3.1;
    Q->fa[1] = 4.1;
 
    void * plink = P;
    *(ST*)(plink) = *Q;  //plink要先强制转换一下,目的是为了让它先知道要覆盖的大小
    //P的内容被Q的内容覆盖
    cout << P->id << " " << P->fa[0] << " " << P->fa[1] << endl;  
    return 0;
}

结果:
world! 3.1 4.1

const限定符

①可用于不允许被修改的变量和形式参数,数据保护
示例:char *strcpy(char *str1,const char *str2);

②声明const变量时需要初始化
示例:const int stuNum = 100;

const和形式参数

用于限定函数形式参数,以保护实参
示例:

void output(const double * pd) 
	{
    
    
		printf(%d”, *pd);		/* 允许 */
		*pd = 15.5 ;  		/* 不允许! */
	}

指向常量数据的指针(注意与指针常量区别)

①指针的值可以改变,无法通过指针修改指向的内容

②关键字const放在指针类型前

示例:

int i, j, *q;
const int * p;    	/* 等价于int const *p; */
p = &j;		/* 允许 */
p = &i;		/* 允许 */
i = 10;		/* 允许 */
*p = 5 ;		/* 不允许 */ 

指针常量

①指针常量又称为常指针

②指针的值不能修改,指向的内容可以修改

③关键字const放在“*”号和指针名之间

例如:

int var1,var2 ;
int * const p = &var1 ;
*p = 5 ;		/* 允许 */
p = &var2 ;	/* 不允许 */

猜你喜欢

转载自blog.csdn.net/qq_46485161/article/details/115037692