admin 管理员组

文章数量: 1086019


2023年12月21日发(作者:excel数据分析怎么调出来)

c语言指针的用法c语言是一种高级编程语言,它可以直接操作内存中的数据。指针是c语言中一种特殊的变量,它可以存储另一个变量的地址,也就是内存中的位置。通过指针,我们可以间接地访问或修改内存中的数据,从而实现更高效和灵活的编程。本文将介绍c语言指针的基本概念、定义和初始化、运算和应用,以及一些常见的错误和注意事项。希望本文能够帮助你掌握c语言指针的用法,提高你的编程水平。指针的基本概念指针是一种数据类型,它可以存储一个地址值,也就是内存中某个位置的编号。每个变量在内存中都有一个唯一的地址,我们可以用指针来记录这个地址,然后通过这个地址来访问或修改变量的值。例如,假设有一个整型变量a,它的值为10,它在内存中的地址为1000(为了简化,我们假设地址是十进制数)。我们可以定义一个指向整型的指针p,并把a的地址赋给p,如下所示:int a = 10; // 定义一个整型变量a,赋值为10int *p; // 定义一个指向整型的指针pp = &a; // 把a的地址赋给p这里,&a表示取a的地址,也就是1000。p = &a表示把1000赋给p,也就是让p指向a。从图中可以看出,p和a是两个不同的变量,它们占用不同的内存空间。p存储了a的地址,也就是1000。我们可以通过p来间接地访问或修改a的值。指针的定义和初始化指针是一种数据类型,它需要在使用前进行定义和初始化。定义指针时,需要指定它所指向的变量的类型。初始化指针时,需要给它赋一个有效的地址值。定义指针的一般格式为:type *pointer_name;其中,type表示指针所指向的变量的类型,如int、char、float等;pointer_name表示指针的名称,如p、q、ptr等;*表示这是一个指针类型。例如:int *p; // 定义一个指向整型的指针pchar *q; // 定义一个指向字符型的指针qfloat *ptr; // 定义一个指向浮点型的指针ptr注意,在定义多个指针时,每个指针前都要加*号,不能省略。例如:int *p, *q; // 正确int* p, q; // 错误,q不是指针初始化指针时,需要给它赋一个有效的地址值。一般有两种方法:使用取地址符&获取一个已存在变量的地址,并赋给指针。使用malloc函数动态分配一块内存,并返回其首地址,并赋给指针。例如:

int a = 10; // 定义一个整型变量a,并赋值为10int *p = &a; // 定义一个指向整型的指针p,并把a的地址赋给它char *q = (char *)malloc(sizeof(char)); // 定义一个指向字符型的指针q,并动态分配一块字符型大小的内存,并把其首地址赋给它注意,使用malloc函数时,需要指定分配内存的大小,一般用sizeof函数来获取某种类型的大小。另外,malloc函数返回的是void

类型的指针,需要根据实际情况进行强制类型转换,例如(char )表示转换为指向字符型的指针。指针的运算指针是一种特殊的变量,它可以进行一些特殊的运算。常见的指针运算有以下几种:取值运算:使用*号可以获取指针所指向的变量的值,也就是指针所存储的地址处的数据。这个运算符也叫做解引用或间接访问运算符。赋值运算:可以给指针赋一个新的地址值,让它指向另一个变量或内存空间。也可以通过*号给指针所指向的变量赋一个新的值,从而修改内存中的数据。指针与整数的加减运算:可以给指针加上或减去一个整数,让它指向相邻的内存单元。这个运算符也叫做指针的移动或偏移。指针之间的减法运算:可以计算两个同类型的指针之间的距离,也就是它们所指向的内存单元之间相差多少个单位。这个运算符也叫做指针的比较或差值。下面我们用一些例子来说明这些运算。取值运算假设有以下代码:int a = 10; // 定义一个整型变量a,并赋值为10int *p = &a; // 定义一个指向整型的指针p,并把a的地址赋给它那么,我们可以用*p来获取p所指向的变量a的值,如下所示:printf("%dn", *p); // 输出10这里,*p表示取p所存储的地址处的数据,也就是a的值。注意,p和p是不同的概念,p表示p所指向的变量的值,而p表示p本身存储的地址值。例如:printf("%dn", p); // 输出1000printf("%dn", *p); // 输出10赋值运算假设有以下代码:int a = 10; // 定义一个整型变量a,并赋值为10int b = 20; // 定义一个整型变量b,并赋值为20int *p = &a; // 定义一个指向整型的指针p,并把a的地址赋给它那么,我们可以用以下两种方式来修改变量a和b的值:直接修改a和b本身:a = 30; // 把30赋给ab = 40; // 把40赋给b

通过*p间接修改a和b:*p = 30; // 把30赋给*p,也就是ap = &b; // 把b的地址赋给p,让p指向b*p = 40; // 把40赋给*p,也就是b这里,p = 30表示把30赋给p所存储的地址处的数据,也就是a。p = &b表示把b的地址赋给p,让p指向b。p = 40表示把40赋给p所存储的地址处的数据,也就是b。注意,通过指针修改变量的值时,要确保指针已经指向了一个有效的地址,否则会出现错误。例如:int *p; // 定义一个指向整型的指针p,但没有初始化*p = 10; // 错误,p没有指向任何有效的地址,不能通过*p赋值指针与整数的加减运算假设有以下代码:int a[5] = {1, 2, 3, 4, 5}; // 定义一个整型数组a,并初始化int *p = a; // 定义一个指向整型的指针p,并把a的首地址赋给它那么,我们可以用以下方式来访问或修改数组a中的元素:使用下标运算符[]:printf("%dn", a[0]); // 输出1printf("%dn", a[1]); // 输出2a[2] = 6; // 把6赋给a[2]a[3] = 7; // 把7赋给a[3]使用指针加减整数的运算:printf("%dn", *p); // 输出1,等价于a[0]printf("%dn", *(p + 1)); // 输出2,等价于a[1]*(p + 2) = 6; // 把6赋给*(p + 2),等价于a[2]*(p + 3) = 7; // 把7赋给*(p + 3),等价于a[3]这里,p表示取p所存储的地址处的数据,也就是a[0]。(p + 1)表示取p加上1后所存储的地址处的数据,也就是a[1]。类似地,(p + 2)表示a[2],(p + 3)表示a[3]。数组在内存中是连续存储的,每个元素占用相同大小的空间。指针加上或减去一个整数时,并不是简单地加上或减去这个数值,而是根据指针所指向的变量的类型来计算偏移量。例如,如果指针是指向整型的,那么每加上或减去一个整数,就相当于偏移一个整型大小的空间。如果指针是指向字符型的,那么每加上或减去一个整数,就相当于偏移一个字符型大小的空间。注意,在使用指针加减整数的运算时,要确保指针不越界,也就是不要访问或修改超出数组范围的内存单元。例如:int a[5] = {1, 2, 3, 4, 5}; // 定义一个整型数组a,并初始化int *p = a; // 定义一个指向整型的指针p,并把a的首地址赋给它printf("%dn", *(p + 5)); // 错误,越界访问,*(p + 5)相当于a[5],但是数组下标只能从0到4*(p + 6) = 8; // 错误,越界修改,*(p + 6)相当于a[6],但是数组下标只能从0到4指针之间的减法运算假设有以下代码:int a[5] = {1, 2, 3, 4, 5}; // 定义一个整型数组a,并初始化int *p = a; // 定义一个指向整型的指针p,并把a的首地址赋给它

int *q = a + 3; // 定义一个指向整型的指针q,并把a加上3后的地址赋给它,也就是a[3]的地址那么,我们可以用以下方式来计算两个指针之间的距离:printf("%dn", q - p); // 输出3,表示q和p之间相差3个整型大小的空间,也就是q指向的元素比p指向的元素大3个下标指针之间的减法运算,并不是简单地减去两个地址值,而是根据指针所指向的变量的类型来计算差值。例如,如果指针是指向整型的,那么两个指针之间的差值就相当于它们所指向的元素之间相差多少个下标。如果指针是指向字符型的,那么两个指针之间的差值就相当于它们所指向的字符之间相差多少个位置。注意,在使用指针之间的减法运算时,要确保两个指针是同类型的,也就是指向同一种类型的变量或数组。否则,这个运算没有意义,也可能出现错误。例如:int a[5] = {1, 2, 3, 4, 5}; // 定义一个整型数组a,并初始化char b[5] = {'a', 'b', 'c', 'd', 'e'}; // 定义一个字符型数组b,并初始化int *p = a; // 定义一个指向整型的指针p,并把a的首地址赋给它char *q = b; // 定义一个指向字符型的指针q,并把b的首地址赋给它printf("%dn", q - p); // 错误,不同类型的指针不能相减指针的应用指针是c语言中一种非常强大和灵活的工具,它可以用于实现很多高级和复杂的功能。常见的指针的应用有以下几种:实现动态内存分配:使用malloc、free等函数可以在程序运行过程中动态地分配和释放内存空间,从而实现更灵活和高效的内存管理。这些函数都需要使用指针来操作内存。实现函数参数的传递:使用指针作为函数参数可以实现传址调用,也就是让函数直接操作实参所在的内存空间,从而实现实参和形参之间的数据交换。这样可以提高函数的效率和灵活性。实现数据结构:使用指针可以构造一些复杂的数据结构,如链表、树、图等。这些数据结构可以存储和处理更多样化和动态化的数据,从而实现更强大和高级的功能。实现字符串操作:使用指针可以方便地操作字符串,如获取字符串长度、比较字符串大小、拷贝字符串内容、连接字符串等。这些操作都需要使用到字符串首地址或末尾地址等信息。实现多维数组:使用指针可以实现多维数组,如二维数组、三维数组等。这些数组可以存储和处理更复杂和丰富的数据,从而实现更多样化和高级化的功能。下面我们用一些例子来说明这些应用。实现动态内存分配假设我们要实现一个动态数组,也就是可以根据用户的输入来确定数组的大小,并且可以在运行过程中改变数组的大小。我们可以使用指针和malloc、free等函数来实现这个功能,如下所示:#include #include int main(){ int n; // 定义一个整型变量n,用来存储用户输入的数组大小 int *a; // 定义一个指向整型的指针a,用来指向动态分配的数组空间 int i; // 定义一个整型变量i,用来作为循环变量 printf("请输入数组的大小:n"); scanf("%d", &n); // 从键盘读取用户输入的数组大小,并赋给n a = (int *)malloc(n * sizeof(int)); // 根据n的值,动态分配n个整型大小的内存空间,并把其首地址赋给a if (a == NULL) // 判断是否分配成功,如果失败,打印错误信息并退出程序 { printf("内存分配失败!n");

exit(1); } printf("请输入数组的元素:n"); for (i = 0; i < n; i++) // 从键盘读取用户输入的数组元素,并赋给a所指向的内存空间 { scanf("%d", a + i); // 等价于scanf("%d", &a[i]); } printf("您输入的数组是:n"); for (i = 0; i < n; i++) // 打印a所指向的内存空间中的数据,也就是数组元素 { printf("%d ", *(a + i)); // 等价于printf("%d ", a[i]); } printf("n"); free(a); // 释放a所指向的内存空间,避免内存泄漏 return 0;}运行结果:请输入数组的大小:5请输入数组的元素:1 2 3 4 5您输入的数组是:1 2 3 4 5从上面的例子可以看出,使用指针和malloc、free等函数可以实现动态内存分配,从而实现更灵活和高效的内存管理。这样可以避免浪费或不足的情况,也可以根据需要改变内存空间的大小。注意,在使用动态内存分配时,要注意以下几点:使用malloc函数时,要指定分配内存的大小,一般用sizeof函数来获取某种类型的大小。另外,malloc函数返回的是void *类型的指针,需要根据实际情况进行强制类型转换。使用free函数时,要确保释放的是由malloc函数分配的内存空间,并且只释放一次。否则,可能会出现内存泄漏或重复释放的错误。在使用动态分配的内存空间时,要确保不越界访问或修改。否则,可能会出现数据丢失或覆盖的错误。在使用完动态分配的内存空间后,要及时释放它们。否则,可能会出现内存泄漏或不足的错误。实现函数参数的传递假设我们要实现一个交换两个整型变量值的函数。我们可以使用指针作为函数参数来实现这个功能,如下所示:#include void swap(int *p, int *q) // 定义一个交换两个整型变量值的函数,使用指针作为参数{ int temp; // 定义一个临时变量,用来存储交换过程中的中间值 temp = *p; // 把*p的值赋给temp,也就是把a的值赋给temp *p = *q; // 把*q的值赋给*p,也就是把b的值赋给a *q = temp; // 把temp的值赋给*q,也就是把a的值赋给b}int main(){ int a = 10; // 定义一个整型变量a,并赋值为10 int b = 20; // 定义一个整型变量b,并赋值为20

printf("交换前:n"); printf("a = %d, b = %dn", a, b); // 打印交换前的a和b的值 swap(&a, &b); // 调用swap函数,传入a和b的地址,也就是指向a和b的指针 printf("交换后:n"); printf("a = %d, b = %dn", a, b); // 打印交换后的a和b的值 return 0;}运行结果:交换前:a = 10, b = 20交换后:a = 20, b = 10从上面的例子可以看出,使用指针作为函数参数可以实现传址调用,也就是让函数直接操作实参所在的内存空间,从而实现实参和形参之间的数据交换。这样可以提高函数的效率和灵活性。注意,在使用指针作为函数参数时,要注意以下几点:在调用函数时,要传入实参的地址,也就是指向实参的指针。不能直接传入实参本身,否则无法实现数据交换。在定义函数时,要使用指针类型来声明形参。不能使用普通类型,否则无法接收地址值。在函数体内,要使用*号来访问或修改形参所指向的实参的值。不能直接使用形参本身,否则无法影响实参。实现数据结构假设我们要实现一个单链表,也就是一种由若干个节点组成的线性结构,每个节点包含一个数据域和一个指针域。我们可以使用指针来构造这种数据结构,如下所示:#include #include typedef struct node // 定义一个节点结构体类型{ int data; // 数据域,用来存储数据 struct node *next; // 指针域,用来指向下一个节点} node;node *create(int n) // 定义一个创建链表的函数,传入参数为链表长度{ node *head, *p, *q; // 定义三个指向节点的指针,分别表示头节点、当前节点和前一个节点 int i; // 定义一个循环变量 head = (node *)malloc(sizeof(node)); // 动态分配一个节点大小的内存空间,并把其首地址赋给头节点 head->data = 0; // 给头节点的数据域赋值为0,表示链表长度 head->next = NULL; // 给头节点的指针域赋值为NULL,表示链表为空 q = head; // 让q指向头节点 for (i = 0; i < n; i++) // 循环n次,创建n个节点,并连接到链表上 { p = (node *)malloc(sizeof(node)); // 动态分配一个节点大小的内存空间,并把其首地址赋给当前节点 printf("请输入第%d个节点的数据:n", i + 1); scanf("%d", &(p->data)); // 从键盘读取用户输入的数据,并赋给当前节点的数据域 p->next = NULL; // 给当前节点的指针域赋值为NULL,表示当前节点是最后一个节点 q->next = p; // 让前一个节点的指针域指向当前节点,从而连接两个节点

q = p; // 让q指向当前节点,为下一次循环做准备 } head->data = n; // 给头节点的数据域赋值为n,表示链表长度 return head; // 返回头节点,也就是链表的首地址}void print(node *head) // 定义一个打印链表的函数,传入参数为头节点{ node *p; // 定义一个指向节点的指针,用来遍历链表 int i; // 定义一个循环变量 p = head->next; // 让p指向头节点的下一个节点,也就是第一个有效节点 printf("链表长度为:%dn", head->data); // 打印头节点的数据域,也就是链表长度 printf("链表元素为:n"); for (i = 0; i < head->data; i++) // 循环head->data次,打印每个节点的数据域 { printf("%d ", p->data); // 打印当前节点的数据域 p = p->next; // 让p指向下一个节点,为下一次循环做准备 } printf("n");}int main(){ node *head; // 定义一个指向节点的指针,用来表示头节点 int n; // 定义一个整型变量,用来存储用户输入的链表长度 printf("请输入链表的长度:n"); scanf("%d", &n); // 从键盘读取用户输入的链表长度,并赋给n head = create(n); // 调用create函数,传入n,创建一个长度为n的链表,并返回其头节点 print(head); // 调用print函数,传入head,打印链表的信息 return 0;}运行结果:请输入链表的长度:3请输入第1个节点的数据:10请输入第2个节点的数据:20请输入第3个节点的数据:30链表长度为:3链表元素为:10 20 30

从上面的例子可以看出,使用指针可以构造一些复杂的数据结构,如链表、树、图等。这些数据结构可以存储和处理更多样化和动态化的数据,从而实现更强大和高级的功能。注意,在使用指针构造数据结构时,要注意以下几点:

在定义结构体类型时,要使用struct关键字,并给结构体类型起一个合适的名称。在定义结构体变量或指针时,要使用typedef关键字来简化类型名。在创建数据结构时,要使用malloc等函数动态分配内存空间,并把其首地址赋给相应的指针。在使用完数据结构后,要使用free等函数释放内存空间,避免内存泄漏。在操作数据结构时,要使用*号或->等运算符来访问或修改结构体成员。不能直接使用结构体指针本身,否则无法影响结构体成员。实现字符串操作假设我们要实现一些字符串操作,如获取字符串长度、比较字符串大小、拷贝字符串内容、连接字符串等。我们可以使用指针来方便地操作字符串,如下所示:#include int strlen(char *s) // 定义一个获取字符串长度的函数,传入参数为指向字符串的指针{ int len = 0; // 定义一个整型变量,用来存储字符串长度 while (*s != '0') // 循环遍历字符串,直到遇到结束符'0' { len++; // 长度加一 s++; // 指针后移一位 } return len; // 返回长度}int strcmp(char *s1, char *s2) // 定义一个比较字符串大小的函数,传入参数为两个指向字符串的指针{ while (*s1 == *s2 && *s1 != '0') // 循环遍历两个字符串,直到遇到不相等的字符或结束符'0' { s1++; // 指针后移一位 s2++; // 指针后移一位 } return *s1 - *s2; // 返回两个字符的差值,如果为正,表示s1大于s2;如果为负,表示s1小于s2;如果为零,表示s1等于s2}void strcpy(char *dest, char *src) // 定义一个拷贝字符串内容的函数,传入参数为两个指向字符串的指针,分别表示目标字符串和源字符串{ while (*src != '0') // 循环遍历源字符串,直到遇到结束符'0' { *dest = *src; // 把源字符串的当前字符赋给目标字符串的当前字符 dest++; // 指针后移一位 src++; // 指针后移一位 } *dest = '0'; // 在目标字符串末尾加上结束符'0'}void strcat(char *dest, char *src) // 定义一个连接字符串的函数,传入参数为两个指向字符串的指针,分别表示目标字符串和源字符串{ while (*dest != '0') // 循环遍历目标字符串,直到遇到结束符'0' { dest++; // 指针后移一位 } while (*src != '0') // 循环遍历源字符串,直到遇到结束符'0' { *dest = *src; // 把源字符串的当前字符赋给目标字符串的当前字符 dest++; // 指针后移一位 src++; // 指针后移一位

} *dest = '0'; // 在目标字符串末尾加上结束符'0'}int main(){ char s1[20] = "Hello"; // 定义一个字符数组,并初始化为"Hello" char s2[20] = "World"; // 定义一个字符数组,并初始化为"World" char s3[20]; // 定义一个字符数组,用来存储拷贝或连接后的结果 printf("s1的长度为:%dn", strlen(s1)); // 调用strlen函数,传入s1,打印s1的长度 printf("s2的长度为:%dn", strlen(s2)); // 调用strlen函数,传入s2,打印s2的长度 printf("比较s1和s2的大小:%dn", strcmp(s1, s2)); // 调用strcmp函数,传入s1和s2,打印比较结果 strcpy(s3, s1); // 调用strcpy函数,传入s3和s1,把s1的内容拷贝给s3 printf("拷贝后的s3是:%sn", s3); // 打印拷贝后的s3 strcat(s3, s2); // 调用strcat函数,传入s3和s2,把s2的内容连接到s3后面 printf("连接后的s3是:%sn", s3); // 打印连接后的s3 return 0;}运行结果:s1的长度为:5s2的长度为:5比较s1和s2的大小:-15拷贝后的s3是:Hello连接后的s3是:HelloWorld从上面的例子可以看出,使用指针可以方便地操作字符串,如获取字符串长度、比较字符串大小、拷贝字符串内容、连接字符串等。这些操作都需要使用到字符串首地址或末尾地址等信息。注意,在使用指针操作字符串时,要注意以下几点:在定义字符数组时,要预留足够的空间,以免越界访问或修改。另外,要在字符串末尾加上结束符'0',以标识字符串的结束。在遍历字符串时,要使用*号或[]等运算符来访问或修改字符。不能直接使用字符指针本身,否则无法影响字符。在比较或拷贝或连接字符串时,要注意不要越界访问或修改。否则,可能会出现数据丢失或覆盖的错误。实现多维数组假设我们要实现一个二维数组,也就是一个由若干个一维数组组成的数组。我们可以使用指针来实现这种数据结构,如下所示:#include #include int main(){ int m, n; // 定义两个整型变量,用来存储用户输入的二维数组的行数和列数 int **a; // 定义一个指向指针的指针,用来指向二维数组 int i, j; // 定义两个循环变量 printf("请输入二维数组的行数和列数:n"); scanf("%d%d", &m, &n); // 从键盘读取用户输入的行数和列数,并赋给m和n a = (int **)malloc(m * sizeof(int *)); // 根据m的值,动态分配m个指针大小的内存空间,并把其首地址赋给a

if (a == NULL) // 判断是否分配成功,如果失败,打印错误信息并退出程序 { printf("内存分配失败!n"); exit(1); } for (i = 0; i < m; i++) // 循环m次,为每一行动态分配n个整型大小的内存空间,并把其首地址赋给a[i] { a[i] = (int *)malloc(n * sizeof(int)); if (a[i] == NULL) // 判断是否分配成功,如果失败,打印错误信息并退出程序 { printf("内存分配失败!n"); exit(1); } } printf("请输入二维数组的元素:n"); for (i = 0; i < m; i++) // 循环m次,从键盘读取用户输入的每一行的元素,并赋给a[i][j] { for (j = 0; j < n; j++) // 循环n次,从键盘读取用户输入的每一列的元素,并赋给a[i][j] { scanf("%d", &a[i][j]); } } printf("您输入的二维数组是:n"); for (i = 0; i < m; i++) // 循环m次,打印每一行的元素 { for (j = 0; j < n; j++) // 循环n次,打印每一列的元素 { printf("%d ", a[i][j]); } printf("n"); } for (i = 0; i < m; i++) // 循环m次,释放每一行所占用的内存空间 { free(a[i]); } free(a); // 释放a所占用的内存空间 return 0;}运行结果:请输入二维数组的行数和列数:2 3请输入二维数组的元素:1 2 34 5 6您输入的二维数组是:1 2 3

4 5 6

从上面的例子可以看出,使用指针可以实现多维数组,如二维数组、三维数组等。这些数组可以存储和处理更复杂和丰富的数据,从而实现更多样化和高级化的功能。

注意,在使用指针实现多维数组时,要注意以下几点:在定义指针时,要根据数组的维数来确定指针的类型。例如,二维数组需要使用指向指针的指针,三维数组需要使用指向指向指针的指针,以此类推。在分配内存空间时,要根据数组的行数和列数来确定分配的大小。一般先分配行数个指针大小的空间,然后再为每一行分配列数个元素大小的空间。在访问或修改元素时,要使用*号或[]等运算符来访问或修改元素。不能直接使用指针本身,否则无法影响元素。在释放内存空间时,要按照分配的顺序逆序释放。一般先释放每一行所占用的空间,然后再释放整个数组所占用的空间。指针的常见错误和注意事项指针是c语言中一种非常重要和常用的工具,但也是一种非常容易出错和难以调试的工具。在使用指针时,要注意以下几种常见的错误和注意事项:指针未初始化:如果定义了一个指针变量,但没有给它赋一个有效的地址值,那么这个指针就是未初始化的。未初始化的指针可能会存储一个随机的地址值,如果试图通过这个指针访问或修改内存中的数据,就可能会出现错误或异常。例如:int *p; // 定义一个指向整型的指针p,但没有初始化printf("%dn", *p); // 错误,试图通过未初始化的指针访问内存中的数据*p = 10; // 错误,试图通过未初始化的指针修改内存中的数据为了避免这种错误,应该在定义指针时就给它赋一个有效的地址值,或者在使用前给它赋一个有效的地址值。指针越界:如果定义了一个指向数组或字符串等连续存储结构的指针,并且给它加上或减去了一个整数,那么这个指针可能会越过这个结构所占用的内存范围,也就是越界。如果试图通过这个越界的指针访问或修改内存中的数据,就可能会出现错误或异常。例如:int a[5] = {1, 2, 3, 4, 5}; // 定义一个整型数组a,并初始化int *p = a; // 定义一个指向整型的指针p,并把a的首地址赋给它printf("%dn", *(p + 5)); // 错误,越界访问,*(p + 5)相当于a[5],但是数组下标只能从0到4*(p + 6) = 8; // 错误,越界修改,*(p + 6)相当于a[6],但是数组下标只能从0到4为了避免这种错误,在使用指针加减整数的运算时,要注意不要超出数组或字符串的范围,也就是不要访问或修改不存在的元素或字符。指针类型不匹配:如果定义了一个指针变量,并给它赋了一个与它所指向的变量类型不匹配的地址值,那么这个指针就是类型不匹配的。如果试图通过这个类型不匹配的指针访问或修改内存中的数据,就可能会出现错误或异常。例如:int a = 10; // 定义一个整型变量a,并赋值为10char *p = &a; // 定义一个指向字符型的指针p,并把a的地址赋给它,类型不匹配printf("%cn", *p); // 错误,试图通过类型不匹配的指针访问内存中的数据*p = 'A'; // 错误,试图通过类型不匹配的指针修改内存中的数据为了避免这种错误,在定义指针时要注意指定正确的类型,或者在赋值时进行强制类型转换。指针重复释放:如果定义了一个指向动态分配的内存空间的指针,并且使用free等函数释放了这个内存空间,那么这个指针就是已释放的。如果再次使用free等函数释放这个已释放的指针,就可能会出现错误或异常。例如:int *p = (int *)malloc(sizeof(int)); // 定义一个指向整型的指针p,并动态分配一个整型大小的内存空间,并把其首地址赋给pfree(p); // 释放p所指向的内存空间free(p); // 错误,重复释放,p已经被释放过了

为了避免这种错误,在使用free等函数释放内存空间时,要注意只释放一次,并且在释放后把指针赋值为NULL,以避免悬空指针。指针未释放:如果定义了一个指向动态分配的内存空间的指针,并且在使用完这个内存空间后没有使用free等函数释放它,那么这个内存空间就会一直占用着,也就是内存泄漏。如果程序中有很多这样的内存泄漏,就可能会导致内存不足或程序崩溃。例如:int *p = (int *)malloc(sizeof(int)); // 定义一个指向整型的指针p,并动态分配一个整型大小的内存空间,并把其首地址赋给p// 省略其他代码// 没有使用free(p)来释放p所指向的内存空间,导致内存泄漏为了避免这种错误,在使用完动态分配的内存空间后,要及时使用free等函数释放它们。总结本文介绍了c语言指针的基本概念、定义和初始化、运算和应用,以及一些常见的错误和注意事项。通过本文,你应该能够掌握c语言指针的用法,提高你的编程水平。本文只是对c语言指针的一个简单介绍,还有很多细节和高级用法没有涉及到。如果你想深入学习c语言指针,建议你参考更多的资料和书籍,并多做一些实践和练习。祝你学习愉快!


本文标签: 指针 使用 指向 字符串 数组