C++理论与实战 第8章 灵活调用内存地址——指针
- 格式:ppt
- 大小:630.50 KB
- 文档页数:12
c语言指针通俗易懂讲解C语言是一门广泛使用的编程语言,而指针是C语言中的一个重要概念。
指针在C语言中具有非常重要的作用,它能够使我们更灵活地操作内存和数据。
但是,对于初学者来说,指针可能会是一个难以理解的概念。
因此,本文将以通俗易懂的方式解释C语言指针的概念和用法。
首先,我们来解释指针的基本概念。
在C语言中,指针是一个变量,其存储的值是内存地址。
你可以将指针看作是指向内存中某个特定位置的“指针”,通过这个指针,我们可以访问和修改该内存位置上的数据。
这个地址可以是任何数据类型,例如整数、字符、数组等。
那么,我们为什么要使用指针呢?使用指针有很多好处。
首先,通过指针,我们可以节省内存空间。
指针可以传递地址,而不是复制整个数据。
其次,指针能够使我们在函数之间传递数据更加高效。
当我们将指针作为参数传递给函数时,可以直接在函数内部修改传递进来的数据,而无需进行数据的复制。
最后,指针还可以用于动态分配内存,使我们能够更灵活地管理和操作内存。
接下来,我们来看一些指针的常见用法。
首先是指针的声明和初始化。
要声明一个指针变量,我们需要在变量前面加上“*”符号。
例如,int *ptr; 声明了一个指向整数的指针变量ptr。
要初始化指针,我们可以将其指向一个已存在的变量或者使用动态内存分配函数(例如malloc())进行初始化。
然后是指针的使用。
使用指针时,我们可以通过“*”运算符来访问指针所指向的值。
例如,*ptr将访问ptr指针指向的整数值。
我们还可以使用“&”运算符来获得变量的地址,例如,&num将返回变量num的地址。
另一个常见的指针用法是指针和数组的结合。
在C语言中,数组名称本质上是一个指向数组第一个元素的指针。
因此,我们可以使用指针来遍历数组中的元素,或者通过指针传递数组给函数。
我们还可以使用指针和结构体一起工作。
指针可以指向结构体,这使得我们能够动态创建结构体对象,并在需要时进行修改或访问。
c语言指针知识点实验总结C语言指针是一种重要的编程概念,而掌握指针的知识对于提高代码的效率和灵活性至关重要。
本文将对C语言指针的知识点进行实验总结,帮助读者更好地理解和应用指针。
1. 指针的定义和基本概念指针是一种变量,它存储着一个内存地址,这个地址对应着存储的数据。
通过指针,我们可以直接访问和操作内存中的数据,提高程序的效率和灵活性。
在C语言中,我们使用"*"来声明指针变量。
2. 指针的使用方法使用指针的第一步是声明指针变量,并且将它指向一个合法的内存地址。
为了确保指针的有效性,我们可以使用取地址符"&"获取变量的地址。
通过指针,我们可以访问存储在该地址上的数据,可以使用"*"来解引用指针,获取或修改指针指向的数据。
3. 指针和数组的关系数组名在C语言中可以被视为指向数组首元素的指针。
通过指针算术运算,我们可以便捷地访问数组中的不同元素,实现数组的遍历和操作。
4. 指针和函数的关系通过指针,我们可以将函数的地址作为参数进行传递。
这样的参数被称为函数指针,通过函数指针,我们可以灵活地在程序中调用不同的函数,实现更加通用和可复用的代码。
5. 指针的动态内存分配C语言提供了动态内存分配的机制,即通过指针在运行时申请内存空间。
使用动态内存分配可以避免静态内存不足的问题,同时也提高了内存的利用效率。
需要注意的是,在使用完毕后,我们必须手动释放被分配的内存。
6. 指针的常见问题和注意事项在使用指针的过程中,我们需要注意指针的有效性、指针的赋值和修改、指针的比较以及指针的空指针问题等。
对于指针操作的不当,可能导致程序的崩溃或者产生错误的结果。
通过实验总结,我们深入探讨了C语言指针的相关知识点。
掌握了指针的定义和基本概念,了解了指针的使用方法,学习了指针和数组、函数的关系,熟悉了指针的动态内存分配,同时也了解了指针的常见问题和注意事项。
通过对C语言指针的综合学习,读者将能够更加灵活地运用指针,提高程序的效率和可维护性。
c语言指针的指针用法详解在C语言中,指针是非常重要的一种数据类型。
而指针的指针是指指向指针变量的指针。
它在C语言中也是非常重要的一种数据类型,经常用于动态内存分配和函数传递参数等方面。
下面,我们来详细介绍一下指针的指针在C语言中的用法。
一、指针的基本概念在C语言中,指针是一个变量,用来表示另一个变量的内存地址。
指针变量可以存储任何数据类型的地址,包括整型、字符型、浮点型等。
使用指针可以实现动态内存分配、函数传递参数等功能。
二、指针的指针的概念指针的指针是指指向指针变量的指针。
它的定义方式如下:```int **p;```其中,p是一个指向指针的指针变量,它可以指向一个指针变量的地址。
三、指针的指针的用法指针的指针在C语言中有很多用途,下面列举几个比较常见的用法。
1.动态内存分配在C语言中,可以使用malloc函数动态分配内存,该函数返回的是一个指向分配内存的首地址的指针。
而在一些情况下,需要动态分配二维数组或者指针数组,这时就需要使用指针的指针了。
例如:```int **p;int i,j;p=(int **)malloc(sizeof(int*)*3);//分配3个指向int类型指针的指针变量for(i=0;i<3;i++){p[i]=(int*)malloc(sizeof(int)*4);//分配4个int类型的变量}for(i=0;i<3;i++){for(j=0;j<4;j++){p[i][j]=i*j;//为p[i][j]赋值}}```上述代码中,先使用malloc函数分配3个指向int类型指针的变量,然后再用循环分别为这3个变量分配4个int类型的变量。
最后,再使用嵌套循环为二维数组赋值。
2.函数传递参数在C语言中,函数可以通过指针传递参数。
指针的指针也可以用于函数传递参数,可以使函数返回多个值。
例如:```void fun(int **p){*p=(int*)malloc(sizeof(int)*4);//为指针p分配4个int类型的变量(*p)[0]=10;(*p)[1]=20;(*p)[2]=30;(*p)[3]=40;}int main(){int *p;fun(&p);//传递p的地址printf("%d %d %d %d\n",p[0],p[1],p[2],p[3]);free(p);//释放内存return 0;}```上述代码中,定义了一个指针类型的函数fun,在函数中通过指针的指针为指针p分配4个int类型的变量,并为这4个变量赋值。
c语言中的指针详解在C语言中,指针是一种特殊的变量类型,它存储了一个变量的内存地址。
通过指针,我们可以间接访问和修改内存中的数据,这对于一些需要动态分配内存的操作非常有用。
以下是关于C语言指针的一些详细解释:1. 定义指针:使用"*"符号来定义指针变量。
例如,int* ptr; 定义了一个指向整型变量的指针 ptr。
2. 取址操作符(&):取地址操作符(&)用于获取变量的内存地址。
例如,&a 返回变量 a 的地址。
3. 解引用操作符(*):解引用操作符(*)用于访问指针所指向的变量的值。
例如,*ptr 返回指针 ptr 所指向的整型变量的值。
4. 动态内存分配:可以使用相关的库函数(如malloc和calloc)在运行时动态分配内存。
分配的内存可以通过指针来访问和使用,并且在使用完后应该使用free函数将其释放。
5. 空指针:空指针是一个特殊的指针值,表示指针不指向任何有效的内存地址。
可以将指针初始化为NULL来表示空指针。
6. 指针和数组:指针和数组在C语言中有密切的关系。
可以通过指针来访问数组元素,并且可以使用指针进行指针算术运算来遍历数组。
7. 传递指针给函数:可以将指针作为函数参数传递,以便在函数内部修改实际参数的值。
这种传递方式可以避免拷贝大量的数据,提高程序的效率。
8. 指针和字符串:字符串在C语言中实际上是以字符数组的形式表示的。
可以使用指针来访问和操作字符串。
需要注意的是,指针在使用时需要小心,因为不正确的操作可能导致程序崩溃或产生不可预料的结果。
对于初学者来说,理解指针的概念和使用方法可能需要一些时间和练习。
指针简介 : 指针式保存变量地址的变量;-- 增加阅读难度 : 指针和 goto 语句会增加程序的理解难度, 容易出现错误;-- ANSI C : American National Standards Institute 美国国家标准学会, 即标准C;-- 通用指针类型 : ANSI C中使用 void* 作为通用指针类型, 即指向void的指针, void 是空类型, void* 是空类型指针, 可以指向任意类型的地址;1. void 与 void*(1) void 简介void 作用 :-- 限定参数: 函数没有返回值, 需要使用 void 声明, 否则默认返回 int 类型;-- 限定返回值 : 函数不接收参数, 使用 void 作为参数, 如果传入参数, 编译器就会报错;使用void注意点 :-- void不能表示变量 : void a, 这样定义是错误的;-- 默认返回值 : C 中, 如果没有标明返回值类型, 默认的返回值不是 void, 是 int 类型;-- void参数 : C 语言中参数是void, 传入参数不会出错, C++中传入参数会出错, 因此这里我们统一规定, 如果函数没有参数, 就定义为void;(2) void*简介void * 作用 :-- 通用数据类型 : void * 指针可以存放任意类型数据的地址, 任何数据类型的指针都可以赋值给 void * 通用类型指针;-- 任意类型 : 如果函数的参数和返回值可以是任意类型, 就可以使用 void * 作为函数的参数或者返回值;使用void* 注意点 :-- void * 与其它类型互相赋值 : int * 变量可以赋值给 void * 变量, 但是void * 变量如果赋值给 int * 变量需要强转为 int * 类型;--void * 不允许进行算数操作 : 标准C 中规定 void * 类型不允许进行加减乘除算数运算, 因为我们不知道这个类型的大小, GUN 中void * 等价于 char * ;2. C 语言程序内存分配(1) 内存分区状况栈区 (stack) :-- 分配, 释放方式 : 由编译器自动分配和释放;-- 存放内容 : 局部变量, 参数;-- 特点 : 具有后进先出特性, 适合用于保存回复现场;堆区 (heap) :-- 分配, 释放方式 : 由程序员手动分配(malloc) 和释放(free), 如果程序员没有释放, 那么程序退出的时候, 会自动释放;-- 存放内容 : 存放程序运行中动态分配内存的数据;-- 特点 : 大小不固定, 可能会动态的放大或缩小;堆区内存申请:-- 申请过程 : OS中有一个记录空闲内存地址的链表, 如果程序员申请内存, 就会找到空间大于申请内存大小的节点, 将该节点从空间内存链表中删除, 并分配该节点;-- 剩余内存处理 : 系统会将多余的部分重新放回空闲内存链表中;-- 首地址记录大小 : 分配内存的首地址存放该堆的大小, 这样释放内存的时候才能正确执行;全局区/静态区 (数据段 data segment /bss segment) :-- 分配, 释放方式 : 编译器分配内存, 程序退出时系统自动释放内存;-- 存放内容: 全局变量, 静态变量;-- 特点 : 全局变量和静态变量存储在一个区域, 初始化的两种变量和未初始化的存储在不同区域, 但是两个区域是相邻的;常量区:-- 分配, 释放方式: 退出程序由系统自动释放;-- 存放内容 : 常量;代码区 (text segment) :-- 分配, 释放方式 : 编译器分配内存, 程序退出时系统自动释放内存;-- 存放内容 : 存放程序的二进制代码, 和一些特殊常量;内存存放顺序 (由上到下): 栈区 -> 堆区 -> 全局区 -> 常量区 -> 代码区;(2)内存分配方式全局内存分配 :-- 生命周期: 编译时分配内存, 程序退出后释放内存, 与程序的生命周期相同;-- 存储内容 : 全局变量, 静态变量;栈内存分配 :-- 生命周期 : 函数执行时分配内存, 执行结束后释放内存;-- 特点 : 该分配运算由处理器处理, 效率高, 但是栈内存控件有限;堆内存分配 :-- 生命周期 : 调用 malloc()开始分配, 调用 free()释放内存, 完全由程序员控制;-- 谨慎使用 : 如果分配了没有释放, 会造成内存泄露, 如果频繁分配释放会出现内存碎片;(3) register变量使用场景 : 如果一个变量使用频率特别高, 可以将这个变量放在 CPU 的寄存器中;-- 修饰限制 : 只有局部变量和参数可以被声明为 register变量, 全局和静态的不可以;-- 数量限制 : CPU 寄存器很宝贵, 不能定义太多register变量;(4) extern 变量extern变量概念 : 声明外部变量, 外部变量就是在函数的外部定义的变量, 在本函数中使用;-- 作用域 : 从外部变量定义的位置开始, 知道本源码结束都可以使用, 但是只能在定义extern后面使用, 前面的代码不能使用;-- 存放位置 : 外部变量存放在全局区;extern变量作用 : 使用extern修饰外部变量, ①扩展外部变量在本文件中的作用域, ②将外部变量作用域从一个文件中扩展到工程中的其它文件;extern声明外部变量的情况 :-- 单个文件内声明 : 如果不定义在文件开头, 其作用范围只能是定义位置开始, 文件结束位置结束;-- 多个文件中声明 : 两个文件中用到一个外部变量, 只能定义一次, 编译和连接的时候, 如果没有这个外部变量, 系统会知道这个外部变量在别处定义, 将另一个文件中的外部变量扩展到本文件中;extern编译原则:-- 本文件中能找到: 编译器遇到 extern 的时候, 现在本文件中找外部变量的定义的位置, 如果找到, 就将作用域扩展到定义的位置直到文件结束;-- 本文件中找不到 : 如果本文件中找不到, 连接其它文件找外部变量定义, 如果找到, 将外部变量作用域扩展到本文件中;-- 外部文件找不到 : 报错;使用效果 : extern 使用的时候, 可以不带数据类型;-- 本文件 : int A = 0; 在第10行, extern A 在第一行, 那么A的作用域就扩展为从第一行到文件末尾;--多文件 : 在任意文件中定义了 int A = 0; 在本文件中声明 extern A, 那么从当前位置到文件末尾都可以使用该变量;(5) static变量与全局变量区别static 变量与全局变量相同点 : 全局变量是静态存储的, 存储的方式和位置基本相同;static 变量与全局变量不用点 : 全局变量的作用域是整个项目工程横跨过多个文件, 静态变量的作用域是当前文件, 其它文件中使用是无效的;变量存储位置 : 全局变量和静态变量存放在全局区/静态去, 局部变量存放在栈区(普通变量, 指针变量内容) 和堆区(指针变量指向的内容);变量静态化:-- 局部变量 : 局部变量加上 static , 相当于将局部变量的生命周期扩大到了整个文件, 作用域不改变;-- 全局变量 : 全局变量加上 static , 相当于将全局变量的作用域缩小到了单个文件, 生命周期是整个程序的周期;关于函数头文件的引申 :-- 内部函数 : 单个文件中使用的内部函数, 仅在那个特定文件中定义函数即可;-- 全局函数 : 如果要在整个工程中使用一个全局函数, 需要将这个函数定义在一个头文件中;static变量与普通变量区别 :-- static全局变量与全局变量区别 : static 全局变量只初始化一次, 防止在其它文件中使用;-- static局部变量与局部变量区别 : static 局部变量只初始化一次, 下一次依据上一次结果;static函数与普通函数区别 : static 函数在内存中只保留一份, 普通函数每调用一次, 就创建一个副本;(6) 堆和栈比较堆(heap)和栈(stack)区别:-- 申请方式 : stack 由系统自动分配, heap 由程序员进行分配;-- 申请响应 : 如果 stack 没有足够的剩余空间, 就会溢出; 堆内存从链表中找空闲内存;-- 内存限制 : stack 内存是连续的, 从高位向低位扩展, 而且很小, 只有几M, 是事先定好的, 在文件中配置; heap 是不连续的, 从低位向高位扩展, 系统是由链表控制空闲程序, 链表从低地址到高地址, 堆大小受虚拟内存限制, 一般32位机器有4G heap;-- 申请效率: stack 由系统分配, 效率高; heap 由程序员分配, 速度慢, 容易产生碎片;7) 各区分布情况.按照下图分布 : 由上到下顺序 : 栈区(stack) -> 堆区(heap) -> 全局区 -> 字符常量区 -> 代码区;验证分区状况 :-- 示例程序 :1./*************************************************************************2. > File Name: memory.c3. > Author: octopus4. > Mail: octopus_5. > Created Time: Mon 10 Mar 2014 08:34:12 PM CST6. ************************************************************************/7.8.#include<stdio.h>9.#include<stdlib.h>10.11.int global1 = 0, global2 = 0, global3 = 0;12.13.void function(void)14.{15.int local4 = 0, local5 = 0, local6 = 0;16.static int static4 = 0, static5 = 0, static6 = 0;17.int *p2 = (int*)malloc(sizeof(int));18.19. printf("子函数局部变量 : \n");20. printf("local4 : %p \n", &local4);21. printf("local5 : %p \n", &local5);22. printf("local6 : %p \n", &local6);24. printf("子函数指针变量 : \n");25. printf("p2 : %p \n", p2);26.27. printf("全局变量 : \n");28. printf("global1 : %p \n", &global1);29. printf("global2 : %p \n", &global2);30. printf("global3 : %p \n", &global3);31.32. printf("子函数静态变量 : \n");33. printf("static4 : %p \n", &static4);34. printf("static5 : %p \n", &static5);35. printf("static6 : %p \n", &static6);36.37. printf("子函数地址 : \n");38. printf("function : %p \n", function);39.}40.41.int main(int argc, char **argv)42.{43.int local1 = 0, local2 = 0, local3 = 0;44.static int static1 = 0, static2 = 0, static3 = 0;45.int *p1 = (int*)malloc(sizeof(int));46.const int const1 = 0;47.char *char_p = "char";48.49. printf("主函数局部变量 : \n");50. printf("local1 : %p \n", &local1);51. printf("local2 : %p \n", &local2);52. printf("local3 : %p \n", &local3);53. printf("const1 : %p \n", &const1);54.55. printf("主函数指针变量 : \n");56. printf("p1 : %p \n", p1);57.58. printf("全局变量 : \n");59. printf("global1 : %p \n", &global1);60. printf("global2 : %p \n", &global2);61. printf("global3 : %p \n", &global3);62.63. printf("主函数静态变量 : \n");64. printf("static1 : %p \n", &static1);65. printf("static2 : %p \n", &static2);66. printf("static3 : %p \n", &static3);68. printf("字符串常量 : \n");69. printf("char_p : %p \n", char_p);70.71. printf("主函数地址 : \n");72. printf("main : %p \n", main);73.74.75. printf("= = = = = = = = = = = = = = = \n");76.77. function();78.79.return 0;80.}-- 执行结果 :1.[root@ip28 pointer]# gcc memory.c2.[root@ip28 pointer]# ./a.out3.主函数局部变量 :4.local1 : 0x7fff75f5eedc5.local2 : 0x7fff75f5eed86.local3 : 0x7fff75f5eed47.const1 : 0x7fff75f5eed08.主函数指针变量 :9.p1 : 0x19bad01010.全局变量 :11.global1 : 0x600e1412.global2 : 0x600e1813.global3 : 0x600e1c14.主函数静态变量 :15.static1 : 0x600e3416.static2 : 0x600e3017.static3 : 0x600e2c18.字符串常量 :19.char_p : 0x4009f720.主函数地址 :21.main : 0x40065f22.= = = = = = = = = = = = = = =23.子函数局部变量 :24.local4 : 0x7fff75f5eea425.local5 : 0x7fff75f5eea026.local6 : 0x7fff75f5ee9c27.子函数指针变量 :28.p2 : 0x19bad03029.全局变量 :30.global1 : 0x600e1431.global2 : 0x600e1832.global3 : 0x600e1c33.子函数静态变量 :34.static4 : 0x600e2835.static5 : 0x600e2436.static6 : 0x600e2037.子函数地址 :38.function : 0x4005283. 指针与地址(1) & 与 * 操作取地址运算符 & : p = &c;-- 表达式解析 : 将 c 的地址赋值给变量 p, p 是指向 c 变量的指针;-- & 可以使用的情况 : 取地址操作只能用于内存中的对象, 如变量或数组, 栈内存堆内存都可以;-- & 不适用的情况 : 不能用于表达式, 常量, register类型变量;间接引用运算符 : * ;-- 声明指针 : int *p ; 该表达式的含义是*p 的结果是 int 类型, 声明变量 a, int a, 声明指针 *p , int *p;-- 获取指针指向的值: int a = *p ;(2) 指针定义解析声明指针和函数 : int *p, max(int a, int b), 声明指针变量语法与声明变量语法类似, 同理声明函数也一样;-- 原理 : *p 和 max()返回值类型都是 int 类型;指针指向 : 每个指针都必须指向某种特定类型;-- 例外: void *p 可以指向任何类型, 但是 p 不能进行取值运算, *p 是错误的, 因为不知道 p 指向的数据类型;(3) 指针运算及示例指针相关运算 : int x = 0; int *p = &x; 那么*p 就可以代表x;-- 算数运算 : x = x + 1; 等价于 *p = *p + 1 ; int y = x + 1; 等价于 int y = *p + 1;-- 自增运算 : 前提 : ++, * 运算顺序是自右向左; ++*p 和 (*p)++, p 指向的值自增1, 注意要加上括号, 否则会将地址自增;-- 指针赋值 : int *p, *q; int a = 0; p = &a; q = p; 最终结果 p 和 q 都指向了变量 a;示例程序 :1./*************************************************************************2. > File Name: pointer_address.c3. > Author: octopus4. > Mail: octopus_5. > Created Time: Mon 10 Mar 2014 09:52:01 PM CST6. ************************************************************************/7.8.#include<stdio.h>9.10.int main(int argc, char ** argv)11.{12.int *p, *q;13.int a = 10, b;14.15.//p指针指向a变量16. p = &a;17.18.//*p 可以代替 a 进行运算19. ++*p;20.21. b = *p + 5;22.23.//指针之间可以直接相互赋值24. q = p;25.26.//打印 p 和 q 指针指向的值27. printf("*p = %d \n", *p);28. printf("*q = %d \n", *q);29.30.31.return 0;32.}执行结果 :1.[root@ip28 pointer]# gcc pointer_address.c2.[root@ip28 pointer]# ./a.out3.*p = 114.*q = 114. 函数参数的传值调用和传址调用(1) 传值调用和传址调用传值调用 : 以传值的方式将参数传递给函数, 不能直接修改主函数中变量的值, 仅仅是将副本传递给了函数;传址调用 : 将变量的指针传递给函数, 当函数对指针进行操作的时候, 主函数中的值也进行了对应变化;交换函数示例1 :1./*************************************************************************2. > File Name: swap.c3. > Author: octopus4. > Mail: octopus_5. > Created Time: Mon 10 Mar 2014 11:07:18 PM CST6. ************************************************************************/7.8.#include<stdio.h>9.10.void swap_1(int a, int b)11.{12.int temp;13. temp = a;14. a = b;15. b = temp;16.17. printf("swap_1 传值函数 a = %d, b = %d \n", a, b);18.}19.20.void swap_2(int *a, int *b)21.{22.int temp;23. temp = *a;24. *a = *b;25. *b = temp;26.27. printf("swap_2 传址函数 a = %d, b = %d\n", *a, *b);28.}29.30.int main(int argc, char **argv)31.{32.int a = 10, b = 5;33.34. printf("初始值 : a = %d, b = %d \n\n", a, b);35.36. swap_1(a, b);37. printf("执行 swap_1 函数, a = %d, b = %d \n\n", a, b);38.39. swap_2(&a, &b);40. printf("执行 swap_2 函数, a = %d, b = %d \n", a, b);41.42.43.return 0;44.}执行结果 :1.[root@ip28 pointer]# gcc swap.c2.[root@ip28 pointer]# ./a.out3.初始值 : a = 10, b = 54.5.swap_1 传值函数 a = 5, b = 106.执行 swap_1 函数, a = 10, b = 57.8.swap_2 传址函数 a = 5, b = 109.执行 swap_2 函数, a = 5, b = 10示例解析:-- 传值调用 : swap_1 是传值调用, 传入的是 main 函数中的 a b 两个变量的副本, 因此函数执行完毕后, 主函数中的值是不变的;-- 传址调用 : swap_2 是传址调用, 传入的是 a , b 两个变量的地址 &a, &b, 当在swap_2 中进行修改的时候, 主函数中的 a,b变量也会发生改变;(2) 高级示例需求分析 : 调用getint()函数, 将输入的数字字符转为一个整形数据;getch 和 ungetch 函数 :-- 使用场景 : 当进行输入的时候, 不能确定是否已经输入足够的字符, 需要读取下一个字符, 进行判断, 如果多读取了一个字符, 就需要将这个字符退回去;-- 使用效果 : getch() 和 ungetch() 分别是预读下一个字符, 和将预读的字符退回去, 这样对于其它代码而言, 没有任何影响;注意的问题 : 出现问题, 暂时编译不通过, 找个C语言大神解决;代码 :1./*************************************************************************2. > File Name: getint.c3. > Author: octopus4. > Mail: octopus_5. > Created Time: Mon 10 Mar 2014 11:40:19 PM CST6. ************************************************************************/7.#include <stdio.h>8.#include <stdlib.h>9.#include <ctype.h>10.#define SIZE 511.12.int getint(int *p)13.{14.//sign 是用来控制数字的正负15.int c, sign;16.17.//跳过空白字符, 如果是空白字符, 就会进行下一次循环, 直到不是空白字符为止18.19.while(isspace(c = getc(stdin)));20.21.//如果输入的字符不是数字, 就将预读的数据退回到标准输入流中22.if(!isdigit(c) && c != EOF && c != '+' && c != '-')23. {24. ungetc(c, stdin);25.return 0;26. }27.28./*29. * 如果预读的是减号, 那么sign 标识就是 -1,30. * 如果预读的是加号, 那么sign 标识就是 1;31. */32. sign = (c == '-') ? -1 : 1;33.//如果 c 是加号或者减号, 再预读一个字符&34.if(c == '+' || c == '-')35. c = getc(stdin);36.37.for(*p = 0; isdigit(c); c = getc(stdin))38. *p = 10 * *p + (c - '0');39.40. *p *= sign;41.42.if(c != EOF)43. ungetc(c, stdin);44.45.return c;46.47.}48.49.int main(int argc, char **argv)50.{51.int n, array[SIZE], i;52.for(n = 0; n < SIZE && getint(&array[n]) != EOF; n++);53.54.for(i = 0; i < SIZE; i++)55. {56. printf("array[%d] = %d \n", i, array[i]);57. }58.return 0;59.}执行结果 :1.octopus@octopus-Vostro-270s:~/code/c/pointer$ ./a.out2.1233.123 434.674 15.array[0] = 1236.array[1] = 1237.array[2] = 438.array[3] = 6749.array[4] = 15. 指针和数组指针数组比较 :-- 可互相替代 : 数组下标执行的操作都可以使用指针替代;-- 效率比较 : 使用指针操作效率比数组要高;指针与数组初始化 :-- 声明数组 : int a[10]; 定义一个长度为10 的int数组;-- 声明指针 : int *p; 定义一个指针, 该指针指向整型;-- 相互赋值 : p = &a[0], 将数组第一个元素的地址赋值给指针变量;-- 使用指针获取数组对象 : *p 等价于 a[0], *(p + 1) 等价于 a[1], *(p + i)等价于 a[i];-- 注意地址的运算 : p + i , 在地址运算上, 每次增加 sizeof(int) * i 个字节;将数组赋值给指针的途径 :-- 将数组第一个元素地址赋值给指针变量 : p = &a[0];-- 将数组地址赋值给指针变量 : p = a;指针和数组访问方式互换 : 前提 int *p, a[10]; p = a;-- 数组计算方式 : 计算a[i]的时候, 先将数组转化为 *(a + i)指针, 然后计算该指针值;-- 取值等价 : a[i] 等价于 *(p + i);-- 地址等价 : &a[i] 与 a + i 是等价的;-- 指针下标访问: p[i] 等价于 *(p + i);-- 结论 : 通过数组和下标实现的操作都可以使用指针和偏移量进行等价替换;指针和数组的不同点 :-- 指针是变量 : int *p, a[10]; p = a 和 p++ 没有错误;-- 数组名不是变量 : int *p, a[10]; a = p 和 a++ 会报错;数组参数 :-- 形参指针 : 将数组传作为参数传递给函数的时候, 传递的是数组的首地址, 传递地址, 形参是指针;数组参数示例 :-- 函数参数是数组 : 函数传入一个字符串数组参数, 返回这个字符串长度;1./*************************************************************************2. > File Name: array_param.c3. > Author: octopus4. > Mail: octopus_5. > Created Time: Sat 15 Mar 2014 12:46:57 AM CST6. ************************************************************************/7.8.#include<stdio.h>9.10.//计算字符串长度11.int strlen(char *s)12.{13.int n;14.for(n = 0; *s != '\0'; s++)15. n++;16.return n;17.}18.19.int main(int argc, char** argv)20.{21. printf("strlen(djdhaj) = %d \n", strlen("djdhaj"));22. printf("strlen(12) = %d \n", strlen("12"));23. printf("strlen(dfe) = %d \n", strlen("dfe"));24.}--执行结果 : warning: conflicting types for built-in function ‘strlen’, 原因是C语言中已经有了strlen 函数了, 如果改一个函数名, 就不会有这个警告了;[plain]view plaincopy1.[root@ip28 pointer]# gcc array_param.c2.array_param.c:12: warning: conflicting types for built-in function ‘strlen’3.[root@ip28 pointer]# ./a.out4.strlen(djdhaj) = 65.strlen(12) = 26.strlen(dfe) = 3数组和指针参数 : 将数组名传给参数, 函数根据情况判断是作为数组还是作为指针;-- 实参 : 指针偏移量和数组下标都可以作为数组或指针函数形参, 如数组情况fun(&array[2]) 或者指针情况fun(p + 2);-- 形参 : 函数的形参可以声明为 fun(int array[]), 或者 fun(int *array), 如果传入的是数组的第二个元素的地址, 可以使用array[-2]来获数组取第一个元素;数组指针参数示例 :1./*************************************************************************2. > File Name: param_array_pointer.c3. > Author: octopus4. > Mail: octopus_5. > Created Time: Sat 15 Mar 2014 01:28:33 AM CST6. ************************************************************************/7.8.#include<stdio.h>9.10.//使用指针做形参取指针的前两位和当前位11.void fun_p(int *p)12.{13. printf("*(p - 2) = %d \n", *(p - 2));14. printf("*p = %d \n", *p);15.}16.17.//使用数组做形参取数组的第-2个元素和第0个元素18.void fun_a(int p[])19.{20. printf("p[-2] = %d \n", p[-2]);21. printf("p[0] = %d \n", p[0]);22.}23.24.int main(int argc, char **argv)25.{26.int array[] = {1,2,3,4,5};27.//向指针参数函数中传入指针28. printf("fun_p(array + 2) : \n");29. fun_p(array + 2);30.31.//向数组参数函数中传入数组元素地址32. printf("fun_a(&array[2]) : \n");33. fun_a(&array[2]);34.35.//向指针参数函数中传入数组元素地址36. printf("fun_p(&array[2]) : \n");37. fun_p(&array[2]);38.39.//向数组参数函数中传入指针40. printf("fun_a(array + 2) : \n");41. fun_a(array + 2);42.return 0;43.}执行效果 :1.[root@ip28 pointer]# gcc param_array_pointer.c2.[root@ip28 pointer]# ./a.out3.fun_p(array + 2) :4.*(p - 2) = 15.*p = 36.fun_a(&array[2]) :7.p[-2] = 18.p[0] = 39.fun_p(&array[2]) :10.*(p - 2) = 111.*p = 312.fun_a(array + 2) :13.p[-2] = 114.p[0] = 3。
c语言中指针的用法在C语言中,指针是一种非常重要的概念,它提供了一种间接访问内存地址的方式。
指针可以用于多种用途,如动态内存分配、参数传递、数组操作等。
首先,指针的创建需要声明变量的类型,并在变量名前加上星号(*)。
例如,int *ptr; 就创建了一个名为ptr的指向整型数据的指针。
指针的一种常见用法是动态内存分配。
通过使用malloc或calloc函数,可以在程序运行时分配内存。
例如,int *ptr = (int*) malloc(sizeof(int)); 就创建了一个指向整型数据的指针,并分配了一个整型变量所需的内存空间。
这种方式可以在需要时动态地分配内存,提高程序的灵活性。
指针还可以用于参数传递。
在函数调用时,可以通过指针将一个变量的地址传递给函数,从而可以在函数内部修改原始变量的值。
这种方式称为通过指针进行函数调用。
例如,void changeValue(int *ptr) { *ptr = 10; } 就是一个通过指针修改变量值的函数。
在函数内部,使用解引用操作符(*)来获取指针指向的变量,并对其进行修改。
另外,指针也可以用于数组操作。
在C语言中,数组名本身就是一个指向数组首元素的指针。
通过使用指针算术运算,可以遍历数组中的元素。
例如,int arr[5] = {1, 2, 3, 4, 5}; int *ptr = arr; 就将数组arr的首地址赋给了指针ptr。
然后,可以使用指针进行遍历,如*ptr,*(ptr+1),等等。
指针还可以用于实现数据结构,如链表、树等。
通过指针的相互连接,可以灵活地操作数据结构中的元素。
需要注意的是,指针的使用需要谨慎,因为指针操作容易引发一些错误,如空指针引用、指针越界等。
在使用指针时,应该保证指针指向有效的内存地址,并且在不再使用指针之后,及时释放相关的内存空间。
总而言之,指针是C语言中非常重要的概念,它提供了一种灵活的方式来操作内存地址。
通过正确地使用指针,可以有效地提高程序的效率和灵活性。
C语言中的指针和内存操作理解指针的重要性和应用场景C语言作为一门强大的编程语言,其指针和内存操作是其核心特性之一。
对于初学者来说,理解指针的重要性和应用场景是至关重要的。
本文将详细讨论C语言中指针的概念、指针的重要性和常见的应用场景。
一、指针的概念在C语言中,指针是一个存储变量地址的变量。
它可以指向另一个变量的内存地址,并允许直接访问该地址上的数据。
指针变量使用“*”符号声明,在使用指针变量时需要使用“&”符号获取变量的地址。
指针的概念可以帮助我们更好地理解内存操作和数据传递。
二、指针的重要性1. 内存管理:指针在C语言中起到了重要的内存管理作用。
通过指针,我们可以直接操作内存地址,实现动态内存分配和释放。
这在一些需要处理大量数据或者动态分配内存的程序中尤为重要。
2. 数据传递:指针可以作为函数参数进行传递,可以直接修改原始数据,而不是进行数据的复制。
这样可以提高程序的效率,尤其是处理大型数据结构时。
3. 数据结构:指针在数据结构中扮演了重要角色。
通过指针,我们可以轻松地访问和操作链表、树等高级数据结构,并且可以动态地创建、插入和删除节点。
三、指针的应用场景1. 动态内存分配:使用malloc()或calloc()函数动态分配内存空间,并通过指针进行访问和操作。
这在需要处理未知数据量或者需要频繁分配释放内存的情况下非常有用。
2. 字符串操作:C语言中的字符串实际上是以字符数组的形式存在的,通过指针可以实现对字符串的各种操作,如拷贝、连接、比较等。
3. 函数指针:函数指针是指向函数的指针变量,可以用于实现回调函数、动态函数调用等高级功能。
4. 数据结构:指针在数据结构中起到了关键作用,如链表、树等复杂数据结构的创建、遍历和操作都离不开指针。
总结通过本文的讨论,我们可以清楚地了解到C语言中指针的重要性和应用场景。
指针的概念不仅帮助我们更好地理解内存操作和数据传递,还可以提高程序的效率和灵活性。
c语言调用指针在C语言中,指针是一个非常重要的概念。
通过使用指针,我们可以实现更灵活的内存管理和数据访问方式。
在本文中,我将介绍C语言中调用指针的一些最佳实践。
首先,让我们回顾一下什么是指针。
指针是一个变量,其值是内存地址。
通过指针,我们可以直接访问内存中的数据,而不需要通过变量名来访问。
这使得我们可以在程序中动态地分配和释放内存,以及传递大型数据结构的副本。
在C语言中,调用指针的最佳实践之一是确保指针变量初始化为NULL。
这是因为未初始化的指针变量可能包含一个随机的内存地址,这可能导致未定义的行为。
通过将指针变量初始化为NULL,我们可以避免这种情况,并在使用指针之前进行有效的检查。
另一个最佳实践是在使用指针之前检查其是否为NULL。
这是因为在某些情况下,指针可能指向一个不存在的内存地址。
如果我们尝试访问一个NULL 指针,将会导致程序崩溃。
因此,在使用指针之前,我们应该始终检查其是否为NULL,并采取适当的措施来处理这种情况。
在调用指针时,我们还应该注意指针的生命周期。
如果我们在函数内部创建一个指针,并将其传递给其他函数,我们需要确保在使用指针之前,其指向的内存仍然有效。
否则,我们可能会访问到无效的内存,导致程序崩溃或产生不正确的结果。
另一个重要的注意事项是,我们应该避免在函数之外返回局部变量的指针。
这是因为局部变量在函数返回后将被销毁,这意味着返回的指针将指向无效的内存。
如果我们需要在函数之外访问局部变量,可以通过动态分配内存来解决这个问题,并返回指向分配内存的指针。
在使用指针时,我们还应该注意内存泄漏的问题。
如果我们在程序中动态地分配了内存,但在使用完毕后没有及时释放,将会导致内存泄漏。
为了避免这种情况,我们应该在不再需要指针指向的内存时,使用free函数手动释放内存。
最后,一个好的实践是使用指针来传递大型数据结构的副本。
如果我们直接传递大型数据结构的副本,将会导致内存开销过大和性能下降。