longtian1911 发表于 2023-11-8 16:06

C语言程序设计指针详解

指针是什么
[*]如果在程序中定义了一个变量,在对程序进行编译时,系统就会给该变量分配内存单元
[*]编译系统根据程序中定义的变量类型,分配一定长度的空间
[*]例如:vc2010为整型变量分配4个字节,对单精度浮点型变量分配4个字节,对字符型变量分配1个字节
[*]内存区的每一个字节有一个编号,这就是“地址”,它相当于旅馆中的房间号
[*]在地址所标识的内存单元中存放数据,这相当于旅馆房间中居住的旅客一样
[*]由于通过地址能找到所需的变量单元,我们可以说,地址指向该变量单元
[*]将地址形象化地成为“指针”
[*]务必弄清楚存储单元的地址和存储单元的内容这两个概念的区别
直接存取和间接存取
[*]通过变量名的访问成为直接存取
[*]通过指针变量的访问成为间接存取
指向就是通过地址来体现的
[*]假设 i_pointer中的值是变量i的地址(2000),这样就在 i_pointer和变量i之间建立起一种联系,即通过i_pointer能知道i的地址,从而找到变量i的内存单元
[*]称i_pointer指向i
[*]变量i_pointer称为指针变量,它保留了变量 i 的地址
[*]由于通过地址能找到所需的变量单元,因此可以说“地址指向该变量单元”,但这样的说法不够形象和生动
[*]将地址形象化地称为“指针”。意思是通过他能找到以他为地址的内存单元
[*]可以通过指针指向某个存储单元,从而可以通过指针间接地访问这个存储单元
[*]一个变量的地址称为该变量的指针,例如:地址2000 是变量 i的指针
[*]如果有一个变量专门用来存放另一个变量的地址(即指针),则它称为 指针变量
[*]i_pointer 就是一个指针变量,指针变量就是地址变量,即专门用来存放其他变量的地址的变量,指针变量的值是地址(即指针)
[*]存放地址的变量是指针变量,它用来指向另一个对象,如变量、数组、函数和指针等
[*]指针本身就是一种类型,另外指针的类型还取决于它所指向的变量的类型(基类型)包括:普通变量指针、数组指针、函数指针、指向指针的指针
int * pointer_1   //此处 * 与类型名在一起共同定义指针变量,类型名 int 用于定义指针变量的基类型

*pointer //此处 * 与指针变量一起使用,此时代表指针变量所指向的变量,标识 “取内容”定义指针变量
[*]定义指针变量的一般形式位:类型 * 指针变量名;如:int *poniter_1
[*]int 是为指针变量的 “基类型”
[*]基类型定义了指针变量可以指向的变量类型
[*]如 pointer_1 可以指向整型变量,但不能指向浮点型变量
[*]指针变量前面的 “ * ” 表示该变量的类型为指针型变量,指针变量名为pointer_1,而不是 *pointer_1

指针变量定义注意事项
[*]在定义指针变量时必须指定基类型
[*]不同类型的数据在内存中所占得字节数和存放方式是不同的
[*]通过指针引用一个变量时,必须知道该数据的类型,才能按存储单元的长度以及数据的存储形式正确地取出该数据
[*]指针的移动和指针的加、减运算。例如:使指针移动一个位置或者使指针值加1,这个1代表与基类型对于的字节长度
[*]整型指针变量只能指向其定义时由“基类型”int支出的形同类型的变量,不能忽而指向int整型变量,忽而指向float实型变量

在说明指针变量时不能只说“a是一个指针变量”,而应该完整的说:
a是指向整型数据的指针变量int *a
b是指向单精度型数据的指针变量 float *b
c是指向字符型数据的指针变量char *c
int *,float * ,char * 是三种不同类型的指针变量,不能混淆
指针变量中只能存放其他对象的地址(指针),不能将一个整数直接赋给一个指针变量,否则判定位非法。

下面都是合法的指针变量定义和初始化:
float *pointer_3;
char *pointer_4;
int a,b;
int *pointer_1=&a, *pointer_2=&b;怎样引用指针变量
[*]在引用指针变量时,可能有三种情况:
[*]给指针变量赋值。如: p = &a;   // 使p 指向a
[*]引用指针变量指向的变量,如有 p = &a; *p=1; // *p相当于a
[*]直接引用指针变量的值。如: printf("%o",p);

掌握两个与指针相关的运算符:1、& 取地址运算符。    &a 是变量a的地址2、* 指针运算符(间接访问运算符)如果:p指向变量 a , 则 *p 就代表 ak = *p;(把a的值赋值给k)*p = 1; (把1赋值给a)
例子: 输入 a 和 b两个整数,按先大后小的顺序输出a和bhttp://pic.aiweiidc.com/java/202210312229502.png
[*]C语言实参变量和形参变量之间的数据传递是单向的“值传递”方式
[*]用指针变量作为函数参数时同样要遵循这一规则,此时传递的是地址值

//例题:输入3个整数,a,b,c,要求按由大到小的顺序将他们输出,用函数实现。
#include<stdio.h>
int main(){
    void exchange(int *q1, int *q2, int *q3);
    int a,b.c,*p1,*p2,*p3;
    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;
}易错点
#include<stdio.h>
#include<conio.h>
int main(){
    float *a;
    float   b = 3;
    a=&b;
    printf("%f,%f\n",*a++,*a);
    *a= 5.0;
    printf("%f\n",*a);
    printf("%f\n",b);
    getch();
    return 0;
}数组元素的指针
[*]一个变量有地址,一个数组包含若干元素,每个数组元素都有相应的地址
[*]指针变量可以指向数组元素(把某一数组元素的地址放到一个指针变量中)
[*]所谓数组元素的指针就是数组元素的地址
[*]引用数组元素可以用下标法(如a),也可以用指针法,即通过指向数组元素的指针找到所需的元素。
[*]使用指针法能使目标程序质量高(占内存少,运行速度快)
[*]C 语言中,数组名(不包括形参数组名,形参数组并不占据实际的内存单元)代表数组中首元素(即序号为0的元素)的地址。它是一个指针型常量可以用一个指针变量指向一个数组元素int a = {1,2,3,4,5,6};int *p;p = &a//等价于 p = a ;
在引用数组元素时指针的运算在指针指向数组元素时,允许以下运算:
[*]加一个整数(用+或+=),如 p+1
[*]减一个整数(用-或-=),如 p-1
[*]自加运算,如 p++, ++p ,但是如果a 是数组名,则 a++a-- 不合法
[*]自减运算,如 p--, --p
[*]两个指针相减,如 p1 - p2 (只有p1 和 p2都指向同一个数组时才有意义)1、如果指针变量p已指向数组中的一个元素,则p+1指向同一个数组中的下一个元素,p-1指向同一个数组中的上一个元素例子: floata,*p = a;假设a的地址为2000 则​        p的值为 2000​    p+1的值为2004​    p-1的值为1996,但这个位置不能用(越界)2、如果p的初值为&a,则p+i和a+i就是数组元素a的地址,或者说,他们指向a数组序号为i的元素3、* (p+i) 或* (a+i) 是p+i 或 a+i所指向的数组元素,即 a4、如果指针 p1 和 p2 都指向同一个数组 则 p2 - p1得到的是他们之间相差多少个元素
引用一个数组元素,可以用下面两种方法:
[*]下标法,如a形式
[*]指针法,如 * (a+i) 或者 *(p+i),其中a是数组名,p是指向数组a中数组元素的指针变量,其初值 p=ahttp://pic.aiweiidc.com/java/202211042214432.png
http://pic.aiweiidc.com/java/202211042215742.png
例题:通过指针变量输出整型数组a的10个元素
解题思路:用指针变量p指向数组元素,通过改变指针变量的值,使p先后指向a到a各元素。
#include<stdio.h>
int main(){
    int *p,i,a;
    p=a;
    for(i=0;i<10;i++) scanf("%d",p++);
    p=a;//一定不能掉了,这种错误在编译时不会报错,一定要注意
    for(i=0;i<10;i++,p++)
    printf("%d",*p);
}注意事项:
[*]指向数组的指针变量也可以带下标,如p在编译时,对下标的处理方法时转换为地址,对p处理成*(p+i)
[*]如果p是指向一个整型数组元素a,则p代表a。但如果当前p指向a,则 p并不代表a,而是a,即a
利用指针引用数组元素比较方便灵活,有不少技巧,
[*]设p指向数组a的首元素即p=a,则:
p++;*pp++使p指向下一元素a,然后若再执行*p,则得到下一个元素a的值*p++;由于++ 和 *同优先级,结合方向为自右向左,它等价于 *(p++)。先引用p的值,实现 *p的运算,然后再使p自增,等同 *p; p++;http://pic.aiweiidc.com/java/202211042232030.png*(p++) 与 *(++p)作用是否相同?
[*]不相同,前者是先取*p的值,然后使p加1
[*]后者是先使p加1指向下一个数组元素,然后再取*p的内容

[*]如果p当前指向 a 数组中的第i个元素a 则:*(p--) 相当于 a ,先对p进行 * 运算(求p所指向元素的值)。再使p 自减*(++p)相当于 a[++i],先使p自加,再进行 * 运算*(--p)相当于 a[--i],先使p自减,再进行 * 运算

//例题:想输出a数组的100个元素,可以用下面的方法:http://pic.aiweiidc.com/java/202211042240205.png用数组名做函数参数
[*]用数组名作函数参数时,因为实参数组名代表该数组首元素的地址,形参应该是一个相应类型的指针变量
[*]C编译都是将形参数组名作为指针变量来处理,因此可以省略数组第一维的大小
[*]实参数组名是指针常量,而形参数组名是按指针变量处理
[*]在函数调用进行虚实结合后,形参数组名(指针变量)的值就是实参数组首元素的地址
[*]在函数执行期间,形参数组名(指针变量)可以再被赋值(因为它本质上是一个变量)
变量名作函数参数和数组名做函数参数的比较
实参类型变量名数组名
要求形参的类型变量名数组名或指针变量
传递的信息变量的值实参数组首元素的地址
通过函数调用能否改变实参的值不能改变实参变量的值能改变实参数组的值

//将数组a中n个整数按相反顺序存放
//阶梯思路:将a 与 a 对换
#include<stdio.h>
#include<conio.h>
int main(){
    void inv(int *x,int n);
    int i, a = {3,7,9,11,0,6,7,5,4,2};
    for (i = 0; i < 10; i++) printf("%d",a);
    printf("\n");
    inv(a,10);
    for ( i = 0; i < 10; i++) printf("%d",a);
    getch();
    return 0;
}
void inv(int *x,int n){
    int *p,temp,*i,*j,m=(n-1) / 2;
    i = x;
    j=x + n - 1;
    p =x + m;
    for (; i <= p; i++,j--)
    {
      temp = *i;
      *i = *j;
      *j = temp;
    }
   
}如果有一个实参数组,要想在函数中改变此数组中元素的值,函数实参与形参的对于关系
实参形参实参形参
数组名数组名指针变量指针变量
数组名指针变量指针变量数组名
http://pic.aiweiidc.com/java/202211041240272.pnghttp://pic.aiweiidc.com/java/202211041241343.pnghttp://pic.aiweiidc.com/java/202211041241827.pnghttp://pic.aiweiidc.com/java/202211041242442.png
//用指针方法对10个整数按由大到小顺序排列
/*
    解题思路:
      在主函数中定义数组a存放10个整数,定义 int * 型指针变量p指向 a
      定义函数 sort 使数组a中的元素按由大到小的顺序排列
      在主函数中调用 sort 函数 用指针 p 做实参
      用选择法进行排序
      什么是选择法排序?
          选择法排序:假设有N个数要按照从大到小的顺序排序,选择法就是先设第一个数是最大的(进行第一次大循环),然后将这个数与数组中剩下的数依次比较,如果剩下的数中有比这个数大的,那就两者交换,直至第一个数是最大的为止;然后再设第二个数为第二大的(进行第二次大循环),将第二个数与数组中除第1、2数外的其余数进行比较,如果有大值,则进行两两交换,直至第二个数是剩下数中最大的为止。

*/

#include<stdio.h>
#include<conio.h>
int main(){
    void sort(int x[], int n);
    int i, *p,a;
    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");
    getch();
    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 > x)// if(*(x+j) > *(x + k))
            {
                k = j;
            }
      }
      if(k != i) {
            t = x;// t = *(x+i)
            x = x;// *(x+i) = *(x+k)
            x = t;// *(x+k) = t
      }   
    }
}通过指针引用多维数组
[*]多维数组元素的地址http://pic.aiweiidc.com/java/202211082033810.pnghttp://pic.aiweiidc.com/java/202211082055683.png
a 和 *(a+i)等价,a+j和*(a+i)+ j等价*(行指针) = 列指针从二维数组角度看,a代表二维数组首元素的地址,现在的首元素不是一个简单的整型元素,而是由4个整型元素所组成的一维数组,因此a代表的是二维数组首行(即第0行)的首地址,a+1代表第一行的首地址,a+2代表第二行的首地址。a,a,a,既然是一位数组名,而c语言又规定了数组名代表数组首元素地址,因此a代表一维数组a中第0列元素的地址,即&a0。也就是说 a的值是&a1,a的值是&a2http://pic.aiweiidc.com/java/202211082103068.png
[*]a 代表第0行首地址
[*]a + 1 代表第 1 行首地址
[*]a + 2 代表第 2 行首地址(行指针每 加 1 ,走一行)
[*]a + i 代表行号为 i的行首地址(按行变化)
提问 * (a+i)代表什么?答:相当于 a
[*]a 代表 a0的地址
[*]a + 1 代表a0的地址
[*]a + 2 代表 a0的地址 (列指针每加1 ,走一列)
提问 a + j代表什么?答:代表ai的地址提问 *(a + j)代表什么?答:代表元素ai提问 *( *(a+i) + j)代表什么?答:代表元素aia和 *(a+1)等价a+1 和 *(a+1)、a都是地址,且其值相等,都等于&a1,区别在于
[*]a + 1是行指针
[*]*(a+1)、a 都是列指针
http://pic.aiweiidc.com/java/202211082124748.png在指向行的指针前面加一个 * ,就转换为指向列的指针。例如,a 和 a + 1是指向行的指针,在它们前面加一个 * 后, * a 和 *(a+1)就成为指向列的指针,分别指向a数组第0行0列的元素和 第 1行0列的元素。反之,在指向列的指针前面加 & 就成为指向行的指针。例如 a的指向第0行0列元素的列指针,在它前面加 一个 & ,得 &a,由于a与*(a+0)等价,因此&a与 & * a等价,也就是与 a 等价,它指向二维数组的第 0 行,是一个行指针。不要把&a简单地理解为元素a的物理地址,因为并不存在a这样一个实际的数据存储单元。它只是一种地址的计算方法,能得到第 i 行的首地址(行指针方式)。&a和a的值相同,但他们的含义不同&a或 a+ i指向行(行指针)   a或* (a+i)指向列(列指针)   它们所指向的对象不同,即指针的基类型是不同的。*(a+i)只是a的另一种表示形式,不要简单地认为 *(a+i) 是 a+i 所指单元中的内容(实际上 a代表一个一维数组,它不是一个实际的存储单元)。在一维数组中a+i所指的是一个数组元素的存储单元,在该单元中有具体值,上述说法是正确的,而对二维数组,a + i 不是指向具体存储单元而是指向数组a的第 i 行在二维数组中,a + i 、&a、a、*(a+i)、&ai的值相等,即他们都代表同一地址,其中:a + i 、&a 是行指针a、*(a+i)、&ai是列指针例题:显示二维数组的有关数据(地址和值)http://pic.aiweiidc.com/java/202211091710971.pnghttp://pic.aiweiidc.com/java/202211091710047.png
//例题1:有一个3 x 4的二维数组,要求用指向数组元素的指针变量输出二维数组各元素的值
/*
    解题思路
    列指针完成
    1.二维数组的元素时整型的,它相当于整型变量,可以用int * 型指针变量指向它
    2.二维数组的元素在内存中是按行顺序存放的,即存放完序号为0的行中的全部元素后,接着存放序号为1的行中的元素,依次类推
    3。因此可用一个指向整型元素的指针变量(列指针),依次指向二维数组中的各个数组元素
*/
#include <stdio.h>
#include <conio.h>
int main(){
    int a = {1,3,5,7,9,11,13,15,17,19,21,23};
    int *p;//p + 1
    for (p=*a; p < a + 12; p++)
    {
      if((p - a) % 4 == 0) printf("\n");
      printf("%4d", *p);
    }
    printf("\n");
    getch();
    return 0;
}

//行指针完成
#include <stdio.h>
#include <conio.h>
int main(){
    int a = {1,3,5,7,9,11,13,15,17,19,21,23};
    int (*p);//p + 1
    int j;
    for (p=a; p < a + 3; p++){
      for(j=0;j < 4;j++)
      {
            printf("%4d", *(*p+j));
      }
      printf("\n");
    }
    getch();
    return 0;   
}

djkhmj 发表于 2023-11-8 19:05

看到这帖子真是高兴!

crystalfan 发表于 2023-11-8 19:16

看到这帖子真是高兴!

子非鱼 发表于 2023-11-8 19:22

强烈支持楼主ing……

Kider 发表于 2023-11-8 19:31

强烈支持楼主ing……

javajeep 发表于 2023-11-8 22:37

强烈支持楼主ing……

notorious 发表于 2023-11-9 00:09

Don't stay mad for too long. Learn to leave things behind.

匆匆那年1111111 发表于 2023-11-9 00:10

强烈支持楼主ing……

Thomas_Chen 发表于 2023-11-9 00:24

看到这帖子真是高兴!

dwanxp 发表于 2023-11-9 06:58

激动人心的时刻到了吗
页: [1] 2 3
查看完整版本: C语言程序设计指针详解