葡萄学习——C语言必备(六)

指针

一、指针是什么

想象你有一张藏宝图,这张藏宝图上有一个标记,告诉你宝藏在哪里。在C语言中,指针就像是这张藏宝图,它记录了一个变量(宝藏)在内存中的地址(位置)。

1. 定义指针

int *p;

这行代码定义了一个指针p,它指向一个整数类型的变量。注意前面有一个*,这是告诉编译器p是一个指针。

2. 指针和变量

假设你有一个变量a

int a = 10;

你可以让指针p指向a

p = &a; // &a 表示变量a的地址

现在,p就记录了a的地址。你可以通过p来访问a的值:

printf("%d", *p); // 输出10,*p 表示指针p指向的变量的值

 二、指针的基本操作

 1. 取地址(&

&操作符用来获取一个变量的地址。比如:

int a = 10;
int *p = &a; // p现在保存了a的地址

 2. 间接访问(*

*操作符用来通过指针访问它指向的变量的值。比如:

printf("%d", *p); // 输出10,因为*p是通过指针p访问变量a的值

 三、指针和数组

数组和指针关系非常紧密,数组名本身就是一个指针,指向数组的第一个元素。

1. 数组名作为指针

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // 或者 int *p = &arr[0]; 这两种写法等价

 现在p指向了数组arr的第一个元素。

int a = 10;

2. 通过指针访问数组元素

printf("%d", *(p + 2)); // 输出3,因为p+2指向数组的第三个元素

 四、指针的指针

想象你有一张藏宝图,这张藏宝图上有一个标记,指向另一张藏宝图,而那张藏宝图上有一个标记,指向真正的宝藏。在C语言中,这就是指针的指针。

1. 定义指针的指针

int a = 10;
int *p = &a; // p指向a
int **pp = &p; // pp指向p

 2. 通过指针的指针访问变量

printf("%d", **pp); // 输出10,因为**pp最终指向变量a

五、指针和函数

指针可以和函数一起用,比如把指针作为函数参数,或者让函数返回一个指针。

1. 指针作为函数参数

void increment(int *p) {
    (*p)++; // 通过指针p修改它指向的变量的值
}

你可以这样调用这个函数:

int a = 10;
increment(&a); // 把a的地址传给函数
printf("%d", a); // 输出11

2. 函数返回指针

int* createArray(int size) {
    int *arr = (int*)malloc(size * sizeof(int)); // 动态分配内存
    for (int i = 0; i < size; i++) {
        arr[i] = i;
    }
    return arr;
}

你可以这样使用这个函数:

int *arr = createArray(5);
printf("%d", arr[0]); // 输出0

六、指针和结构体

结构体是一种可以包含多个不同类型数据的集合。指针也可以指向结构体。

1. 定义结构体和结构体指针

typedef struct {
    int age;
    char name[50];
} Person;

Person *p;

2. 使用结构体指针

Person person = {25, "Alice"};
p = &person; // p指向person
printf("%s is %d years old.", p->name, p->age); // 使用->访问结构体成员

七、指针的常见问题

1. 野指针

野指针是指没有指向有效内存地址的指针。比如:

int *p; // p没有初始化,是一个野指针

 使用野指针是非常危险的,可能会导致程序崩溃。

 2. 悬挂指针

悬挂指针是指指向的内存已经被释放的指针。比如:

int *p; // p没有初始化,是一个野指针

八、数组与指针一些的区分

1.定义和内存分配

数组:

  • 数组是一组相同类型数据的集合,定义时需要指定大小,内存分配是连续的,大小在编译时确定。
  • 例如:int arr[5]; 定义了一个可以存储5个整数的数组,内存分配是连续的。

指针:

  • 指针是一个变量,用于存储内存地址,内存分配是动态的,大小在运行时确定。
  • 例如:int *p; 定义了一个指针变量,用于存储整数的地址。

2.数组名和指针变量的区别

数组名:

  • 数组名是一个常量指针,指向数组的第一个元素,不能修改。
  • 例如:int arr[5]; arr = &arr[1]; 是错误的,因为数组名是常量指针。

指针变量:

  • 指针变量可以动态地指向不同的内存地址。
  • 例如:int a = 10, b = 20; int *p; p = &a; p = &b; 这里指针变量p可以动态地指向ab的地址。

3.数组和指针在内存布局上的区别

数组:

  • 数组在内存中占据连续的内存空间,数组名代表了数组的起始地址。
  • 例如:int arr[5] = {1, 2, 3, 4, 5}; 数组在内存中连续存储这5个元素。

指针:

  • 指针本身占据一个小的内存空间(存储地址),它指向的内存可以是动态分配的。
  • 例如:int *p = (int *)malloc(5 * sizeof(int)); 这里动态分配了5个整数的内存空间。

4.总结

  • 数组是一组相同类型数据的集合,内存连续,大小固定;指针是一个变量,存储内存地址,内存分配动态。
  • 数组名是常量指针,指向数组首元素;指针变量可以指向不同的地址。
  • 数组在内存中占据连续空间;指针本身占小空间,指向的内存可以动态分配。
  • 数组作为函数参数传递首地址,影响原数组;指针作为参数传递地址值,可修改内容。
  • 数组内存大小固定;指针可用于动态内存管理。

九、练习题

问题:程序的输出是什么?

练习题1:指针和数组的基本操作

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *p = arr;

    // 通过指针访问数组元素
    for (int i = 0; i < 5; i++) {
        printf("%d ", *(p + i));
    }
    printf("\n");

    // 修改数组元素的值
    *(p + 2) = 10;
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}

答案:

1 2 3 4 5
1 2 10 4 5

 练习题2:指针的指针

#include <stdio.h>

int main() {
    int a = 10;
    int *p = &a;
    int **pp = &p;

    printf("%d\n", **pp); // 输出a的值

    **pp = 20;
    printf("%d\n", a);

    return 0;
}

答案:

10
20

练习题3:指针和函数

#include <stdio.h>

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 5, y = 10;
    swap(&x, &y);
    printf("x = %d, y = %d\n", x, y);
    return 0;
}

答案:

x = 10, y = 5

练习题4:指针和结构体

#include <stdio.h>

typedef struct {
    int age;
    char name[50];
} Person;

int main() {
    Person person = {25, "Alice"};
    Person *p = &person;

    printf("%s is %d years old.\n", p->name, p->age);

    p->age = 30;
    printf("%s is now %d years old.\n", person.name, person.age);

    return 0;
}

答案:

Alice is 25 years old.
Alice is now 30 years old.

练习题5:野指针和悬挂指针

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *p = (int *)malloc(sizeof(int));
    *p = 10;
    printf("%d\n", *p);

    free(p);
    // p现在是一个悬挂指针
    *p = 20; // 危险操作
    printf("%d\n", *p);

    int *q; // 野指针
    printf("%d\n", *q); // 危险操作

    return 0;
}

答案:

第一个printf输出10。
第二个printf输出未定义行为,因为p是一个悬挂指针。
第三个printf输出未定义行为,因为q是一个野指针。

十、总结口诀

  1. 指针定义要加*,取地址用&,值间接访问还用*

  2. 数组名是指针,指向首元素,数组大小编译定,指针内存动态分。

  3. 指针可变地址,数组名是常量,指针数组紧相关,操作灵活效率高。

  4. 指针函数参数传,修改内容很方便,结构体指针用->,访问成员很简单。

  5. 野指针悬指针,使用需谨慎,内存管理要小心,释放后指针别乱用。


下一节详细解释函数和结构体,byb~ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值