AutoLeaders控制组-语言指针学习笔记

一、什么是指针

指针是计算机编程中的一个概念,它是存储变量内存地址的变量。换句话说,指针是一个包含内存地址的变量,可以用于跟踪和访问存储在计算机内存中的数据。

在许多编程语言中,包括 C、C++ 和 Java,指针是一种强大且灵活的工具,可以用于对内存中的数据进行直接访问和操作。通过使用指针,我们可以实现数据结构如链表和树,进行动态内存分配,以及在函数之间传递大量数据等。

二、指针变量

2.1使用指针变量的例子

以下是一个简单的示例来说明指针的定义及其用法:

#include <stdio.h>

int main() {
    int num = 10;  // 定义一个整数变量
    int *ptr;     // 定义一个指向整数的指针变量

    ptr = &num;   // 将指针指向num变量的地址

    printf("num 的值:%d\n", num);      // 输出 num 的值
    printf("num 的地址:%p\n", &num);   // 输出 num 的地址
    printf("ptr 的值:%p\n", ptr);      // 输出 ptr 指向的地址
    printf("ptr 指向的值:%d\n", *ptr);  // 输出 ptr 指向的值

    return 0;
}

在这个示例中,我们首先定义了一个名为 num 的整数变量,然后创建了一个指向整数的指针变量 ptr。通过使用 & 运算符,我们将 ptr 的值设置为 num 的地址。然后,我们可以使用 * 运算符对指针进行间接引用,获取指针指向的值。

输出结果将显示变量 num 的值、地址,以及指针 ptr 的值和指向的值。

num 的值:10
num 的地址:0x7ffc287e0a4c
ptr 的值:0x7ffc287e0a4c
ptr 指向的值:10

这个示例演示了通过指针访问变量的值和地址的基本概念,并展示了指针在编程中的用途之一。

需要注意的是,对指针的错误使用可能导致内存访问错误和程序崩溃,因此在使用指针时应特别小心,并确保对指针进行正确的初始化和操作。

2.2怎样定义指针变量

在常见的编程语言中,指针变量是一种存储内存地址的变量。它们允许程序直接引用内存中的数据,而不是通过值进行访问。以下是在不同编程语言中定义指针变量的方法示例:

在C语言中,定义指针变量需要使用" * "运算符来指示该变量是指针类型。例如:

int *ptr; // 定义一个整型指针变量ptr

2.3怎样引用指针变量

在C语言中,可以使用指针变量来引用其他变量的地址。要引用指针变量,可以使用以下步骤:
1、声明指针变量:首先,需要声明一个指针变量,用于存储其他变量的地址。指针变量的声明需要指定指针的类型,例如 int* 表示指向整数的指针。
2、初始化指针变量:在声明指针变量后,需要将其初始化为要引用的变量的地址。可以使用取地址运算符 & 来获取变量的地址,并将其赋值给指针变量。
3、使用指针变量:一旦指针变量被初始化为某个变量的地址,就可以使用指针变量来访问该变量。可以使用解引用运算符 * 来获取指针所指向的变量的值。

以下是一个简单的示例,演示如何引用指针变量:

#include <stdio.h>

int main() {
    int num = 42;  // 声明一个整数变量
    int* ptr;     // 声明一个指向整数的指针变量

    ptr = &num;   // 将指针变量初始化为num的地址

    printf("num 的值:%d\n", num);      // 输出 num 的值
    printf("ptr 所指向的变量的值:%d\n", *ptr);  // 输出 ptr 所指向的变量的值

    return 0;
}

在上面的示例中,ptr 是一个指向整数的指针变量,通过 ptr = # 将其初始化为 num 的地址。然后,通过 *ptr 可以获取 ptr 所指向的变量的值,即 num 的值。输出结果将会是:

num 的值:42
ptr 所指向的变量的值:42

这样就成功引用了指针变量。请注意,在使用指针变量之前,确保它已经被正确初始化,否则可能会导致未定义的行为。

2.4指针变量作为函数参数

当将指针变量作为函数参数时,可以通过传递指针的地址或者直接传递指针来实现对函数外部变量的修改。下面是一个例子,演示了如何使用指针变量作为函数参数来交换两个整数的值:

#include <stdio.h>

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

int main() {
    int x = 10;
    int y = 20;

    printf("Before swap: x = %d, y = %d\n", x, y);

    swap(&x, &y);

    printf("After swap: x = %d, y = %d\n", x, y);

    return 0;
}

在上面的例子中,swap 函数接受两个指针参数 a 和 b,并通过解引用指针来交换它们所指向的值。在 main 函数中,我们声明了两个整数变量 x 和 y,并将它们的地址传递给 swap 函数。这样,swap 函数就可以通过指针修改 x 和 y 的值,从而实现了两个变量的交换。

输出结果:

Before swap: x = 10, y = 20
After swap: x = 20, y = 10

通过传递指针变量作为函数参数,可以在函数内部修改函数外部的变量值,从而实现对变量的间接操作。这在需要修改函数外部变量的值时非常有用。

三、通过指针引用数组

3.1数组元素的指针

数组是一组相同类型的元素的集合,而数组元素的指针则指向数组中的一个元素。

在C语言中,可以通过在数组名后加上索引来访问数组中的元素。例如,对于数组 arr,可以使用 arr[0] 来访问第一个元素,arr[1] 来访问第二个元素,以此类推。

如果要获取某个数组元素的指针,可以使用以下语法:

&arr[index]

其中,& 是取地址操作符,它返回其操作数的内存地址。

以下是一个示例代码,演示如何获取数组元素的指针:

#include <stdio.h>

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

    // 获取第三个元素的指针
    int *ptr = &arr[2];

    // 输出指针所指向的元素的值
    printf("Value: %d\n", *ptr);

    return 0;
}

在上面的代码中,我们定义了一个包含5个整数的数组 arr,然后通过 &arr[2] 获取了数组的第三个元素的指针,并将其赋值给 ptr 变量。最后,使用 *ptr 来访问指针所指向的元素的值,并将其打印到控制台上。

注意,在指针类型前面加上 * 号,可以表示该指针指向的值。因此,在打印指针所指向的元素值时,使用 *ptr。

3.2在引用数组元素时指针的运算

在引用数组元素时,可以使用指针运算来访问数组中的元素。在C语言中,数组名本身就是一个指向数组首元素的指针。通过对指针进行加法或减法运算,可以访问数组中的其他元素。

下面是一些示例代码,演示了如何使用指针运算引用数组元素:

#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int *ptr = arr;  // 数组名arr是指向数组首元素的指针

    // 使用指针运算访问数组元素
    printf("arr[0]: %d\n", *ptr);  // 输出: arr[0]: 1

    ptr++;  // 指针向后移动一个位置
    printf("arr[1]: %d\n", *ptr);  // 输出: arr[1]: 2

    ptr += 2;  // 指针向后移动两个位置
    printf("arr[3]: %d\n", *ptr);  // 输出: arr[3]: 4

    ptr--;  // 指针向前移动一个位置
    printf("arr[2]: %d\n", *ptr);  // 输出: arr[2]: 3

    return 0;
}

在上面的示例中,我们首先将数组名arr赋值给指针ptr,这样ptr就指向了数组的首元素。然后,通过对指针进行加法或减法运算,我们可以移动指针的位置来访问不同的数组元素。通过在指针前面加上*,我们可以获取指针所指向位置的值,即数组元素的值。

需要注意的是,指针运算要确保不越界访问数组。在上面的示例中,我们没有超出数组范围进行指针运算,以避免访问无效的内存位置。

3.3通过指针引用数组元素

在C语言中,可以使用指针来引用数组元素。指针是一个变量,它存储了内存地址。通过指针,可以访问存储在该地址上的值。

要引用数组元素,可以使用以下语法:

#include <stdio.h>

int main() {
    int array[5] = {1, 2, 3, 4, 5};
    int *ptr;
    int index = 2;

    ptr = &array[index];

    printf("Value at index %d: %d\n", index, *ptr);

    return 0;
}

在上面的示例中,我们定义了一个包含5个整数的数组array,并初始化了它。然后,我们声明了一个指向整数的指针ptr,并将其设置为指向索引为2的数组元素的地址。最后,我们通过*ptr访问该地址上的值,并将其打印出来。

输出将是:

Value at index 2: 3

这表明我们通过指针成功引用了数组元素。

3.4用数组名作函数参数

在C语言中,将数组名作为函数参数是允许的。当你将数组名作为函数参数传递时,实际上传递的是数组的地址,而不是数组本身的副本。这意味着在函数内部对数组的修改将影响到原始数组。

以下是一些与数组名作为函数参数相关的重要知识点:
1、数组作为函数参数时,在函数内部可以使用形式参数来引用该数组。形式参数是函数定义中用来接收实际参数的参数。在函数定义中,你可以使用数组符号([])来声明形式参数的类型。例如:

void myFunction(int arr[]) {
    // 函数体
}

2、在函数内部,可以通过数组操作符([])或指针操作符(*)来访问和修改数组的元素。形式参数实际上是一个指向数组首元素的指针。因此,你可以像操作普通指针一样操作数组。例如:

void myFunction(int arr[]) {
    arr[0] = 10;  // 修改数组的第一个元素
    int x = *(arr + 1);  // 获取数组的第二个元素的值
}

3、数组名传递给函数时,只传递数组的地址,而不传递整个数组的副本。这意味着,在函数内部对数组的修改将影响到原始数组。例如:

void myFunction(int arr[]) {
    arr[0] = 10;  // 修改原始数组的第一个元素
}
int main() {
    int arr[3] = {1, 2, 3};
    myFunction(arr);  // 传递数组名作为参数
    // 现在,arr[0] 的值将是 10
}

需要注意的是,虽然在函数中可以修改原始数组的值,但无法通过数组名来修改数组的大小。数组的大小在定义时已经确定,并且无法被改变。

3.5通过指针引用多维数组

通过指针引用多维数组是一种在C或C++中访问和操作多维数组的方法。多维数组可以看作是由多个一维数组组成的表格或矩阵结构。在内存中,多维数组以连续的内存块进行存储。

下面是通过指针引用多维数组的一些基本知识点:
1、声明多维数组指针:

int arr[][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
int (*ptr)[3] = arr;

2、访问多维数组元素:

int value = (*ptr)[1];

3、使用指针遍历多维数组:

for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        printf("%d ", *(*ptr + j));
    }
    printf("\n");
    ptr++;
}

4、多维数组传递给函数:

void printArray(int (*arr)[3], int rows, int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

int main() {
    int arr[][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
    printArray(arr, 3, 3);
    return 0;
}

这里的 printArray 函数接受一个指向包含 3 个整数的数组的指针,并通过行和列参数传递数组的维度信息。在 main 函数中,将 arr 数组传递给 printArray 函数。

四、通过指针引用字符串

4.1字符串的引用方式

1、字符串是一个字符数组,由连续的字符组成。在C语言中,字符串以null字符(‘\0’)结尾。
2、指针是一个存储了内存地址的变量。可以使用指针来引用字符串的地址,以及访问字符串的内容。
3、字符串字面值(比如:“Hello”)在内存中以常量形式存在,因此不能直接修改它们的内容。但是,指针可以用来引用这些字符串,并通过指针间接修改字符串的内容。
4、使用指针引用字符串可以使用不同的方法,包括以下几种常见的方式:
a、使用字符指针:可以声明一个字符指针来引用字符串的第一个字符,然后通过逐个移动指针来访问字符串中的每个字符。

char *str = "Hello";
char *ptr = str; // ptr指向字符串的第一个字符
while (*ptr != '\0') {
    // 访问字符
    printf("%c", *ptr);

    // 移动指针到下一个字符
    ptr++;
}

b、使用指针数组:可以声明一个指针数组,每个指针都指向字符串的一个字符。这样可以通过逐个访问数组元素来访问字符串中的字符。

char str[] = "Hello";
char *ptrs[6];
int i;

for (i = 0; i < 6; i++) {
    ptrs[i] = &str[i]; // 每个指针指向字符串中的一个字符
}

for (i = 0; i < 5; i++) {
    // 访问字符
    printf("%c", *ptrs[i]);
}

c、使用指向字符数组的指针:可以声明一个指向字符数组的指针,并直接操作字符数组中的内容。

char str[] = "Hello";
char *ptr = str; // 指针指向字符数组

// 修改字符数组中的字符
*ptr = 'h';
printf("%s", str); // 输出 "hello"

4.2字符指针作函数参数

字符指针可以作为函数参数传递给函数。在C语言中,字符指针用于表示字符串,它指向字符串的第一个字符。通过将字符指针作为函数参数,可以在函数内部对字符串进行操作或者访问字符串的内容。

下面是一个示例,演示了如何使用字符指针作为函数参数:

#include <stdio.h>

// 函数接受一个字符指针作为参数
void printString(char* str) {
    printf("%s\n", str);
}

int main() {
    char* message = "Hello, world!";
    printString(message); // 调用函数并传递字符指针作为参数
    return 0;
}

在上面的示例中,printString函数接受一个字符指针 str 作为参数,并使用printf函数打印出字符串。在main函数中,我们定义了一个字符指针 message,并将字符串 “Hello, world!” 的地址赋给它。然后,我们调用printString函数并将 message 作为参数传递给它。

当函数被调用时,函数内部可以通过字符指针访问传递的字符串。在示例中,printString函数使用printf函数打印出了字符串 “Hello, world!”。

需要注意的是,在使用字符指针作为函数参数时,需要确保传递的指针指向有效的字符串。否则,可能会导致未定义的行为或错误。

4.3使用字符指针变量和字符数组比较

字符指针变量和字符数组可以用于比较字符串。在C语言中,字符串是以null字符(‘\0’)结尾的字符数组。

下面是使用字符指针变量和字符数组进行字符串比较的示例代码:

#include <stdio.h>
#include <string.h>

int main() {
    char *str1 = "Hello";
    char str2[] = "Hello";

    // 使用字符指针变量比较字符串
    if (strcmp(str1, str2) == 0) {
        printf("str1 and str2 are equal.\n");
    } else {
        printf("str1 and str2 are not equal.\n");
    }

    // 使用字符数组比较字符串
    if (strcmp(str2, "Hello") == 0) {
        printf("str2 and \"Hello\" are equal.\n");
    } else {
        printf("str2 and \"Hello\" are not equal.\n");
    }

    return 0;
}

在这个例子中,我们声明了一个字符指针变量str1和一个字符数组str2,并将它们分别初始化为"Hello"。我们使用strcmp函数来比较字符串。

根据strcmp函数的返回值,如果两个字符串相等,返回值为0,我们可以使用==运算符来检查两个字符串是否相等。否则,它们是不相等的。

在上面的代码中,我们通过比较str1和str2,以及str2和字符串字面量"Hello"来展示了使用字符指针变量和字符数组进行字符串比较的方法。

输出结果将会根据字符串的比较结果而有所不同。

五、指向函数的指针

5.1什么是函数的指针

函数的指针是指向函数的指针变量。在C和C++编程语言中,函数被存储在内存中的代码段中,函数指针可以指向这个内存地址,从而允许程序在运行时动态地调用函数。
例如,如果有一个函数 int add(int a, int b),你可以声明一个指向该函数的指针如下:

int (*ptr)(int, int);

然后,可以使用函数指针来调用该函数:

int result = (*ptr)(3, 4);  // 调用函数指针

此外,函数指针还可以作为参数传递给其他函数,从而实现回调函数的概念。这在需要根据不同的条件调用不同函数的情况下非常有用。

函数指针提供了一种灵活的方式来处理函数调用,可以根据实际需求动态地选择要调用的函数,具有较高的程序设计灵活性和扩展性。

5.2用函数指针变量调用函数

要使用函数指针变量调用函数,你需要遵循以下步骤:

1、声明函数指针变量:首先,你需要声明一个函数指针变量,该变量将指向你要调用的函数。函数指针的声明方式与函数原型类似,只需将函数名替换为指针变量名,并在前面加上*符号。

例如,如果你要声明一个指向返回整数类型、接受两个整数参数的函数的指针变量,可以这样写:

int (*func_ptr)(int, int);

2、将函数地址赋给指针变量:接下来,你需要将要调用的函数的地址赋给函数指针变量。你可以使用函数名(不带括号)来获取函数的地址。

例如,如果你有一个名为add的函数,你可以将其地址赋给函数指针变量func_ptr:

func_ptr = add;

注意,这里不需要在函数名后面加上括号,因为我们不是在调用函数,而是获取函数的地址。

3、调用函数:一旦你将函数地址赋给了函数指针变量,你就可以使用函数指针来调用函数。要调用函数,只需在函数指针变量名后面加上括号,并传递相应的参数。

例如,使用上面的示例,你可以通过函数指针变量func_ptr调用add函数:

int result = func_ptr(3, 4);

这将调用add函数,并将参数3和4传递给它。函数指针的调用方式与直接调用函数相同。

下面是一个完整的示例代码:

#include <stdio.h>

int add(int a, int b) {
    return a + b;
}

int main() {
    int (*func_ptr)(int, int);
    func_ptr = add;
    int result = func_ptr(3, 4);
    printf("Result: %d\n", result);
    return 0;
}

这个示例中,我们声明了一个指向add函数的函数指针变量func_ptr,将add函数的地址赋给了它,并使用函数指针变量调用add函数。最后,我们将结果打印出来。

5.3怎样定义和使用指向函数的指针变量

在C语言中,可以使用指针运算符(*)来定义和使用指向函数的指针变量。具体来说,如果有一个函数指针类型func_ptr,可以按照以下方式来定义和使用指向函数的指针变量:
1、定义指向函数的指针变量

int add(int a, int b) {
    return a + b;
}

// 定义一个指向函数的指针变量p,并将其指向add函数
int (*p)(int, int);
p = add;
 

在上面的例子中,首先定义了一个名为add的函数,该函数接受两个整型参数a和b,并返回它们的和。然后,定义了一个名为p的指向函数的指针变量,并将其指向add函数。注意,在定义指向函数的指针变量时,需要指定指针变量所指向的函数的返回值类型和参数类型。
2、使用指向函数的指针变量

int result = p(3, 4); // 调用指向函数的指针变量p所指向的函数,并将结果赋值给result
printf("3 + 4 = %d
", result); // 输出结果为7
 

在上面的例子中,通过将指向函数的指针变量p所指向的函数作为实参传递给另一个函数,从而实现了对add函数的调用。注意,在调用指向函数的指针变量所指向的函数时,需要使用括号将实参括起来。

5.4用指向函数的指针作函数参数

在C语言中,可以使用指向函数的指针作为函数参数。具体来说,可以将指向函数的指针作为其他函数的参数传递,从而实现对函数的调用。下面是一个示例:

 
#include <stdio.h>

void print_hello(void);
void print_world(void);

void hello_world(void (*func)(void)) {
    func();
}

int main() {
    hello_world(print_hello); // 调用hello_world函数,并将print_hello函数作为参数传递
    hello_world(print_world); // 调用hello_world函数,并将print_world函数作为参数传递
    return 0;
}

void print_hello(void) {
    printf("Hello, ");
}

void print_world(void) {
    printf("world!\n");
}
 

在上面的例子中,定义了两个函数print_hello和print_world,分别用于打印"Hello, “和"world!”。然后定义了一个名为hello_world的函数,该函数接受一个指向函数的指针作为参数,并调用该函数。最后,在main函数中,通过将print_hello和print_world函数作为参数传递给hello_world函数,实现了对这两个函数的调用。

六、返回指针值的函数

在C语言中,可以使用指针运算符(*)来返回指针值。具体来说,如果有一个函数返回一个整型指针,可以按照以下方式来定义和使用该函数:

#include <stdio.h>

int *get_pointer() {
    int value = 10;
    int *p = &value;
    return p;
}

int main() {
    int *ptr = get_pointer(); // 调用get_pointer函数,并将返回的指针赋值给ptr变量
    printf("Value: %d, Address: %p\n", *ptr, ptr); // 输出ptr所指向的值和地址
    return 0;
}
 

在上面的例子中,定义了一个名为get_pointer的函数,该函数返回一个整型指针。在函数内部,定义了一个整型变量value,并将其地址赋给指针变量p。最后,通过return语句将指针变量p返回。在main函数中,调用get_pointer函数,并将返回的指针赋值给ptr变量。然后使用printf函数输出ptr所指向的值和地址。

七、指针数组和多重指针

7.1什么是指针数组

指针数组是一种特殊的数组,其元素均为指针类型。换句话说,指针数组中的每个元素都存储着一个指针,这些指针都指向相同的数据类型。例如,我们可以定义一个整型指针数组,其中每个元素都是一个整型指针,这些指针可以指向不同的整数变量或一维整型数组的元素。
与其他数组一样,指针数组也具有长度,即包含一定数量的元素,这些元素的类型均为指针。在C语言中,指针数组的定义形式通常为:dataType * arrayName [length]。这里,dataType代表指针所指向的数据类型,arrayName是数组的名称,length则是数组的长度。
指针数组常被用于处理多个字符串或者其他需要动态分配内存的数据类型。由于每个元素都是一个指针,因此可以通过改变这些指针的值来访问或操作内存中不同的数据。例如,字符型指针数组就可以实现二维字符数组的数据存储。总的来说,指针数组提供了一种灵活、方便的字符串处理和动态内存管理的方法。

7.2指向指针数据的指针变量

指向指针数据的指针变量,也就是指向指针的指针,是一种常见的数据类型。它存放的是另一个指针变量的地址,从而可以间接访问到被指向的指针变量所指向的数据。
例如,假设有一个 int 类型的变量 a,p1是指向 a 的指针变量,p2 又是指向 p1 的指针变量。在这种情况下,p1是一个一级指针,它存放的是变量a的地址;而p2是一个二级指针,它存放的是p1这个一级指针的地址。
在C语言中,我们可以使用两个星号(**)来声明一个指向指针的指针变量。例如,下面声明了一个指向 int 类型指针的指针:int **var。
这种指针的使用场景很广泛,特别是在需要通过多层间接访问数据的情况下。例如,可以使用二级指针来实现动态内存分配、二维数组的处理等复杂的数据结构操作。

7.3指针数组作main函数的形参

在C语言中,main函数是程序的入口点,它可以接受参数。尽管在许多场合中,main函数的第一行通常写成void main()或int main()的形式,此时表示main函数没有参数。但实际上,main函数完全可以接受参数。例如,我们常见的void main(int argc, char *argv[])形式中的argc和argv[]就是main函数的参数。
argc是一个整数,用来记录命令行总的参数个数。而argv是一个字符指针数组,其中每一个元素都代表一个命令行参数。特别需要注意的是,argv[0]通常是程序的名称,而argv[1]则是程序的第一个参数,以此类推。
这种使用指针数组作为main函数形参的方式,使得我们可以从命令行接收并处理多个参数。例如,我们可以利用指针数组来访问和操作传递给程序的各个字符串参数。

在C语言中,指针数组可以作为main函数的形参。下面是一个使用指针数组作为main函数形参的例子:


#include <stdio.h>

void print_array(int *arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("Element %d: %d\n", i, *arr[i]);
    }
}

int main(int argc, char *argv[]) {
    int n = argc - 1; // 计算参数个数
    int *arr[n]; // 定义指针数组

    for (int i = 0; i < n; i++) {
        arr[i] = &atoi(argv[i + 1]); // 将字符串转换为整数并存储到指针数组中
    }

    print_array(arr, n); // 调用函数打印数组元素

    return 0;
}
 

在这个例子中,我们首先定义了一个名为print_array的函数,该函数接受一个指向指针的指针和一个整数作为参数,用于打印指针数组中的元素。然后,在main函数中,我们使用命令行参数来初始化一个指针数组,并将其传递给print_array函数进行打印。

八、动态内存分配与指向它的指针变量

8.1什么是内存的动态分配

动态内存分配,顾名思义,是在程序运行期间根据需要来分配内存的一种方式。不同于静态内存分配,后者是在编译期间确定并分配固定大小的内存空间,动态内存分配的优势在于它可以根据程序的需要来实时控制内存的大小。
这种内存管理方式主要在堆区进行,而不是栈区。堆区具有较大的内存空间,可以有效地满足程序对内存的临时需求。当某一块内存不再需要时,我们可以手动通过编程释放这块内存,从而有效防止了内存泄漏的问题。
C语言中提供了两个重要的函数来实现动态内存分配和释放:malloc和free。其中,malloc函数用于从堆区申请一块连续的可用空间,大小由程序员指定,并且返回指向这块空间的指针。而free函数则用于释放之前由malloc分配的内存。需要注意的是,如果参数ptr指向的空间不是动态开辟的,free函数的行为是未定义的。
总的来说,动态内存分配是一种灵活、强大的工具,能够满足许多复杂程序对内存使用的临时性和变动性需求。但同时,它也要求程序员更加注意内存的管理,避免出现内存泄漏等问题。

8.2怎样建立内存的动态分配

在C语言中,动态内存分配是一种在程序运行期间根据需要来分配内存空间的方式。它与静态内存分配(编译期间确定并分配固定大小的内存空间)形成对比,主要的优势在于其能够根据程序的需求实时控制内存的大小。

要进行动态内存分配,我们可以使用C语言中的 malloc 函数。这个函数的作用是在堆区申请一块连续的可用空间,其大小由程序员指定,并且它会返回一个指向这块空间的指针。

例如,如果我们想要分配一个包含10个整数的数组,可以使用以下代码:

int *arr = (int*) malloc(10 * sizeof(int));

在这里, malloc 函数接收一个参数,即所需内存的字节数。由于我们要存储的是整数,因此每个整数都需要占用 sizeof(int) 个字节。所以,我们需要的内存大小就是 10 * sizeof(int) 。最后,我们将返回的地址赋值给指针变量 arr 。

此外,我们可以通过调用 free 函数来释放之前由 malloc 分配的内存。例如:

 
free(arr);
 

需要注意的是,如果参数 ptr 指向的空间不是动态开辟的, free 函数的行为是未定义的。另外,如果我们不释放动态申请的内存,那么在程序结束时,这部分内存将不会被自动回收,从而可能导致内存泄漏的问题。

九、有关指针的小结

(1)首先要准确理解指针的含义。“指针”是C语言中一个形象化的名词,形象地表示“指向”的关系,其在物理上的实现是通过地址来完成的。正如高级语言中的“变量”,在物理上是“命名的存储单元”。Windows中的“文件夹”实际上是“目录”。离开地址就不可能弄清楚什么是指针。明确了“指针就是地址”,就比较容易理解了,许多问题也迎刃而解了。例如:

  • &a是变量a的地址,也可称为变量a的指针。
  • 指针变量是存放地址的变量,也可以说,指针变量是存放指针的变量。
  • 指针变量的值是一个地址,也可以说,指针变量的值是一个指针。
  • 指针变量也可称为地址变量,它的值是地址。
  • &是取地址运算符,&a是a的地址,也可以说,&是取指针运算符。&.a是变量a的指针(即指向变量a的指针)。
  • 数组名是一个地址,是数组首元素的地址,也可以说,数组名是一个指针,是数组首元素的指针
  • 函数名是一个指针(指向函数代码区的首字节),也可以说函数名是一个地址(函数代码区首字节的地址)。
  • 函数的实参如果是数组名,传递给形参的是一个地址,也可以说,传递给形参的是一个指针。

(2)在C语言中,所有的数据都是有类型的,例如常量123并不是数学中的常数123,数学中的123是没有类型的,123和123.0是一样的,而在C语言中,所有数据都要存储在内存的存储单元中若写成123,则认为是整数.按整型的存储形式存放,如果写成123.0,则认为是单精度实数,按单精度实型的存储形式存放。此外,不同类型数据有不同的运算规则,可以说,C语言中的数据都是"有类型的数据”,或称"带类型的数据"。
对地址而言也是同样的,它也有类型、首先,它不是一个数值型数据,不是按整型或浮点型方式存储,它是按指针型数据的存储方式存储的(虽然在VisualC++中也为指针变量分配4个字节,但不同于整型数据的存储形式),指针型存储单元是专门用来存放地址的,指针型数据的存储形式就是地址的存储形式。
其次。它不是一个简单的纯地址·还有一个指向的问题,也就是说它指向的是哪种类型
的数据。如果没有这个信息,是无法通过地址存取存储单元中的数据的。所以,一个地址型的数据实际上包含3个信息:
①表示内存编号的纯地址
②它本身的类型、即指针类型
③以它为标识的存储单元中存放的是什么类型的数据,即基类型。
例如:已知变量为a为int型,&.a为a的地址,它就包括以上3个信息,它代表的是一个整型数据的地址,int是&a的基类型(即它指向的是n型的存储单元)。可以把②和③两项合成一项,如“指向整型数据的指针类型"或"基类型为整型的指针类型”,其类型可以表示为“int”型。这样,对地址数据来说,也可以说包含两个要素:内存编号(纯地址)和类型(指针类型和基类型)。这样的地址是"带类型的地址"面不是纯地址。
(3)要区别指针和指针变量。指针就是地址。而指针变量是用来存故地址的变量。有人认为指针是类型名.指针的值是地址。这是不对的。类型是没有值的,只有变量才有值,正确的说法是指针变量的值是一个地址。不要杜撰出“地址的值”这样莫须有的名词。地址本身就是一个值。
(4)什么叫“指向”?地址就意味着指向,因为通过地址能找到具有该地址的对象。对于指针变量来说,把谁的地址存放在指针变量中,就说此指针变量指向谁,但应注意:并不是任何类型数据的地址都可以存放在同一个指针变量中的,只有与指针变量的基类型相同的数据的地址才能存放在相应的指针变量中。例如:

int a,*p;	//p是int*型的指针变量,基类型是int型	
float b; p=&a
p=&b;	//a是int型,合法	//b是float 型,类型不匹配	

既然许多数据对象(如变量、数组、字符串和函数等)都在内存中被分配存储空间,就有了地址,也就有了指针。可以定义一些指针变量,分别存放这些数据对象的地址,即指向这些对象。
void *指针是一种特殊的指针,不指向任何类型的数据。如果需要用此地址指向某类型的数据,应先对地址进行类型转换。可以在程序中进行显式的类型转换,也可以由编译系统自动进行隐式转换。无论用哪种转换,读者必须了解要进行类型转换。
(5)要深入掌握在对数组的操作中正确地使用指针,搞清楚指针的指向。一维数组名代表数组首元素的地址,如:

int * p,a[10];
 p=a;

p是指向int型类型的指针变量,显然,p只能指向数组中的元素(int型变量),而不是指向整个数组。在进行赋值时一定要先确定赋值号两侧的类型是否相同,是否允许赋值。
对“p=a;”,准确地说应该是:p指向a数组的首元素,在不引起误解的情况下,有时也简称为:p指向a数组,但读者对此应有准确的理解。同理,p指向字符串,也应理解为p指向字符串中的首字符。
(6)有关指针变量的归纳比较
请自行查询相关汇总书籍
(7)指针运算.
1指针变量加(减)一个整数
例如:p++,p-一,p+i,p-i,p+=i,p一=i等均是指针变量加(减)一个整数。将该指针变量的原值(是一个地址)和它指向的变量所占用的存储单元的字节数相加(减)。
2指针变量赋值
将一个变量地址赋给一个指针变量。例如:

p=&a;(将变量a的地址赋给p)
p=array;(将数组array首元素地址赋给p)
p=&array[i];(将数组array第i个元素的地址赋给p)
p=max;(max为已定义的函数,将max的入口地址赋给p)
p1=p2;(p1和p2是基类型相同指针变量,将p2的值赋给p1)

注意:不应把一个整数赋给指针变量
3两个指针变量可以相减
如果两个指针变量都指向同一个数组中的元素,则两个指针变量值之差是两个指针之间的元素个数
4两个指针变量比较
若两个指针指向同一个数组的元素,则可以进行比较。指向前面的元素的指针变量“小于”指向后面元素的指针变量。如果p1和p2不指向同一数组则比较无意义。
(8)指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示:

p=NULL:

其中,NULL是一个符号常量,代表整数0。在stdio.h头文件中对NULL进行了定义:

# define NULL0

它使p指向地址为0的单元。系统保证使该单元不作它用(不存放有效数据)。
应注意,p的值为NULL与未对p赋值是两个不同的概念,前者是有值的(值为0),不指向任何变量,后者虽未对p赋值但并不等于p无值,只是它的值是一个无法预料的值,也就是p可能指向一个事先未指定的单元,这种情况是很危险的,因此,在引用指针变量之前应对它赋值。
任何指针变量或地址都可以与NULL作相等或不相等的比较,例如:

 if(p==NULL)..

指针是C语言中很重要的概念,是C的一个重要特色。使用指针的优点:①提高程序效率;②在调用函数时当指针指向的变量的值改变时,这些值能够为主调函数使用,即可以从函数调用得到多个可改变的值:③可以实现动态存储分配。
同时应该看到,指针使用实在太灵活,对熟练的程序人员来说,可以利用它编写出颇有特色、质量优良的程序,实现许多用其他高级语言难以实现的功能,但也十分容易出错,而且这种错误往往比较隐蔽。指针运用的错误可能会使整个程序遭受破坏,比如由于未对指针变量p赋值就向*p赋值,就可能破坏了有用的单元的内容。如果使用指针不当,会出现隐蔽的、难以发现和排除的故障。因此,使用指针要十分小心谨慎,要多上机调试程序,以弄清一些细节,并积累经验。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值