内容目录
指针引用二维数组元素
在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 ; /* 不允许 */