C语言入门阶段08:C语言指针

        嗨喽,大家好,我是程序猿老王,程序猿老王就是我。
        今天给大家讲一讲C语言指针。

目录

一、指针的由来

二、指针应用

三、指针含义

四、指针定义

五、指针初始化

六、指针解引用

七、指针运算

八、指针与数组

九、指针与结构体

十、指针与函数

十一、函数指针

十二、总结

十三、常见问题

Q1: 什么是野指针?

Q2: 什么是空指针?

Q3: 什么是指针常量?

Q4: 什么是常量指针?

Q5: 什么是指向指针的指针?


一、指针的由来

        指针的概念最早出现在计算机科学的早期阶段,当时计算机的内存非常昂贵,而且存储器容量很小,程序员需要尽可能地利用每一个内存单元。
        在那个时代,程序员需要手动操作内存,这样可以更高效地使用内存和控制程序的执行。指针的概念就是在这个时期被引入的,它允许程序员直接操作内存地址,这样可以更高效地使用内存和处理数据。
        C语言是在20世纪70年代由贝尔实验室的Dennis Ritchie和他的同事开发的,C语言是一种系统编程语言,被广泛应用于操作系统、编译器、网络协议、嵌入式系统等领域。C语言的指针特性使得它非常适合对底层硬件进行控制和优化,同时也可以实现高级数据结构和算法,是一门非常强大的编程语言。

二、指针应用

        在C语言中,指针被广泛应用于数据结构和算法中。例如,使用指针可以很方便地实现链表、树、图等数据结构,也可以实现常见的排序和查找算法,如快速排序、归并排序、二分查找等。
另外,指针还可以用于动态内存分配和释放,这是一种非常灵活的内存管理方式。在程序运行时,可以通过指针动态地分配内存,这样可以避免在编译时就分配过多的内存,从而节省内存空间。使用完内存后,还可以通过指针释放内存,这样可以避免内存泄漏和程序运行缓慢等问题。
        除了这些应用,指针还可以用于函数的参数和返回值。通过使用指针作为函数参数,可以避免在函数调用时复制大量的数据,从而提高程序的执行效率。通过使用指针作为函数返回值,可以实现复杂的返回值类型,如数组、结构体等,从而实现更加灵活和高效的程序设计。
        总之,指针是C语言的核心特性之一,它提供了一种高效、灵活的内存管理方式,可以实现很多高级的数据结构和算法。虽然指针也带来了一些问题和挑战,但只要小心使用,指针仍然是一种非常有用的编程工具。

三、指针含义

        指针是C语言中一种重要的数据类型,用于存储和操作内存地址。指针变量本身存储的是内存地址,而不是实际的数据值。通过指针变量可以访问和操作内存中的数据。

四、指针定义

指针的定义包括两个部分:

  • 指针类型
  • 指针变量名
    指针类型指定了指针所指向的数据类型,例如int类型的指针可以指向整型数据,char类型的指针可以指向字符数据,等等。指针类型的声明使用*符号,例如:
int *p;      // 定义一个指向int类型的指针变量p
char *s;     // 定义一个指向char类型的指针变量s

        指针变量名是一个标识符,用于标识指针变量,例如:

int *p;     // 定义一个指向int类型的指针变量p
int *count; // 定义一个指向int类型的指针变量count

五、指针初始化

        指针变量必须先初始化,才能使用。指针变量的初始化可以使用地址运算符&,将一个变量的地址赋给指针变量,例如:

int x = 10; // 定义一个整型变量x,并赋值为10
int *p;     // 定义一个指向int类型的指针变量p
p = &x;     // 将变量x的地址赋给指针变量p

上面的代码将变量x的地址赋给了指针变量p,使得p指向了变量x。
        指针变量还可以初始化为NULL,表示指针不指向任何有效的内存地址。NULL是C语言标准库中定义的一个宏,其值为0,例如:

int *p = NULL; // 定义一个指向int类型的指针变量p,初始化为NULL

六、指针解引用

        指针变量存储的是一个地址,如果要访问该地址指向的数据,需要使用指针的解引用操作符*,例如:

int x = 10;         // 定义一个整型变量x,并赋值为10
int *p;             // 定义一个指向int类型的指针变量p
p = &x;             // 将变量x的地址赋给指针变量p
printf("%d\n", *p); // 输出变量x的值,应该输出10

上面的代码使用指针的解引用操作符*,访问了指针变量p所指向的地址的数据,输出了变量x的值。

七、指针运算

        指针变量也可以进行运算。指针运算有两种:指针的自增和指针的加减运算。
        指针的自增运算符++会将指针变量的值增加指针类型所指向的数据类型的字节数,例如:

int arr[] = {1, 2, 3, 4, 5};
int *p = arr; // 将数组的第一个元素的地址赋给指针变量p
p++;          // 指针自增,p指向数组的第二个元素

        指针的加减运算会将指针变量的值加上或减去指定的偏移量,偏移量的单位是指针类型所指向的数据类型的字节数,例如:

int arr[] = {1, 2, 3, 4, 5};
int *p = arr; // 将数组的第一个元素的地址赋给指针变量p
p += 2;       // 指针加2,p指向数组的第三个元素

        需要注意的是,指针运算的结果必须指向有效的内存地址,否则会引发错误。

八、指针与数组

        在C语言中,数组名是一个指向数组第一个元素的指针常量,可以用数组名来初始化指针变量,例如:

int arr[] = {1, 2, 3, 4, 5};
int *p = arr;    // 将数组的第一个元素的地址赋给指针变量p

        这里将数组名arr赋给指针变量p,p指向了数组的第一个元素。
        指针也可以像数组一样访问数组的元素,使用指针变量名和下标来访问元素,例如:

int arr[] = {1, 2, 3, 4, 5};
int *p = arr;            // 将数组的第一个元素的地址赋给指针变量p
printf("%d\n", p[2]);    // 输出数组的第三个元素,应该输出3

上面的代码使用了指针变量名和下标来访问数组的第三个元素。

九、指针与结构体

        指针在C语言中还可以用于操作结构体。结构体是一种用户自定义的数据类型,它可以将多个不同类型的数据组合成一个整体,从而方便程序的设计和管理。结构体通常包含多个成员,每个成员可以是不同的数据类型,如整型、字符型、数组、指针等。
        使用指针可以更加方便地操作结构体。在C语言中,结构体变量可以被看作是一段连续的内存空间,结构体成员的地址是相对于结构体变量地址的偏移量。因此,可以通过指针来访问结构体的成员,这样可以方便地修改结构体的内容和实现结构体的操作。
        例如,下面是一个简单的结构体类型的定义:

struct Person {
    char name[20];
    int age;
    float height;
    float weight;
};

使用指针可以方便地操作结构体变量,例如:

struct Person p = {"Alice", 20, 170.0, 60.0};
struct Person *p_ptr = &p;
p_ptr->age = 21;

        在上面的代码中,首先定义了一个Person类型的结构体变量p,然后定义了一个指向p的指针p_ptr。通过p_ptr->age的方式,可以方便地访问和修改结构体变量p的成员age
        除了结构体的成员,指针还可以指向整个结构体变量。例如,可以通过指针动态地分配结构体变量的内存,并返回指针,实现灵活的内存管理。例如:

struct Person *p_ptr = (struct Person *) malloc(sizeof(struct Person));
p_ptr->age = 21;

        在上面的代码中,首先通过malloc函数动态地分配了一个Person类型的结构体变量的内存,然后将返回的指针转换成了struct Person类型的指针p_ptr,并设置了p_ptr->age的值。

十、指针与函数

        指针在函数参数传递中使用非常广泛,通过传递指针,函数可以修改调用者的数据。在函数声明中,使用指针类型来指定函数参数是指针类型,例如:

void func(int *p);

上面的代码声明了一个函数func,它接受一个指向int类型的指针变量p作为参数。
        在函数定义中,使用指针变量名来访问指针所指向的数据,例如:

void func(int *p) {
    *p = *p + 1;    // 修改指针所指向的数据
}

上面的代码定义了一个函数func,它接受一个指向int类型的指针变量p作为参数,在函数体中使用*p来访问指针所指向的数据,并将其加1。
        在调用函数时,将指向数据的指针作为参数传递给函数,例如:

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

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

上面的代码定义了一个swap函数,通过指针参数a和b可以交换它们所指向的变量的值。在主函数中,调用swap函数时将x和y的地址作为参数传递进去,从而实现了交换x和y的值的效果。
        指针还可以用来返回函数的结果。例如,可以定义一个函数来查找数组中的最大值,并返回指向最大值的指针,例如:

int *findMax(int *arr, int size) {
    int *max = arr;
    for (int i = 0; i < size; i++) {
        if (arr[i] > *max) {
            max = &arr[i];
        }
    }
    return max;
}

上面的代码定义了一个函数findMax,它接受一个指向int类型的数组和数组大小作为参数,并返回一个指向最大值的指针变量。
        在函数中,使用指针变量max来记录数组中最大值的位置。遍历数组,如果找到了比当前最大值更大的元素,就将max指向该元素的位置。最后,函数返回指向最大值的指针max。
        在调用函数时,将指向数组的指针和数组大小作为参数传递给函数,并使用返回的指针来访问最大值,例如:

int arr[] = {5, 2, 9, 4, 7};
int size = sizeof(arr) / sizeof(int);
int *p = findMax(arr, size);    // 调用函数,返回指向最大值的指针
printf("%d\n", *p);             // 输出最大值,应该输出9

上面的代码创建了一个int类型的数组arr,并使用sizeof运算符计算数组大小。然后调用函数findMax,将指向数组的指针arr和数组大小size作为参数传递给函数。函数会返回指向最大值的指针p,最后程序会输出最大值9。

十一、函数指针

        函数指针是指向函数的指针变量,可以用来调用该函数。在C语言中,函数指针可以作为参数传递给其他函数,可以用来实现回调函数和动态函数调用等高级特性。
        例如,定义一个遍历整数数组的函数traverse,可以使用函数指针作为回调函数参数,实现遍历数组并执行回调函数的功能:

void print(int x) {
    printf("%d ", x);
}

void traverse(int *arr, int size, void (*callback)(int)) {
    for (int i = 0; i < size; i++) {
        callback(arr[i]);
    }
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    traverse(arr, 5, print);
    return 0;
}

在traverse函数中,callback是函数指针类型的参数,通过函数指针可以调用回调函数。在调用traverse函数时,使用print函数作为回调函数参数,实现遍历数组并输出元素的功能。

十二、总结

        指针是C语言中的重要概念,它提供了一种灵活的方式来访问内存中的数据。指针变量存储了内存地址,可以用来访问变量、数组、结构体等数据类型。指针运算可以用来遍历数组、字符串等数据结构,以及动态分配内存。指针还可以用来传递函数参数和返回函数结果,它是C语言中的重要工具之一。

十三、常见问题

Q1: 什么是野指针?

A1: 野指针是指一个指针变量指向未知的内存地址,也就是没有被初始化的指针变量。使用野指针会导致不可预期的行为,包括程序崩溃等问题。因此,我们应该在定义指针变量时,给它一个合法的初始值,或者将它初始化为NULL。

Q2: 什么是空指针?

A2: 空指针是指一个指针变量指向空地址,也就是NULL常量。空指针在C语言中有重要的作用,它常常用来表示指针变量没有指向任何有效的内存地址。使用空指针时需要注意,因为它不能被解引用。

Q3: 什么是指针常量?

A3: 指针常量是指一个指针变量的值不能被改变,但是指向的数据可以被改变。在定义指针常量时需要使用const关键字,例如:

int num = 10;
const int *p = #

上面的代码定义了一个指针常量p,它指向int类型的数据,但是p的值不能被改变。这意味着,p不能被用来指向其他的地址,但是可以通过p来访问所指向的数据。

Q4: 什么是常量指针?

A4: 常量指针是指一个指针变量指向的地址不能被改变,但是指向的数据可以被改变。在定义常量指针时需要使用const关键字,例如:

int num1 = 10;
int num2 = 20;
int *const p = &num1;
*p = 100;     // 合法,可以修改指向的数据
p = &num2;    // 非法,不能修改指向的地址

上面的代码定义了一个常量指针p,它指向int类型的数据,但是p指向的地址不能被改变。这意味着,p不能被用来指向其他的地址,但是可以通过p来访问所指向的数据。

Q5: 什么是指向指针的指针?

A5: 指向指针的指针,也就是二级指针,是指一个指针变量存储的是另一个指针变量的地址。在C语言中,我们可以使用二级指针来实现对二维数组的动态内存分配。例如:

int **arr;
arr = (int **)malloc(10 * sizeof(int *));
for (int i = 0; i < 10; i++) {
    arr[i] = (int *)malloc(10 * sizeof(int));
}

上面的代码动态分配了一个10行10列的二维数组,其中arr是一个二级指针,指向一个一维指针数组,而每个一维指针又指向一个整型数组。

---END---


        关于更多嵌入式C语言、FreeRTOS、RT-Thread、Linux应用编程、linux驱动等相关知识,关注公众号【嵌入式Linux知识共享】,后续精彩内容及时收看了解。

你可能感兴趣的:(C语言入门阶段,c语言,开发语言,学习方法)