第四章 指针

本文详细介绍了C语言中的指针概念,包括指针的声明、使用及算术运算。通过实例展示了如何通过指针修改变量值、传递地址、比较指针以及创建指针数组。此外,还探讨了二级指针、返回指针的函数以及函数指针的使用。文中还提到了指针作为回调函数和函数指针在回调函数中的应用。最后,强调了指针使用时的注意事项,如初始化空指针和避免指针悬空。
摘要由CSDN通过智能技术生成

一、概念

简单的说指针就是一个地址

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */

//指针的入门
#include <stdio.h>
​
void main(){
​
    int num = 1;
    //定义一个指针变量,指针
    //说明:
    //1.int*表示类型为 指针类型(执行一个int类型指针)
    //2.名称 ptr, ptr就是一个int*类型的变量地址
    //ptr指向了一个int类型的变量的地址
    int*ptr = &num;
​
​
    //num的地址是多少
    //如果要输出一个变量的地址,使用格式是%p
    //&num表示取出这个num这个变量的对应地址
    printf("num 的值=%d\nnum 的地址=%p",num,&num);
​
    //指针变量本身也有地址:
    printf("\nptr 的地址是%p",&ptr);
    printf("\nptr 存放的值是一个地址%p",ptr);
    printf("\nptr 指向的值=%d",*ptr);
​
    getchar();
}

练习:

#include <stdio.h>
​
void main(){
    //1) 写一个程序,获取一个int变量num的地址,并显示到终端
    int num = 100;
    
    //2) 将num的地址赋给指针 ptr , 并通过ptr去修改num的值.
    //指针的类型和指向的变量类型对应(相等)
    int * ptr = &num;
    printf("num的值=%d\nnum的地址=%p",num,&num);//num =100
    *ptr = 99;//修改num的值
    printf("\nnum的新值=%d",num);//num = 99
    
    //3) 并画出案例的内存布局图
​
    getchar();
}

void main() {
int a = 300;
int *ptr = a;
}
题1
错误: 把 int 赋给 int *
    
void main() {
int a = 300;
float *ptr = &a;
}
题2
错误:把 int 的地址赋给
float *

void main() {
int a = 300; // a = 300
int b = 400; // b = 400
int * ptr = &a; //ok ptr 指向 a
*ptr = 100; // a = 100
ptr = &b; // ok ptr 指向 b
*ptr = 200; // b = 200
printf("\n a=%d,b=%d,*ptr=%d", a, b, *ptr);
getchar();
}
//输出什么内容
a=100; b=200;&ptr=200;

指针细节说明

  1. 基本类型,都有对应的指针类型, 形式为 数据类型 *,比如 int的对应的指针就是 int *, float 对应的指针类型就是 float * , 依次类推。

  2. 此外还有指向数组的指针、指向结构体的指针,指向共用体的指针

二、值传递和地址传递

C语言传递参数(或者赋值)可以是值传递(pass by value),也可以传递指针(a pointer passed by value), 传递指针也叫地址传递。

  1. 默认传递值的类型:基本数据类型 (整型类型、小数类型,字符类型), 结构体,共用体。

  2. 默认传递地址的类似:指针、数组

练习

#include <stdio.h>
​
void main(){    
//试编写程序实现如下效果
//姓名 年龄 成绩 性别 爱好
//xx   xx    xx   xx  xx
//要求:
//a、用变量将姓名、年龄、成绩、性别、爱好存储
//b、添加适当的注释
//c、添加转义字符
    char name[10] = "蒋容丞";//字符数组可以存放字符串
    short age = 23;
    float score = 99.8;
    char gender = 'M';
    char hobby[10] = "篮球";
​
    printf("姓名\t年龄\t成绩\t性别\t爱好\n%s\t%d\t%.2f\t%c\t%s",name,age,score,gender,hobby);
    
    getchar();
​
}
//自己推结果
void main() {
    int number1;
    int number2;
    int number3;
    int number4 = 50;
    int number5;
    number1 = 10;
    number2 = 20;
    number3 = number1 + number2;
    printf("\nNumber3 = %d" , number3)//30
    number5 = number4 - number3;
    printf("\nNumber5 = %d" , number5);//20
    getchar();
}

传地址或指针给指针变量

#include <stdio.h>
​
void test2(int *p);//函数声明,接收int *
void main() {
    int  num=90;
    int *p = &num;//将num 的地址赋给p
    test2(&num); //传地址(传num的地址)
    printf("\nmain() 中的num=%d", num);
    test2(p); //传指针(传p之指针也就是p里存放的指针)
    printf("\nmain() 中的num=%d", num);
    getchar();
}
void test2(int *p) {
    *p += 1;//*p 就访问num的值
}

传数组给指针变量

#include <stdio.h>
​
/* 函数声明 */
double getAverage(int *arr, int size);
double getAverage2(int *arr, int size);
int main ()
{
    /* 带有 5 个元素的整型数组 */
    int balance[5] = {1000, 2, 3, 17, 50};
    double avg;
    /* 传递一个指向数组的指针作为参数 */
    avg = getAverage2( balance, 5 );
    /* 输出返回值 */
    printf("Average value is: %f\n", avg );
    getchar();
    return 0;
}
​
//说明arr是一个指针
double getAverage(int *arr, int size)
{
    int i, sum = 0;
    double avg;
    for (i = 0; i < size; ++i)
    {
        sum += arr[i];
    }
    avg = (double)sum / size;
    return avg;
}
​
double getAverage2(int *arr, int size)
{
    int i, sum = 0;
    double avg;
    for (i = 0; i < size; ++i)
    {
        sum += *arr;
        arr++;
    }
    avg = (double)sum / size;
    return avg;
}

三、指针的算术运算

介绍:

指针是一个用数值表示的地址。可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。

指针递增操作(++)

指针递减操作(--)

#include <stdio.h>
​
const int MAX = 3;
​
int main () {
    int var[] = {10, 100, 200};
    int i, *ptr;
​
    /* 指针中最后一个元素的地址 */
    ptr = &var[MAX-1];
​
    for ( i = MAX; i > 0; i--) {
        printf("ptr存放的地址=%p, var[%d] 的地址= %p\n", ptr, i-1,&var[i-1] );
        printf("存储值:var[%d] = %d\n", i-1, *ptr );
        ptr--;
    }
    getchar();
    return 0;
}

指针+、-操作

#include <stdio.h>
​
int main () {
    int var[] = {10, 100, 200};
    int i, *ptr;
​
    ptr = var;//将var的首地址赋给ptr
    ptr += 2; //ptr的存储地址+8个字节
    printf("var[2]=%d var[2]的地址=%p ptr存储的位置(prt的地址)=%p  ptr指向的值                         =%d",var[2], &var[2], ptr, *ptr);
​
    getchar();
    return 0;
​
}

四、指针的比较

指针可以用关系运算符进行比较,如 ==、< <=(小于等于) 和 > >=(大于等于)。如果 p1 和 p2 指向两个变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较, 看下面代码,说明输出什么?

#include <stdio.h>
​
int main () {
    int var[] = {10, 100, 200};
    int *ptr;
    ptr = var;//ptr 指向var 首地址(第一个元素)
​
    if(ptr == var[0]) {//错误,类型不一样(int* 和 int不兼容)
        printf("ok1");
    }
    if(ptr == &var[0]) { // 可以
        printf("\nok2"); //输出
    }
    if(ptr == var) { //可以
        printf("\nok3"); //输出
    }
    if(ptr >= &var[1]) { //可以比较,但是返回false
        printf("\nok4");//不会输出
    }
    getchar();
}

#include <stdio.h>
​
const int MAX = 3;
int main () {
    int var[] = {10, 100, 200};
    int i, *ptr;
    ptr = var;
    i = 0;
    
    while ( ptr <= &var[MAX - 2] )//&var[1]
    {
        printf("Address of var[%d] = %x\n", i, ptr );
        printf("Value of var[%d] = %d\n", i, *ptr );
        ptr++;
        i++;
    } //会输出 10 , 100
    
    getchar();
    return 0; 
}

五、指针数组

基本介绍

要让数组的元素 指向 int 或其他数据类型的地址(指针)。可以使用指针数组。

指针数组定义 数据类型 *指针数组名[大小]; 比如: int *ptr[3];

  1. ptr 声明为一个指针数组

  2. 由 3 个整数指针组成。因此,ptr 中的每个元素,都是一个指向 int 值的指针。

#include <stdio.h>
​
const int MAX = 3;
int main ()
{
    int var[] = {10, 100, 200};
    int i, *ptr[3];
    for ( i = 0; i < MAX; i++)
    {
        ptr[i] = &var[i]; /* 赋值为整数的地址 */
    }
    for ( i = 0; i < MAX; i++)
    {
        printf("Value of var[%d] = %d\n", i, *ptr[i] );
    }
    getchar();
    return 0;
}

#include <stdio.h>
//请编写程序,定义一个指向字符的指针数组来存储字符串列表(四大名著书名), 并
//通过遍历 该指针数组,显示字符串信息 , (即:定义一个指针数组,该数组的每个
//元素,指向的是一个字符串)
​
const int Max = 3;
void main(){
    //定义一个指针数组,该数组的每个元素,指向的是一个字符串
    char *books[] = {
        "三国演义",
        "水浒传",
        "西游记",
        "红楼梦"
    };
    int  i;
    for(i = 0;i<=Max;i++){
        printf("\nbooks[%d]=%s",i,books[i]);
        //%s传递地址,所以books[i]不加*
    
    }
    getchar();
}
​

六、指向指针的指针(多重指针)

基本介绍 指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置

多重指针(二级,三级)快速入门案例

  1. 一个向指针的指针变量必须如下声明,即在变量名前放置两个星号。例如,下面声明了一个指向 int 类型指针的指针:

    int **ptr;       // ptr 的类型是 int **
  2. 当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符, 比如 **ptr

#include <stdio.h>
​
int main () {
    int var;
    int *ptr;//一级指针
    int **pptr;//二级指针
    var = 3000;
    ptr = &var;
    pptr = &ptr;
​
    printf("var的地址=%p var = %d \n", &var, var );
    printf("ptr 的本身的地址=%p ptr存放的地址=%p*ptr = %d \n", &ptr, ptr, *ptr );
    printf("pptr 本身地址 = %p pptr存放的地址=%p**pptr = %d\n", &pptr, pptr, **pptr);
​
    getchar();
    return 0;
}
​
var的地址=006FFB94 var = 3000
ptr 的本身的地址=006FFB88 ptr存放的地址=006FFB94*ptr = 3000
pptr 本身地址 = 006FFB7C pptr存放的地址=006FFB88**pptr = 3000

七、返回指针的函数

C语言 允许函数的返回值是一个指针(地址),这样的函数称为指针函数

#include <stdio.h>
#include <string.h>
​
char *strlong(char *str1, char *str2){ //函数返回的char * (指针)
    printf("\nstr1的长度%d str2的长度%d", strlen(str1), strlen(str2));
        if(strlen(str1) >= strlen(str2)){
            return str1;
        }else{
            return str2;
        }
}
int main(){
    char str1[30], str2[30], *str;//str是一个指针类型,指向字符串
    printf("\n请输入第1个字符串");
    gets(str1);
    printf("\n请输入第2个字符串");
    gets(str2);
    str = strlong(str1, str2);
    printf("\nLonger string: %s \n", str);
    getchar();
    return 0;
}

指针函数注意事项和细节

  1. 用指针作为函数返回值时需要注意,函数运行结束后会销毁在它内部定义的所有局部数据, 包括局部变量、局部数组和形式参数,函数返回的指针不能指向这些数据【案例演示】

#include <stdio.h>
​
int *func(){
    int n = 100;//局部变量 在func返回时,就会销毁
    return &n;
}
​
/*
函数运行结束后会销毁该函数所有的局部数据,
这里所谓的销毁并不是将局部数据所占用的内
存全部清零,而是程序放弃对它的使用权限,
后面的代码可以使用这块内存 
*/
​
int main(){
    int *p = func(), n;//返回得是指针
    n = *p;
    printf("\nvalue = %d\n", n);
    getchar();
    return 0;
}

  1. 函数运行结束后会销毁该函数所有的局部数据,这里所谓的销毁并不是将局部数据所占用的内 存全部清零,而是程序放弃对它的使用权限,后面的代码可以使用这块内存 【案例演示】

  2. C 语言不支持在调用函数时返回局部变量的地址,如果确实有这样的需求,需要定义局部变量为 static 变量 【案例演示】

#include <stdio.h>
​
int *func(){
​
    static int n = 100;//如果这个局部变量时static性质的,那么n存放数据的空间在静态数据取
    return &n;
}
​
int main(){
    int *p = func(), n;//func返回指针
    printf("okookokko");//可能是使用到局部变量 int n = 100占用空间
    n = *p;
    printf("\nvalue = %d\n",n);
    getchar();
    return 0;
}

练习:

#include <stdio.h>
#include <stdlib.h>
//编写一个函数,返回一个一维数组
​
int * f1(){
    static int arr[10];//必须加上static,让arr的空间在静态数据区分配.
​
    int i= 0;
    for(i = 0;i<10;i++){
        arr[i] = rand();//随机数
    }
    return arr;
}
​
void main(){
    int *p;
    int i;
    p = f1();//
    for(i = 0;i<10;i++){//p指向的是在f1 生成的数组的首地址(即第一个元素的地址)
        printf("\n%d",*(p+i));
    
    }
    getchar();
}

八、函数指针

函数指针(指向函数的指针)

基本介绍

  1. 一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似。

  2. 把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。

#include <stdio.h>
​
//max函数 接收两个int,返回较大数
int max(int a, int b){
    return a>b ? a : b;
}
​
int main(){
​
    //说明 函数指针
    //函数指针的名字是pmax
    //int 表示 该函数指针指向的函数是返回int类型
    //(int,int)表示该函数指针指向的函数形参是接收两个int
    //在定义函数指针时,也可以写上形参名
​
    int x, y, maxVal;
    int (*pmax)(int, int) = max; //
    printf("Input two numbers:");
    scanf("%d %d", &x, &y);
    //还可以用maxVal = pmax(x, y);
    maxVal = (*pmax)(x, y);
    printf("Max value: %d\n", maxVal);
    getchar();
    getchar();
    return 0;
}
​

九、回调函数

基本介绍

  1. 函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。

  2. 简单的讲:回调函数是由别人的函数执行时调用你传入的函数(通过函数指针完成)

#include <stdio.h>
#include <stdlib.h>
​
// 回调函数
//int(*f)(void)
//f就是 函数指针,它可以接收的函数是 (返回int,没有形参的函数)
//f在这里被intArray调用,充当了回调函数的角色
void initArray(int *array, int arraySize, int (*f)(void)) {
    int i ;
    for ( i=0; i<arraySize; i++)
        array[i] = f();//通过函数指针 调用了getNextRandomValue函数
}
// 获取随机值
int getNextRandomValue(void) {
    return rand();
}
int main(void) {
    int myarray[10],i;
​
    //调用initArray函数
    //传入了一个函数名getNextRandomValue(地址),需要使用函数指针接收
    initArray(myarray, 10, getNextRandomValue);
​
    for(i = 0; i < 10; i++) {
        printf("%d ", myarray[i]);
    }
    printf("\n");
    getchar();
    return 0;
}

十、指针的注意事项和细节

  1. 指针变量存放的是地址,从这个角度看指针的本质就是地址。

  2. 变量声明的时候,如果没有确切的地址赋值,为指针变量赋一个 NULL 值是好的编程习惯。

  3. 赋为 NULL 值的指针被称为空指针,NULL 指针是一个定义在标准库 <stdio.h>中的值为零的常量 #define NULL 0 [案例]

  4. 指针使用一览 (见后)

#include <stdio.h>
void main(){
    int *p = NULL;//p空指针
    int num = 34;
    p = &num;
​
    printf("p = %d",*p);//34
    getchar();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值