C 语言 “神秘魔杖”—— 指针初相识,解锁编程魔法大门(一)

文章目录

  • 一、概念
    • 1、取地址操作符(&)
    • 2、解引用操作符(*)
    • 3、指针变量
      • 1、 声明和初始化
      • 2、 用途
  • 二、内存和地址
  • 三、指针变量类型的意义
    • 1、 指针变量类型的基本含义
    • 2、 举例说明不同类型指针变量的意义
  • 四、const修饰指针
    • 1、const修饰指针的三种情况
  • 五、指针运算
    • 1、指针的算术运算
    • 2、指针的关系运算
  • 六、野指针
    • 1、野指针的定义
    • 2、野指针的示例
    • 3、造成野指针的原因
    • 4、如何避免野指针
  • 七、assert断言
    • 1、assert断言的基本概念
  • 八、指针的使用和传址调用
    • 1、指针的使用
    • 2、传址调用

一、概念

  • 指针是C语言中的一个重要概念,它是一个变量,其值为另一个变量的地址。简单来说,指针“指向”了内存中的某个位置,这个位置存储着其他变量的值。就好像你有一个房间号(指针),通过这个房间号可以找到房间里存放的东西(变量的值);也就是可以通过告诉我们房间号(指针),快速的找到房间里的内容(变量的值)。

1、取地址操作符(&)

  • 含义
    • 取地址操作符&用于获取变量在内存中的地址。在C语言中,每个变量都被存储在内存中的某个位置,这个位置有一个唯一的地址。通过&操作符,我们可以得到这个地址的值。
  • 示例
    • 以下是一个简单的示例代码,展示如何使用取地址操作符。
#include <stdio.h>
int main() {int num = 10;// 使用取地址操作符获取num的地址int *ptr = &num;printf("The address of num is: %p\n", (void*)&num);return 0;
}
 - 在这个例子中,定义了一个整型变量`num`并初始化为`10`。- 然后通过`&num`获取`num`的地址,并将这个地址赋值给一个指针变量`ptr`。- `%p`是用于打印指针(地址)的格式化说明符,由于`printf`函数的参数要求,将`&num`强制转换为`(void*)`类型。

2、解引用操作符(*)

  • 含义
    • 解引用操作符*用于访问指针所指向的内存位置中的值。当我们有一个指针变量,它存储了一个变量的地址,通过解引用操作符可以获取该地址所对应的实际值。
  • 示例
    • 以下是一个示例,演示了解引用操作符的使用。
#include <stdio.h>
int main() {int num = 10;int *ptr = &num;// 使用解引用操作符获取ptr指向的内存中的值printf("The value pointed to by ptr is: %d\n", *ptr);return 0;
}
 - 在这里,首先定义了一个整型变量`num`和一个指向`int`类型的指针`ptr`,并将`num`的地址赋值给`ptr`。- 然后通过`*ptr`来解引用指针,获取`ptr`所指向的内存位置(也就是`num`所在的内存位置)中的值,- 并将其打印出来。这个值就是`10`,因为`ptr`指向`num`,而`num`的值为`10`。

总的来说,取地址操作符&和解引用操作符*是C语言中操作指针的重要工具,它们经常一起配合使用,使得程序员能够灵活地处理内存中的数据。

3、指针变量

  • 指针变量是一种特殊的变量,它存储的是另一个变量的内存地址。在C语言中,变量在内存中有自己的存储位置,这个位置可以通过地址来表示。指针变量就像是一个指向其他变量的“指示器”,它本身占用一定的内存空间,用于存放目标变量的地址。

1、 声明和初始化

  • 声明格式
    • 数据类型 *指针变量名;
    • 例如,int *p;声明了一个名为p的指针变量,它可以用来存储int类型变量的地址。这里的*是一个说明符,表示p是一个指针变量,它指向的数据类型是int
  • 初始化
    • 指针变量在使用前最好进行初始化。可以通过取地址操作符&来获取变量的地址并赋值给指针变量。例如:
int num = 10;
int *p = &num;
 - 在这个例子中,先定义了一个整型变量`num`并赋值为`10`,然后定义了一个指针变量`p`,- 并通过`&num`将`num`的地址赋值给`p`,这样`p`就指向了`num`。

2、 用途

  • 访问变量的值
    • 通过解引用指针变量(使用*操作符)可以访问它所指向变量的值。例如:
#include <stdio.h>
int main() {int num = 10;int *p = &num;// 解引用指针p来获取num的值printf("The value of num through pointer is %d\n", *p);return 0;
}
 - 这里,`*p`获取了`p`所指向的变量(即`num`)的值,然后将其打印出来。
  • 函数参数传递
    • 指针变量在函数参数传递中有重要作用。当我们想要在函数内部修改外部变量的值时,可以将变量的地址传递给函数,函数通过指针来访问和修改该变量。例如:
#include <stdio.h>
// 函数用于交换两个整数的值
void swap(int *a, int *b) {int temp;temp = *a;*a = *b;*b = temp;
}
int main() {int x = 5, y = 10;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`将`x`和`y`的地址传递给`swap`函数,从而实现了`x`和`y`值的交换。
  • 了解指针中的操作符,我们来举一个例子:假设有一个整型变量int num = 10;,可以定义一个指针来指向这个变量。int *p;这里p就是一个指针,它可以用来存储num的地址。通过p=&num;操作,就使得p指向了num在这里插入图片描述

二、内存和地址

  • 在计算机中,内存就像是一个个小格子(字节),每个格子都有一个唯一的编号,这个编号就是地址。变量存储在内存中,指针存储的就是这些变量的内存地址。也就是说,指针就是这个内存单元的地址。
    在这里插入图片描述

  • 例如,对于上面的num变量,它在内存中有一个地址。如果num的地址是0x100(这是假设的十六进制地址),那么p的值在执行p = &num;后就会变为0x100。可以通过printf("%p\n", p);来打印p所指向的地址。
    在这里插入图片描述

三、指针变量类型的意义

1、 指针变量类型的基本含义

  • 在C语言中,指针变量的类型非常重要。指针变量的类型决定了它所指向的数据类型。例如,int *类型的指针是用来指向int类型的数据,char *类型的指针是用来指向char类型的数据。
  • 这种类型规定了指针在进行解引用操作时,编译器会按照该类型所对应的字节数来访问内存。不同数据类型在内存中占用的字节数不同,比如在大多数32位系统中,int类型通常占用4个字节,char类型占用1个字节。

2、 举例说明不同类型指针变量的意义

  • 指向整数的指针(int *
    • 示例代码:
#include <stdio.h>
int main() {int num = 10;int *ptr = &num;// 解引用指针,获取整数的值printf("The value of num through pointer is %d\n", *ptr);// 指针加法运算ptr++;// 此时ptr指向的地址向后偏移了一个int类型的大小(通常为4字节)// 这里只是展示指针类型对偏移量的影响,实际这样使用可能会导致未定义行为// 因为ptr已经指向了num之后的内存位置,而这个位置可能是未分配给程序使用的return 0;
}
 - 在这个例子中,`int *ptr`声明了一个指向`int`类型的指针`ptr`。当`ptr`被初始化为`&num`后,通过`*ptr`解引用可以获取`num`的值。- 当执行`ptr++`时,指针`ptr`会在内存中向后移动`sizeof(int)`个字节(在常见系统中是4字节),这是因为`ptr`是`int *`类型,- 编译器知道`int`类型数据的大小,所以会按照这个大小来移动指针。
  • 指向字符的指针(char *
    • 示例代码:
#include <stdio.h>
int main() {char ch = 'A';char *cptr = &ch;printf("The character through pointer is %c\n", *cptr);cptr++;// 此时cptr指向的地址向后偏移了一个char类型的大小(1字节)return 0;
}
 - 对于`char *cptr`,它指向`char`类型的数据。当`cptr`被初始化为`&ch`后,通过`*cptr`可以获取`ch`的值。执行`cptr++`时,- 指针会在内存中向后移动`sizeof(char)`个字节(1字节),这是因为`char`类型数据在内存中占用1字节,- 指针的移动是根据其指向的数据类型来确定的。
  • 指向结构体的指针(struct xxx *
    • 假设我们有一个简单的结构体:
#include <stdio.h>
struct Student {char name[20];int age;
};
int main() {struct Student stu = {"John", 20};struct Student *sptr = &stu;// 解引用结构体指针,访问结构体成员printf("The student's name is %s and age is %d\n", sptr->name, sptr->age);return 0;
}
 - 在这里,`struct Student *sptr`是一个指向`struct Student`类型结构体的指针。- 当它被初始化为`&stu`后,通过`->`操作符(这是一种用于结构体指针访问成员的便捷操作符,- 等价于`(*sptr).name`和`(*sptr).age`)可以访问结构体`stu`中的成员。如果要移动这个指针,- 编译器会按照`struct Student`类型结构体的大小(这个大小是`name`成员数组的大小加上`age`成员占用的大小)来移动指针。

四、const修饰指针

1、const修饰指针的三种情况

  • 指向常量的指针(const修饰所指对象)
    • 含义
      • 这种情况下,指针所指向的数据被视为常量,不能通过该指针来修改所指向的数据,但指针本身可以重新指向其他内存单元。
    • 语法形式
      • const 数据类型 *指针变量名;,例如const int *p;
    • 示例
#include <stdio.h>
int main() {int num1 = 10;int num2 = 20;const int *p = &num1;// 下面这行代码是错误的,因为不能通过p修改它所指向的数据// *p = 30;p = &num2;  // 这是可以的,指针p可以重新指向num2printf("The value pointed to by p is %d\n", *p);return 0;
}
 - 在这个例子中,`p`是一个指向`const int`类型的指针,也就是`p`所指向的`int`数据被当作常量。- 当试图通过`*p = 30`修改`p`所指向的数据时,编译器会报错。但可以将`p`重新指向`num2`,因为只是改变了指针的指向,- 而没有违反`const`的限制。
  • 常量指针(const修饰指针本身)
    • 含义
      • 指针本身是常量,它不能再重新指向其他内存单元,但可以通过该指针修改它所指向的数据。
    • 语法形式
      • 数据类型 *const指针变量名;,例如int *const p;
    • 示例
#include <stdio.h>
int main() {int num1 = 10;int *const p = &num1;*p = 30;  // 这是可以的,能够修改p所指向的数据// 下面这行代码是错误的,因为p是常量指针,不能重新指向其他内存单元// p = &num2;printf("The value of num1 after modification is %d\n", num1);return 0;
}
 - 这里,`p`是一个常量指针,它被初始化为指向`num1`后,就不能再指向其他变量了。- 不过,可以通过`*p = 30`修改`p`所指向的`num1`的值,因为`const`修饰的是指针本身,而不是它所指向的数据。
  • 指向常量的常量指针(const同时修饰指针和所指对象)
    • 含义
      • 指针本身不能重新指向其他内存单元,并且不能通过该指针修改所指向的数据。
    • 语法形式
      • const 数据类型 *const指针变量名;,例如const int *const p;
    • 示例
#include <stdio.h>
int main() {int num1 = 10;const int *const p = &num1;// 下面这行代码是错误的,不能通过p修改所指向的数据// *p = 30;// 下面这行代码也是错误的,p不能重新指向其他内存单元// p = &num2;return 0;
}
 - 在这个例子中,`p`是一个指向常量的常量指针。既不能通过`*p`修改`p`所指向的数据,也不能让`p`重新指向其他内存单元,- `p`的指向和它所指向的数据都被`const`修饰固定了。
  • 指针可以进行算术运算,如加法、减法等。当指针加上或减去一个整数时,它实际移动的字节数是该整数乘以指针所指向数据类型的大小。
  • 例如,对于一个int *p,如果p的值是0x100,执行p++;后,p的值会增加sizeof(int)字节。在32位系统中,int占4字节,所以p的值会变为0x100 + 4 = 0x104
  • 指针还可以进行减法运算,用来计算两个指针之间的距离。例如,有两个指针p1p2指向同一个数组中的元素,int *p1, *p2;p1 - p2的结果是两个指针之间元素的个数。

五、指针运算

1、指针的算术运算

  • 指针与整数相加/相减
    • 含义
      • 当指针与一个整数进行加法或减法运算时,指针移动的字节数是该指针所指向的数据类型大小乘以这个整数。例如,对于一个int *类型的指针(假设int类型占4字节),如果与整数n相加,那么指针在内存中实际向前移动4 * n字节。
    • 示例
#include <stdio.h>
int main() {int arr[] = {1, 2, 3, 4, 5};int *p = &arr[0];// 指针p向后移动2个int类型的长度p = p + 2;printf("The value pointed to by p after addition is %d\n", *p);// 指针p向前移动1个int类型的长度p = p - 1;printf("The value pointed to by p after subtraction is %d\n", *p);return 0;
}
 - 在这个例子中,首先定义了一个整型数组`arr`,并让指针`p`指向数组的第一个元素`arr[0]`。- 当执行`p = p + 2`时,由于`p`是`int *`类型,`int`类型通常占4字节,- 所以`p`在内存中向后移动了`2 * 4 = 8`字节,此时`p`指向`arr[2]`,输出为`3`。- 接着执行`p = p - 1`,`p`向前移动了4字节,此时`p`指向`arr[1]`,输出为`2`。
  • 两个指针相减
    • 含义
      • 两个相同类型的指针相减,结果是两个指针之间相隔的元素个数,而不是字节数。这个运算的结果类型是ptrdiff_t(在<stddef.h>头文件中定义的有符号整数类型)。这个运算通常用于计算数组中两个元素之间的距离。
    • 示例
#include <stdio.h>
#include <stddef.h>
int main() {int arr[] = {1, 2, 3, 4, 5};int *p1 = &arr[1];int *p2 = &arr[3];ptrdiff_t diff = p2 - p1;printf("The difference between p2 and p1 is %td\n", diff);return 0;
}
 - 在这里,定义了数组`arr`,`p1`指向`arr[1]`,`p2`指向`arr[3]`。- 当计算`p2 - p1`时,结果是`2`,因为`p2`和`p1`之间相隔2个`int`类型的元素。

2、指针的关系运算

  • 含义
    • 指针可以进行关系运算(如><>=<===!=),用于比较两个指针在内存中的位置关系。通常用于判断指针是否指向同一个数组中的元素,或者判断一个指针是否超出了某个数组的范围。
  • 示例
#include <stdio.h>
int main() {int arr[] = {1, 2, 3, 4, 5};int *p1 = &arr[0];int *p2 = &arr[3];if (p2 > p1) {printf("p2 points to a later element in the array than p1\n");}return 0;
}
 - 在这个例子中,定义了数组`arr`,`p1`指向`arr[0]`,`p2`指向`arr[3]`。- 通过比较`p2 > p1`,因为`p2`在内存中指向的位置在`p1`之后,所以条件成立,会输出相应的信息。

需要注意的是,指针运算应该在合理的范围内进行,特别是对于非数组元素的指针进行算术运算可能会导致未定义行为。

六、野指针

1、野指针的定义

  • 野指针是指指针变量指向的内存位置是不确定的(随机的、不可预知的)。野指针不是NULL指针,NULL指针明确地指向地址为0的内存空间,而野指针指向的位置是未定义的,可能是已经被释放的内存区域、未分配的内存区域或者其他非法的内存地址。使用野指针可能会导致程序崩溃、数据损坏或者产生不可预测的行为。

2、野指针的示例

  • 下面是一个产生野指针的简单示例:
#include <stdio.h>
int main() {int *p;// 此时p是一个未初始化的指针,它的值是随机的,是野指针*p = 10;return 0;
}
  • 在这个例子中,定义了一个指针变量p,但是没有对它进行初始化。然后就试图通过*p = 10来修改p所指向的内存位置的值。由于p没有被初始化,它指向的是一个随机的内存地址,这个操作可能会导致程序崩溃或者出现其他错误。

3、造成野指针的原因

  • 指针变量未初始化
    • 这是最常见的原因之一。当定义一个指针变量时,如果没有给它赋初值,它的值是随机的,就会成为野指针。例如上面的例子中,int *p;定义后没有初始化p就直接使用。
  • 指针所指向的内存被释放后,指针没有置为NULL
    • 当使用free()函数(在动态内存分配中)释放了指针所指向的内存后,指针本身的值(即内存地址)并没有改变,它仍然指向原来的内存位置。而这个内存位置已经被释放,再通过这个指针访问内存就会出现问题。例如:
#include <stdio.h>
#include <stdlib.h>
int main() {int *p = (int *)malloc(sizeof(int));if (p!= NULL) {*p = 10;// 释放内存free(p);// 此时p成为野指针,因为它仍然指向刚刚被释放的内存位置*p = 20; }return 0;
}
  • 在这个例子中,通过malloc函数分配了一块内存给p,然后给这块内存赋值为10。接着使用free函数释放了这块内存,但是p仍然指向这块被释放的内存。当试图再次通过*p = 20修改这个内存位置的值时,就会出现错误,因为这块内存已经被释放,此时p是野指针。

4、如何避免野指针

  • 初始化指针变量
    • 尽量在定义指针变量时就对它进行初始化。如果暂时不知道指针应该指向哪里,可以将它初始化为NULL。例如int *p = NULL;,这样在使用p之前就可以通过判断p == NULL来确定是否可以安全地进行解引用操作。
  • 及时将释放内存后的指针置为NULL
    • 在使用free()函数释放指针所指向的内存后,应该立即将指针赋值为NULL。例如:
#include <stdio.h>
#include <stdlib.h>
int main() {int *p = (int *)malloc(sizeof(int));if (p!= NULL) {*p = 10;free(p);p = NULL;// 此时p为NULL,后续代码如果不小心使用p,就可以通过判断p == NULL避免错误操作}return 0;
}
  • 注意指针的作用域和生命周期
    • 确保指针在其指向的对象的生命周期内有效。例如,在一个函数内部定义的局部指针,在函数返回后,如果这个指针指向的是函数内部的局部变量,那么这个指针就不应该再被使用,因为函数返回后,局部变量的内存已经被释放。# 七、assert断言
  • assert是一个宏,用于在程序调试阶段检查一个条件是否为真。当条件为假时,程序会终止并输出错误信息。在涉及指针时,可以使用assert来检查指针是否为NULL等情况。
  • 例如:
    int *p = NULL;
    assert(p!= NULL);  // 程序会在这里终止,因为p为NULL
    
  • 通常在程序开发过程中,使用assert可以帮助发现一些潜在的指针错误,提高程序的健壮性。不过在发布版本中,可能会因为性能等原因关闭assert检查。

七、assert断言

1、assert断言的基本概念

  • 定义
    • assert是一个在C语言标准库<assert.h>中定义的宏。它用于在程序开发过程中进行调试检查,帮助程序员验证程序中的假设是否成立。其主要功能是检查一个表达式的值是否为真,如果表达式的值为假(即为0),则程序会以一种“标准错误”的方式终止,并输出相关的错误信息,包括断言失败的表达式、文件名和行号等内容。
  • 目的
    • 用于捕捉程序中的逻辑错误,特别是在程序开发阶段,可以帮助定位那些不应该出现的错误情况。例如,在使用指针时,可以通过assert来检查指针是否为NULL,以避免因为空指针解引用而导致的程序崩溃。
  1. 使用assert断言的示例
    • 检查指针是否为NULL
      • 示例代码如下:
#include <stdio.h>
#include <assert.h>
int main() {int *p = NULL;// 使用assert检查指针p是否为NULLassert(p!= NULL);// 如果p为NULL,程序会在这里终止,并输出错误信息// 因为p是NULL,所以下面这行代码不会执行*p = 10;return 0;
}
 - 在这个例子中,首先定义了一个指针`p`并将其初始化为`NULL`。- 然后使用`assert(p!= NULL)`来检查`p`是否不为`NULL`。由于`p`实际上是`NULL`,所以这个断言会失败。- 当断言失败时,程序会终止,并输出类似于以下的错误信息(具体格式可能因编译器和环境而异):- `Assertion failed: p!= NULL, file main.c, line 7`- 这表明在`main.c`文件的第7行,`p!= NULL`这个断言不成立。- 这样就可以很容易地定位到错误发生的位置,并且知道是由于指针为`NULL`导致的问题。
  • 检查数组索引是否越界(结合指针运算)
    • 假设我们有一个函数,用于遍历一个整数数组并打印元素,代码如下:
#include <stdio.h>
#include <assert.h>
void print_array(int *arr, int size) {int i;// 对于每个合法的索引,打印数组元素for (i = 0; i < size; i++) {assert((arr + i) >= arr);// 检查指针是否还在数组范围内printf("%d ", *(arr + i));}
}
int main() {int arr[] = {1, 2, 3, 4, 5};int size = sizeof(arr) / sizeof(arr[0]);print_array(arr, size);return 0;
}
 - 在`print_array`函数中,`for`循环用于遍历数组。- 在每次循环中,使用`assert((arr + i) >= arr)`来检查`arr + i`这个指针是否还在数组范围内。- `arr + i`表示数组`arr`中第`i`个元素的地址,它应该总是大于或等于数组的起始地址`arr`。- 如果在循环过程中由于某种原因(例如数组索引计算错误)导致`arr + i`小于`arr`,- 断言就会失败,程序会终止并输出错误信息,帮助我们发现数组索引越界的问题。

八、指针的使用和传址调用

1、指针的使用

  • 访问变量
    • 基本原理
      • 通过指针可以间接访问它所指向的变量。首先要获取变量的地址,然后将这个地址存储在指针变量中,最后通过解引用指针来访问变量的值。
    • 示例
#include <stdio.h>
int main() {int num = 10;int *p = &num;// 解引用指针p获取num的值printf("The value of num through pointer is %d\n", *p);return 0;
}
 - 在这个例子中,`int *p = &num;`声明了一个指针`p`并使其指向变量`num`。`*p`用于解引用指针,从而获取`p`所指向变量(即`num`)的值。
  • 动态内存分配
    • 基本原理
      • C语言中的malloccallocrealloc函数用于动态分配内存,返回的是指向所分配内存块的指针。malloc函数分配指定字节数的内存,calloc函数在分配内存的同时将内存块初始化为0,realloc函数用于重新分配已经分配过的内存块的大小。
    • 示例(使用malloc
#include <stdio.h>
#include <stdlib.h>
int main() {int *arr;int n = 5;// 分配能容纳5个整数的内存空间arr = (int *)malloc(n * sizeof(int));if (arr == NULL) {// 检查内存分配是否成功printf("Memory allocation failed!\n");return -1;}// 使用分配的内存for (int i = 0; i < n; i++) {arr[i] = i + 1;}for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}// 释放内存free(arr);return 0;
}
 - 这里,`arr = (int *)malloc(n * sizeof(int));`分配了足够容纳`n`个`int`类型数据的内存空间。- 如果`arr`不为`NULL`,则表示内存分配成功,可以像使用普通数组一样使用`arr`。- 最后,使用`free(arr)`释放分配的内存。

2、传址调用

  • 基本原理
    • 在C语言中,函数参数传递默认是值传递,即函数会复制一份实参的值作为形参。传址调用则是将变量的地址作为参数传递给函数。这样,函数内部可以通过指针来访问和修改外部变量的值。
  • 示例(交换两个数)
#include <stdio.h>
// 交换两个整数的函数,使用传址调用
void swap(int *a, int *b) {int temp;temp = *a;*a = *b;*b = temp;
}
int main() {int x = 5, y = 10;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`函数中,通过`swap(&x, &y)`将`x`和`y`的地址传递给`swap`函数。在`swap`函数内部,通过解引用指针`*a`和`*b`来访问和交换`x`和`y`的值。这样,就实现了在函数内部修改外部变量的值。
  • 通过传址调用返回多个值
    • 示例(计算一个数的平方和立方)
#include <stdio.h>
// 计算一个数的平方和立方,通过指针返回结果
void square_and_cube(int num, int *square, int *cube) {*square = num * num;*cube = num * num * num;
}
int main() {int num = 3;int square, cube;square_and_cube(num, &square, &cube);printf("The square of %d is %d and the cube is %d\n", num, square, cube);return 0;
}
 - 这里,`square_and_cube`函数接受一个整数`num`和两个指针`square`和`cube`。- 函数内部通过解引用指针来将计算结果(`num`的平方和立方)- 存储在`main`函数中定义的`square`和`cube`变量中,从而实现了通过一个函数返回多个值的功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/61752.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android 使用OpenGLES + MediaPlayer 获取视频截图

概述 Android 获取视频缩略图的方法通常有: ContentResolver: 使用系统数据库MediaMetadataRetriever: 这个是android提供的类&#xff0c;用来获取本地和网络media相关文件的信息ThumbnailUtils: 是在android2.2&#xff08;api8&#xff09;之后新增的一个&#xff0c;该类为…

博泽Brose EDI项目案例

Brose 是一家德国的全球性汽车零部件供应商&#xff0c;主要为全球汽车制造商提供机电一体化系统和组件&#xff0c;涵盖车门、座椅调节系统、空调系统以及电动驱动装置等。Brose 以其高质量的创新产品闻名&#xff0c;在全球拥有多个研发和生产基地&#xff0c;是全球第五大家…

python使用python-docx处理word

文章目录 一、python-docx简介二、基本使用1、新建与保存word2、写入Word&#xff08;1&#xff09;打开文档&#xff08;2&#xff09;添加标题&#xff08;3&#xff09;添加段落&#xff08;4&#xff09;添加文字块&#xff08;5&#xff09;添加图片&#xff08;6&#xf…

aws(学习笔记第十五课) 如何从灾难中恢复(recover)

aws(学习笔记第十五课) 如何从灾难中恢复 学习内容&#xff1a; 使用CloudWatch对服务器进行监视与恢复区域(region)&#xff0c;可用区(available zone)和子网(subnet)使用自动扩展(AutoScalingGroup) 1. 使用CloudWatch对服务器进行监视与恢复 整体架构 这里模拟Jenkins Se…

lwip raw、netcoon、socket三种接口编程的区别

目录 一、前言 二、LWIP 简介 三、LWIP RAW 编程 1.概念与原理 2.编程模型与流程 3.示例代码 4.优点与缺点 四、LWIP NETCONN 编程 1.概念与原理 2.编程模型与流程 3.示例代码 4.优点与缺点 五、LWIP SOCKET 编程 1.概念与原理 2.编程模型与流程 3.示例代码 …

【XGlassTerminal.js】快速 构建 炫酷 终端 网页 以及 Linux 模拟器 在线!!

XGlassTerminal.js XGlassTerminal.js 是一个用于构建前端终端样式的 JavaScript 库。它允许开发者轻松地创建一个具有终端风格的用户界面&#xff0c;并对用户输入的命令进行事件处理。 该库提供了丰富的功能&#xff0c;包括文本添加、命令处理、点击事件绑定等。 同时还支…

车辆传动系统的simulink建模与仿真,分析加速和刹车两个工况

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 车辆传动系统的simulink建模与仿真,分析加速和刹车两个工况。模型包括车辆模块&#xff0c;传动模块&#xff0c;发动机模块&#xff0c;换挡模块&#xff0c;刹车油门输入模块…

宝塔配置定时任务详解

文章目录 宝塔配置定时任务详解一、引言二、配置定时任务1、登录宝塔面板2、添加定时任务2.1、步骤 3、配置任务3.1、设置任务名称和执行周期3.2、设置执行命令 4、保存并测试 三、使用示例1、备份数据库2、清理日志文件 四、总结 宝塔配置定时任务详解 一、引言 在服务器管理…

【C++笔记】map和set的使用

【C笔记】map和set的深度剖析 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;C笔记 文章目录 【C笔记】map和set的深度剖析前言一.set1.1 序列式容器和关联式容器1.2 set系列的使用1.3 set类的介绍1.4 set的构造和迭代器1.5 set的增删查1.6…

springboot+mybatis对接使用postgresql中PostGIS地图坐标扩展类型字段

方案一&#xff08;完全集成和自动解析&#xff09;&#xff1a; <dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId></dependency> 使用 org.postgresql.geometric包下的 PGpoint 类来接收数据库中POINT…

《只狼》运行时提示“mfc140u.dll文件缺失”是什么原因?“找不到mfc140u.dll文件”要怎么解决?教你几招轻松搞定

《只狼》运行时提示“mfc140u.dll文件缺失”的科普与解决方案 作为一名软件开发从业者&#xff0c;在游戏开发和维护过程中&#xff0c;我们经常会遇到各种运行时错误和系统报错。今天&#xff0c;我们就来探讨一下《只狼》这款游戏在运行时提示“mfc140u.dll文件缺失”的原因…

华为HarmonyOS 让应用快速拥有账号能力 -- 3 获取用户手机号

场景介绍 当应用对获取的手机号时效性要求不高时&#xff0c;可使用Account Kit提供的手机号授权与快速验证能力&#xff0c;向用户发起手机号授权申请&#xff0c;经用户同意授权后&#xff0c;获取到手机号并为用户提供相应服务。以下只针对Account kit提供的手机号授权与快…

linux环境人大金仓数据库修改密码

1.进入人大金仓安装目录 cd /home/opt/Kingbase/ES/V9/Server/bin2.连接数据库 ./ksql -U system -d mydb -h 127.0.0.1 -p 54321-u 用户名 -d 数据库名 -h ip地址 -p 端口号 3.修改密码 ALTER USER system WITH PASSWORD 密码;

使用R语言进行美国失业率时空分析(包括绘图)

今天写一篇利用R语言&#xff0c;针对面板数据的简单分析与绘图。让我们直接开始把。 一、数据准备 这次的示例数据非常简单&#xff0c;只有一个shp格式的美国区县矢量数据&#xff0c;我们在QGIS中打开数据查看一下它的属性表。事实上我们需要的数据都在属性表的字段中。 二…

单片机几大时钟源

在单片机中&#xff0c;MSI、HSI和HSE通常指的是用于内部晶振配置的不同功能模块&#xff1a; MSI (Master Oscillator System Interface)&#xff1a;这是最低级的一种时钟源管理单元&#xff0c;它控制着最基本的系统时钟&#xff08;SYSCLK&#xff09;&#xff0c;一般由外…

前端开发 之 15个页面加载特效上【附完整源码】

文章目录 一&#xff1a;彩球环绕加载特效1.效果展示2.HTML完整代码 二&#xff1a;跷跷板加载特效1.效果展示2.HTML完整代码 三&#xff1a;两个圆形加载特效1.效果展示2.HTML完整代码 四&#xff1a;半环加载特效1.效果展示2.HTML完整代码 五&#xff1a;音乐波动加载特效1.效…

Spring入园须知

序 聊 Spring&#xff0c;先从发展历史谈起&#xff0c;对整个生态有个大致认识&#xff0c;最后再看下 Spring 依赖的基础机制——IoC 和 AOP&#xff0c;就达到入门须知的目的了。毕竟 Spring 太大了&#xff0c;如果把话题扯得太宽泛太细节&#xff0c;很可能会迷失在 Spri…

作品截图_

openstack project create --domain default --description "姓氏" xingopenstack user create --domain default --password-prompt --description "名字" mingziopenstack role create --description "姓名首字母" xmzopenstack role add --pr…

使用API管理Dynadot域名,设置默认域名服务器ip信息

前言 Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮箱&…

聚合支付系统官方个人免签系统三方支付系统稳定安全高并发

系统采用fastadmin框架独立全新开发&#xff0c;安全稳定,系统支持代理、商户、码商等业务逻辑。 针对最近一-些JD&#xff0c;TB等业务定制&#xff0c;子账号业务逻辑API 非常详细&#xff0c;方便内置对接! 注意&#xff1a;系统没有配置文档很使用教程&#xff0c;不清楚…