发表时间:2022-03-25来源:网络
C语言具有获取变量地址和操纵地址的能力,而用来操作地址的这种特殊数据类型就是指针。
简单来说,指针就是一种数据类型,用来表示内存地址,因此使用指针变量就可以对内存进行灵活的操作。
表示内存地址的数据类型就是指针类型。所以,地址就是指针,一个变量的地址就是一个指针型常量,用来保存地址的变量就是一个指针型变量。
假设变量名为a,它的内容为241,它存储在了地址为0016的地方。
它的指针名字为b,由于地址就是指针,那么该指针的值为a的地址,即0016,指针的地址为1010。
所以这里b叫作指针变量。
指针变量的属性:
1、指针变量的地址,即为指针变量分配的内存空间地址。(1010)
2、指针变量的值,指针内存空间上的内容。(0016)
3、以指针变量的值为地址的空间地址,也称指针指向的空间地址。(0016)
4、指针指向的内存空间的内容。(241)
虽然指针变量的值和其指向空间地址的值相同,但指针变量的值为一个内存空间的内容,是可以改变的。而指针变量指向的空间的地址是一个内存空间的地址,是一个常量,不可以被改变。
指针类型由两部分组成:数据类型名和指针操作符。 其中数据类型名声明了指针变量指向的内存空间存储的数据类型。
数据类型名 * 指针变量名
范例1
#include int main(void){ printf("sizeof(int *) = %d\n",sizeof(int *)); printf("sizeof(char *) = %d\n",sizeof(char *)); printf("sizeof(float *) = %d\n",sizeof(float *)); return 0; }
指针类型所占空间与其指向的内存存储的数据类型无关。
为指针变量赋值就是为指针设定它指向的内存空间的过程。给指针变量赋值必须赋给它一个地址。
char c = 'a'; char * cp; cp = &c;//指针指向c的地址,用&取c的地址 printf("%c",c); printf("%c",*cp);//这句打印与上面一句是等价的,都是将c打印出来范例2
指针可以访问和修改变量

范例3
指针变量的值在一次赋值后,可以再次被赋值为其他地址。
在输出符时,对于指针的值、地址符的输出用%p

对指针变量赋值的过程,就是使指针变量指向一个新的内存空间的过程。
原则上不能将整数数值赋值给指针变量,否则指针变量会指向以该整数数值为地址值的内存空间。
但是如果该整数值空间是有效的,那么可以使用。
范例4
使用scanf函数将整数赋值给指针变量

使用指针时最好为其进行初始化,否则指针将指向一个不可知的空间。
int a = 0; int *p1 = &a; int *p2 = NULL;NULL的值为0,它是指针型数值中约定的0值的表示形式。
将变量p声明为指向存储const int型数据的内存空间的指针变量,该类指针指向的内存空间的内容是不可变的。
const int a = 1; const int *p1 = &a; *p1 = 2; //*p1已经为1,不能为2 const型指针变量int *const p;const型指针变量指向的内存空间是固定的,初始化后不能将其指向其他空间。
int a = 1; int b = 2; int *const p = &a; *p = 12;//正确 p = &b;//指针变量的值不可改变,即不能改变指向的内存空间地址 指向const的const指针变量const int *const p;该指针变量的值和该指针指向的空间的值都是不可改变的。
int a = 1; int b = 2; int const *const p = &a; *p = 12;//错误 p = &b;//错误指针变量也可以作为函数参数使用。
函数调用时,将实参的值赋给形参。使用指针变量作为函数参数,可以将一个内存空间的地址传递到函数中,可以通过该地址来操作该地址上的内存空间。
void func(int *pt,int a); int *p; int x,y; x = 5; y = 20; p = &x; func(p,y);这里实参y传给形参a,则将a赋值为20;而p为x的地址,将p赋给指针*pt,则pt所指向的内容为5。
指针可以做到在函数内改变函数外的值,通过地址来传递值,这种方式称为地址传递。
范例5
使用指针作为形参的函数实现两个数的交换

通过上面的指针使用,我大致总结出了如下规律:
定义指针时一般为ptr;在使用时将ptr=&变量,相当于将变量的地址赋给指针变量ptr;而想要取地址的内容的时候使用ptr即可将该变量的值取出来。
指针形参的使用方法:
定义一个含有指针变量形参的函数;在主调函数中为该变量分配空间,并将一个指针变量指向该空间;以这个指针变量为实参调用定义好的函数;在函数内改变该指针指向的值;函数返回后,主调函数中的变量已经被改变。函数的函数返回值也可以是指针型的数据,即地址。返回该类型值时,执行机制与返回其他类型完全相同。声明方式一般为:
数据类型 * 函数名(形参列表)
范例6
使用指针变量作为函数返回值

可以看出函数的地址即函数的值。
void型指针就是无类型指针。void型指针可以指向存储任意数据类型端的空间。 定义形式如下:
void * 变量名;
不能对void型指针做加减运算,因为对指针端的加减是基于指针指向的类型空间的字节长度进行的。void型指针变量的地址空间类型是未知的,因此不能进行加减。
**void型指针通常用作函数的形参。**声明如下:
void * 函数名(形参类型);
练习1
//使用指针从标准输入获取三个整数,并求其中最大值 #include int main(void){ int a,b,c; int *p1 = &a; int *p2 = &b; int *p3 = &c; scanf("%d %d %d",p1,p2,p3); if(*p1 p1 = p3; } printf("Max is %d\n",*p1); return 0; }
练习2
指针也可以用来指向数组或指向数组中的一个元素。当指针指向数组时,可以通过指针来访问数组中的元素。
指向数组元素的指针是指一个指针变量,其指向的空间属于一个数组中的某一个元素。例如:
int array[10] = {0}; int *p0 = &array[0]; /*等效于*/ int *p0 = array;这是因为数组的第0个元素的地址就是数组的地址。
在数组中,相邻元素的地址只差一个元素的字长。因此,可以用一个数组元素型指针加上要访问的数组元素的地址偏移量即可获得该元素的地址。
范例8
#include #define SIZE 10 int main(void){ char a_ch[SIZE] = "Student"; int a_int[SIZE] = {1,2,3,4,5,6,7,8,9,10}; char *p_ch = a_ch; int *p_int = a_int; //使用索引法输出两个元素的地址 printf("&a_ch[4] = %p, &a_ch[3] = %p\n",&a_ch[4],&a_ch[3]); //使用索引法输出两个元素之间的地址偏移量 printf("&a_ch[4] - &a_ch[3] = %d\n\n",&a_ch[4]-&a_ch[3]); //使用索引法输出两个元素的地址 printf("&a_int[4] = %p, &a_int[3] = %p\n",&a_int[4],&a_int[3]); //使用索引法输出两个元素之间的地址偏移量 printf("&a_int[4] - &a_int[3] = %d\n\n",&a_int[4]-&a_int[3]); //使用指针法输出两个元素的地址 printf("p_ch + 4 = %p,p_ch + 3 = %p\n",p_ch+4,p_ch+3); //使用指针法输出两个元素之间的地址偏移量 printf("p_int + 4 = %p,p_int + 3 = %p\n",p_int+4,p_int+3); return 0; }
在计算两个地址的差的时候,为实际差/存储类型占字节大小
可以通过数组名和地址偏移量的组合来访问数组元素。
int array[5] = {1,2,3,4,5}; printf("%d\n",*(array+2));数组元素型指针与数组变量在很多方面是不同的:
值的可改变性不同范例9
#include #define SIZE 5 int main(void){ int a_int[SIZE] = {1,2}; int *p_int = a_int; //输出数组元素型指针和数组变量的占用空间 printf("sizeof(a_int) = %d\n",sizeof(a_int));//数组变量 printf("sizeof(p_int) = %d\n\n",sizeof(p_int));//指针变量 //输出数组第0个元素的值及其地址 printf("a_int[0] = [%d]\n",a_int[0]);//第0个元素 printf("&a_int[0] = [%p]\n\n",&a_int[0]);//第0个元素的地址 //输出数组变量的三个属性 printf("*a_int = [%d]\n",*a_int);//a_int指向的内容 printf("a_int = [%p]\n",a_int);//a_int的值 printf("&a_int = [%p]\n\n",&a_int);//a_int的地址 //输出数组元素型指针的三个属性 printf("*p_int = [%d]\n",*p_int);//p_int指向的内容 printf("p_int = [%p]\n",p_int);//p_int的值 printf("&p_int = [%p]\n",&p_int);//p_int的地址 return 0; }
从上面例子可以得到:数组变量的值=第2个元素的地址=数组变量的地址;而数组元素型指针的值=第1个元素的地址,但它的地址是另外一个地址。
当数组作为函数形参时,数组的地址可以用作函数调用的实参。声明形式有以下几种:
//形式1 void print_array(int array[SIZE]); //形式2 void print_array(int array[]); //形式3 void print_array(int * array);范例10
//验证上述3种方法都是指针变量 #include #define SIZE 6 void func_1(const char a[SIZE]){ printf("char a[SIZE]: %d\n",sizeof(a)); } void func_2(const char a[10000]){ printf("char a[10000]: %d\n",sizeof(a)); } void func_3(const char *a){ printf("char *a: %d\n",sizeof(a)); } void func_4(const char a[]){ printf("char a[]: %d\n",sizeof(a)); } int main(void){ char a_char[SIZE] = {'a','b','c','d','e','f'}; char * p_char =a_char; //定义字符指针 printf("sizeof(a):\n"); func_1(a_char); func_2(p_char); func_3(a_char); func_4(p_char); return 0; }
一般情况下,为了获得数组容量,通常将形式2和形式3改写为如下:
这种方式还可以在函数开始时对数组容量进行判 断,提高程序安全。
调用含数组形参的函数时,数组变量和数组元素型指针变量都可以作为实参。但不论是数组变量还是数组元素型指针变量,他们的值都是数组的首地址。
范例11
使用数组作为形参,实现了可输出升序数组中大于或等于0的数组元素的函数

使用指针法访问二维数组有三种方式:使用指向数组元素的指针、使用一维数组型指针和使用二维数组型指针
1. 使用指向数组元素的指针
2. 使用一维数组型指针
对于一维数组型指针,应该将其赋值为数组第0个一维数组的地址。
3. 使用二维数组型指针
int a[3][2] = {{1,2},{11,12},{21,22}}; int (*p)[3][2] = &a; 那么数组a中第m行第n列元素可以表示为: *(*(*p+m)+n) //表示第m个1维数组地址下的第n个偏量 或 *(**p+2*m+n) //第m行n列元素相对第一个元素的偏移量为(2 * m + n)二维数组的地址可以用做函数的形参。在传递二维数组地址时,可以使用三种不同的指针作为形参来获得二维数组。
1. 数组元素型指针
将数组第一个元素的地址作为实参赋值给形参,在函数中使用该首地址和元素偏移量的组合来获得数组元素的地址,通过地址访问元素。
范例11
#include #define ROW_SIZE 3 #define COL_SIZE 3 void print_array(int *p,const int row,const int col){ int i,j; for(i=0;i int i,j; for(j=0;j int array[ROW_SIZE][COL_SIZE] = { {1,2,3}, {11,12,13}, {21,22,23} }; printf("Output the matrix:\n"); print_array(*array,ROW_SIZE,COL_SIZE);//以*array为第一个实参调用函数 printf("Reverse the matrix:\n"); reverse(&array[0][0],ROW_SIZE,COL_SIZE);//以&array[0][0]为第一个实参调用函数 return 0; }
2. 使用一维数组型指针
可以使用一维数组型指针作为形参来传递二维数组。
范例12
#include #define ROW_SIZE 3 #define COL_SIZE 3 void print_array(int (*p)[COL_SIZE],const int row){ /*数组型指针作为变量*/ int i,j; for(i=0;i int i,j; for(j=0;j int array[ROW_SIZE][COL_SIZE] = { {1,2,3}, {11,12,13}, {21,22,23} }; printf("Output the matrix:\n"); print_array(array,ROW_SIZE);//以array为实参调用函数 printf("Reverse the matrix:\n"); reverse(&array[0],ROW_SIZE);//以&array[0]为实参调用函数 return 0; }
3. 使用二维数组型指针
范例13
#include #define ROW_SIZE 3 #define COL_SIZE 3 void print_array(int (*p)[ROW_SIZE][COL_SIZE]){ int i,j; for(i=0;i int array[ROW_SIZE][COL_SIZE] = { {1,2,3}, {11,12,13}, {21,22,23} }; printf("Output the matrix:\n"); print_array(&array); return 0; }
字符指针是指向字符型内存空间的指针变量,一般定义语句如下:
char *p = NULL; 访问字符数组:将字符指针赋值为字符数组的首地址即可实现对字符数组的访问。char str[] = "Hello,world!"; char *p = str; printf("%c",*(p+i)); //访问str中的某个元素 printf("%s",p); //打印整个字符数组 同时也可以使用p改变字符数组中的内容,例如: for(i=0;i //初始化2个字符数组 char str1[SIZE] = "\0"; char str2[SIZE] = "\0"; //定义两个字符指针指向这两个数组 char *p1 = str1; char *p2 = str2; printf("Please input the first string: "); gets(p1); printf("Please input the second string: "); gets(p2); while(*p1 == *p2 && '\0' != *p1){ ++p1; //将p1后移 ++p2; //将p2后移 } //根据p1和p2指向的值来判断比较结果 if(*p1 == *p2) printf("They are same.\n"); else if(*p1 > *p2) printf("The first one is larger than the second one.\n"); else printf("The first one is smaller than the second one.\n"); return 0; }
范例16
使用字符指针将一个字符串的内容复制到另一个字符串中。

*号和++属于同一优先级,且方向都是从右向左的,*p++和 *(p++)作用相同。
在这里的话*p2++和 *p1++的作用是先p1++进行移位,然后在用 *取其中的内容,实现一位一位地复制。
在使用字符指针数组时,如果不指定字符长度,会使每一个字符串的空间都至少扩大为最长字符串所占空间,这样会对资源产生浪费。因此可以使用字符指针数组,在数组中保存字符指针,每个指针指向特定的字符常量。
范例17
使用啊字符指针数组对字符串进行排序

练习2
设计一个取子字符串的函数,函数声明包括以下信息:
源字符串、子字符串开始位置、子字符串字符个数
上一篇:C++和JAVA得区别?
下一篇:ZZUIL题解1111
皓盘云建最新版下载v9.0 安卓版
53.38MB |商务办公
ris云客移动销售系统最新版下载v1.1.25 安卓手机版
42.71M |商务办公
粤语翻译帮app下载v1.1.1 安卓版
60.01MB |生活服务
人生笔记app官方版下载v1.19.4 安卓版
125.88MB |系统工具
萝卜笔记app下载v1.1.6 安卓版
46.29MB |生活服务
贯联商户端app下载v6.1.8 安卓版
12.54MB |商务办公
jotmo笔记app下载v2.30.0 安卓版
50.06MB |系统工具
鑫钜出行共享汽车app下载v1.5.2
44.7M |生活服务