一个变量的地址称为该变量的 “ 指针 ”
如果有一个变量用来存放另一个变量的地址,称为 “ 指针变量 ”
指针变量就是地址变量
指针变量的值是地址
内存区的每一个字节有一个编号,这就是内存单元的 “ 地址 ”
通过地址能找到所需的变量单元,地址指向该变量单元
在 C 语言中,将地址形象化地称为 “ 指针 ”
按 C 语言的规定,可以定义一种特殊的变量,用于 存放地址 : point = &a ,表示变量 point 存放变量 a 的内存地址
直接按变量名进行访问称为 “ 直接访问 ”
将变量 a 的地址存放在另一个变量中,称为 “ 间接访问 ”
所谓指向就是通过地址来体现的
通过指针变量访问整型变量
#include
int main(){
int a,b;
int *pointer_1,
*pointer_2;
a = 100;
b = 10;
pointer_1 = &a;
pointer_2 = &b;
printf("a = %d,b = %d\n",a,b);
printf("*pointer_1 = %d,*pointer_2 = %d\n",*pointer_1,*pointer_2);
return 0;
}
运行结果:
a = 100,b = 10
*pointer_1 = 100,*pointer_2 = 10
* 表示所定义的变量是指针变量,不是普通变量
基类型 *指针变量名
如:
int *pointer_1;
左端的 int 是在定义指针变量时必须指定的 “ 基类型 ”
指针变量的基类型用来指定指针变量可以指向的变量的类型
*pointer_1 变量名是 pointer_1 ,而不是 *pointer_1
赋给指针变量的是变量地址而不能是任意类型的数据,而且只能是与指针变量的基类型相同类型的变量的地址
指针变量中只能存放地址,不要将一个整型值赋给一个指针变量
*p 是指针变量,p 指向的对象的值
p = &a;
// 指针变量 p 的值是变量 a 的地址
printf("%d",*p);
// 以十进制整数形式输出指针变量 p 所指向的变量的值,即变量 a 的值
*p = 1;
// 将整数 1 赋给 p 当前所指向的变量
// 如果 p 指向变量 a,相当于 a = 1
printf("%o",p);
// 以八进制数形式输出指针变量 p 的值
// 如果 p 指向了 a,就是输出了 a 的地址,即 &a
输入 a 和 b 两个整数,按先大后小的顺序输出 a 和 b
#include
int main(){
int *p1,*p2,*p,a,b;
scanf("%d,%d",&a,&b);
p1 = &a;
p2 = &b;
if(a < b){
p = p1;
p1 = p2;
p2 = p;
}
printf("a = %d,b = %d\n",a,b);
printf("max = %d,min = %d\n",*p1,*p2);
return 0;
}
运行结果:
5,9
a = 5,b = 9
max = 9,min = 5
指针代表的不是一个纯地址,而是一个带类型的地址
在 C 语言中,所有数据都是存放在内存单元中,因此所有数据都是有类型的
指针变量作为函数的参数时,其作用是将一个变量的地址传送到另一个函数中
输入 a 和 b 两个整数,按先大后小的顺序输出 a 和 b
#include
int 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("max = %d,min = %d",a,b);
return 0;
}
// 交换的是 a 和 b 的值,而不是 p1 和 p2 的值
void swap(){
int temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
}
如果要通过函数调用得到 n 个要改变的值,可以这样做:
- 在主调函数中设 n 个变量,用 n 个指针变量指向它们
- 设计一个函数,有 n 个指针形参。在这个函数中进行一系列操作改变 n 个形参的值
- 在主调函数中调用这个函数,在调用时将这 n 个指针变量作实参,将它们的地址传给该函数的形参
- 在执行该函数的过程中,通过形参指针变量,改变它们所指向的 n 个变量的值
- 主调函数中就可以使用这些改变了值的变量
不可能通执行函数改变实参指针变量的值,但是可以改变实参指针所指变量的值
函数的调用可以仅且可以得到一个返回值,而使用指针变量作参数,可以得到多个变化了的值
输入 3 个整数 a,b,c,要求按大小顺序将它们输出。用函数实现改变这 3 个变量的值
#include
int main(){
void exchange(int *q1,int *q2,int *q3);
int a,b,c,*p1,*p2,*p3;
printf("Pleases enter three numbers : ");
scanf("%d,%d,%d",&a,&b,&c);
p1 = &a;
p2 = &b;
p3 = &c;
exchange(p1,p2,p3);
printf("%d,%d,%d\n",a,b,c);
return 0;
}
void exchange(int *q1,int *q2,int *q3){
void swap(int *pt1,int *pt2);
if(*q1 < *q2){
swap(q1,q2);
}
if(*q1 < *q3){
swap(q1,q3);
}
if(*q2 < *q3){
swap(q2,q3);
}
}
void swap(int *pt1,int *pt2){
int temp;
temp = *pt1;
*pt1 = *pt2;
*pt2 = temp;
}
一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址
数组元素的指针就是数组元素的地址
int a[10]; // 定义 a 为包含 10 个整型数据的数组
int *p; // 定义 p 为指向整型变量的指针变量
p = a[0]; // 把 a[0] 元素的地址赋给指针变量 p,也就是 p 指向数组下标为 0 的元素
// 以下两个语句等价
// 数组名 a 不代表整个数组中的全部数据,p = a 是把 a 数组第一个元素的地址赋给指针变量 p,而不是所有元素
p = a[0];
p = a;
int *p;
p = &a[0]; // 注意不是 *p = &a[0]
// 等价于
int *p = a;
数组名代表数组首元素的地址,故可以通过数组名计算出数组中序号为 i 的元素的地址,其形式为 *(a + i)
用一个指针变量 p 指向数组首元素,然后用 *(p + i) 调用 a 数组中序号为 i 的元素
有一个数组存放 10 个学生的年龄,用不同的方法输出数组中的全部元素
#include
int main(){
int a[10] = {19,17,20,18,16,22,24,15,23,25};
int i,*p = a;
// 方式一:用数组名加下标
for(i = 0;i < 10;i++){
printf("%d",a[i]);
}
printf("\n");
// 方式二:通过数组名计算数组元素地址,找到元素
for(i = 0;i < 10;i++){
printf("%d",*(a + i));
}
printf("\n");
// 通过指针变量计算数组元素地址,找到元素
for(i = 0;i < 10;i++){
printf("%d",*(p + i));
}
printf("\n");
// 用指针变量先后指向各数组元素
for(p;p < (a + 10);p++){
printf("%d",*p);
}
printf("\n");
return 0;
}
指针运算
实参数组名代表该数组首元素的地址,而形参是用来接收从实参传递过来的数组首元素地址的
因此形参应该是一个指针变量(只有指针变量才能存放地址)
C 编译都是将形参数组名作为指针变量处理的
实参数组名代表一个固定的地址 ,或者说是指针常量,但形参数组并不是一个固定的地址值,而是作为指针变量,在函数调用开始后,它的值等于实参数组首元素的地址,在函数执行期间,它可以再被赋值,但它的值的改变不会传递回主调函数,不会改变实参的值
将数组 a 中 n 个整数按相反顺序存放
#include
int main(){
void inv(int x[],int n);
int i,a[10] = {3,7,9,11,0,6,7,5,4,2};
printf("The origninal array : \n");
for(i = 0; i < 10; i++){
printf("%d ",a[i]);
}
printf("\n");
inv(a,10);
printf("The array has been inverted : \n");
for(i = 0; i < 10; i++){
printf("%d ",a[i]);
}
printf("\n");
return 0;
}
void inv(int x[],int n){
int temp,i,j,m = (n - 1) / 2;
for(i = 0; i < = m; i++){
j = n - 1 -i;
temp = x[i];
x[i] = x[j];
x[j] = temp;
}
return;
}
如果有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下 4 种情况:
编写一个函数,用选择法对 10 个整数按由大到小顺序排序,用数组名作实参
#include
int main(){
void sort(int x[],int n);
int *p,i,a[10];
p = a;
for(i = 0;i < 10;i++){
scanf("%d",p++);
}
p = a;
sort(p,10);
for(p = a,i = 0;i < 10;i++){
printf("%d",*p);
p++;
}
printf("\n");
return 0;
}
void sort(int x[],int n){
int i,j,k,t;
for(i = 0;i < n - 1;i++){
k = i;
for(j = i + 1;j < n;j++){
if(x[j] > x[k]){
k = j;
}
}
if(k != i){
t = x[i];
x[i] = x[k];
x[k] = t;
}
}
}
在 C 语言中,可以用两种方法访问一个字符串
定义字符指针,指向一个字符串
#include
int main(){
char *string = "I love China!";
printf("%s\n",string);
return 0;
}
注意:
有一个字符数组 a,在其中存放字符串 “I am a boy.”,要求把该字符串复制到字符数组 b 中
#include
int main(){
char a[] = "I am a boy.",b[20];
int i;
for(i = 0;*(a + i) != "\0";i++){
*(b + i) = *(a + i);
}
*(b + i) = '\0';
printf("string a is : %s\n",a);
printf("string b is : ");
for(i = 0;b[i] != '\0';i++){
printf("%c",b[i]);
}
printf("\n");
return 0;
}
使用指针变量
#include
int main(){
char a[] = "I am a boy.",*p1,*p2;
int i;
p1 = a;
p2 = b;
for(;*p1 != '\0';P1++,P2++){
*p2 = *p1;
}
p2 = '0';
printf("string a is : %s\n");
printf("string b is : ");
for(i = 0;b[i] != '\0';i++){
printf("%c",b[i]);
}
printf("\n");
return 0;
}
有一个字符数组 a,要求把该字符串复制到字符数组 b 中,使用函数实现
#include
int main(){
void copy_string(char *from,char *to);
char *a = "I am a teacher.";
char b[] = "You are a student.";
char *p = b;
printf("string a = %s\nstring b = %s\n",a,p);
printf("\ncopy string a to string b : \n");
copy_string(a,p);
printf("string a = %s\nstring b = %s\n",a,p);
return 0;
}
void copy_string(char *from,char *to){
for(;*from != '\0';from++,to++){
*to = *from;
}
*to = '\0';
}
把字符串 a 拼接到字符串 b 后面
#include
int main(){
void link_string(char *arr1,char *arr2);
char a[40] = "I am a teacher.";
char b[] = "You are a student.";
char *p1 = a,*p2 = b;
printf("string a : %s\nstring b : %s\n",p1,p2);
link_string(p1,p2);
printf("Now,\nstring a : %s\nstring b : %s\n",a,b);
return 0;
}
void link_string(char *arr1,char *arr2){
int i;
for(i = 0;*arr1 != '\0';i++){
arr1++; // 指针变量指向 ‘\0’
}
for(;*arr2 != '\0';arr1++,arr2++){
*arr1 = *arr2;
}
arr1 = '\0';
}
字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第一个字符的地址),绝不是讲字符串放到字符指针变量中
赋值方式
char atr[14];
atr = "I Love China!";
char *a;
a = "I Love China!";
注意,赋给 a 的不是字符,而是字符串第一个元素的地址
赋初值
char *a = "I Love China!";
等价于
char *a;
a = "I Love China!";
而对数组的初始化
char str[14] = {"I Love China!"};
不能等价于
char atr[14];
str[] = "I Love China!";
即数组可以在定义时整体赋初值,但不能在赋值语句中整体赋值
如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的一段地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址,也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋予一个地址值,则它并未具体指向一个确定的字符数据
指针变量的值是可以改变的
若指针变量 p 指向数组 a,则可以用指针变量带下标的形式引用数组元素;若字符指针变量 p 指向字符串,就可以用指针变量带下标形式引用所指的字符串中的字符
字符数组中各元素的值是可以改变的,但字符指针变量指向的字符串常量中的内容是不可以被取代的
改变指针变量的值
#include
int main(){
// 正确用法
char *a = "I Love China!";
a = a + 7;
printf("%s\n",a);
// 错误示范
// 数组名虽然代表地址,但它是常量,值时不能改变的
char atr[] = {"I Love China!"};
str = atr + 7;
printf("%s",str);
return 0;
}
运行结果
China!
上文的 copy_string 函数改造
原代码
void copy_string(char *from,char *to){
for(;*from != '\0';from++,to++){
*to = *from;
}
*to = '\0';
}
改造一
void copy_string(char *from,char *to){
while((*to = *from) != '\0'){
to++;
from++;
}
}
改造二
void copy_string(char *from,char *to){
while((*to++ = *from++) != '\0');
}
改造三
void copy_string(char *from,char *to){
while(*from != '\0'){
*to++ = *from++;
}
*to = '\0';
}
改造四
void copy_string(char *from,char *to){
while(*from){
*to++ = *from++;
}
*to = '\0';
}
改造五
void copy_string(char *from,char *to){
while(*to++ = *from++);
// 等价于
// while((*to++ = *from++) != '\0');
}
改造六
void copy_string(char *from,char *to){
for(;(*to++ = *from++) != 0;);
// 或
for(;*to+= = *from++;);
}
改造七
void copy_string(char from[],char to[]){
char *p1,*p2;
p1 = from;
p2 = to;
while((*p2++ = *p1++) != '\0');
}
如果在程序中定义了一个函数,在编译时,编译系统给这个函数代码分配一段存储空间,这段存储空间的起始地址称为这个 函数的指针
可以定义一个指向函数的指针变量,用来存放某一函数的起始地址,此时此指针变量指向该函数
数据类型 (*指针变量名)(函数参数列表);
类型名 *函数名(参数列表);
注意:在 * 号两侧没有括号,有括号就成了指向函数的指针变量了
一个数组若其元素均为指针类型数据,称为 指针数组 ,也就是说,指针数组中的每一个元素都存放一个地址,相当于一个指针变量
类型名 数组名[数组长度]
指向另一个指针数据的指针变量,称为 指向指针的指针
char **p;
指针变量可以有 “空值”,即该指针变量不指向任何变量
p = NULL;
其中 NULL 是一个符号常量,代表整数 0
在 stdio.h 头文件中对 NULL 进行了定义
#define NULL 0
它使 p 指向地址为 0 的单元
系统保证使该单元不作他用(不存放有效数据),即有效数据的指针不指向 0 单元
注意:
p 的值为 NULL 与未对 p 赋值时两种不同的概念。前者是有值的(值为 0),不指向任何程序变量,后者虽未对 p 赋值但也不等于 p 无值,只是它的值是一个无法预料的值,也就是 p 可能指向一个事先未指定的单元
指针变量的定义形式
一 叶 知 秋,奥 妙 玄 心