c的详细学习(7)指针学习(一)
指针是c语言的一个重要概念,指针类型是c语言最有特色的数据类型:
*利用指针编写的程序可使调用函数共享变量或数据结构,实现双向数据通信;
*可以实现内存空间的动态存储分配;可以提高程序的编译效率和执行速度。
(1)指针的基本概念及指针变量的定义
1.基本概念
计算机的内存是以字节为单位的连续的存储空间,每个字节都有一个编号,这个编号称为地址。由于内存的存储空间是连续的,因此编号地址也是连续的。
变量名与内存中的一个地址相对应。
直接按变量的地址存取变量值的方式称为直接存取方式。
先通过一个变量a得到另一个变量b的地址,然后再进行存取变量值的方式得到b的值的方式称为间接存取方式。
2.定义方法
指针变量定义的一般形式
类型说明符 *标识符
*指针变量定义形式中的星号*不是变量名的一部分,它的作用是用来说明该变量是指针变量。
*如果一个表达式的值是指针类型的,即是内存地址,则称这个表达式是指针表达式。数组名代表数组的地址,是地址常量,也是指针表达式。
(2)指针运算
1.赋值运算
功能:将指针表达式的值赋给指针变量,即用指针表达式的值取代指针变量原来存储的地址值。
赋值运算符右侧的指针表达式指向的数据类型和左侧指针变量所指向的数据类型必须相同。
2.取地址运算
&标识符
功能:执行该表达式后返回"&"符后面名为"标识符"的变量(或数组元素)的地址值。
标识符只能是一个除register类型之外的变量或者数组元素。
int *e;int d;e=&d;
scanf("%d",e)等于scanf("%d",&d);
3.取内容运算
*指针表达式
"*指针表达式"的功能与"*"号后面"指针表达式"所指向的变量和数组元素等价。
取内容运算符"*"是单目运算符,也称为指针运算符或者间接访问运算符。
#include<stdio.h> int main(){
int x,*p;
x=;
p=&x;
printf("%d %p\n",x,p);//输出x的值和p的值(也就是x的地址)
x=;
//*p=x;这个语句可有可无
printf("%d %d",x,*p);
return ;
}
4.指针表达式与整数相加相减运算
p+n或p-n
表达式p+n的值=p的值+p所指向的类型长度*n;
表达式p-n的值=p的值-p所指向的类型长度*n;
只有p+n和p-n都指向连续存放的同类型数据区域,例如数组,指针加、减整数才具有实际意义。
5.自增自减运算
++p;p++;--p;p--;
都是使p指向下一个数据。
6.同类指针相减运算
得到两个同类型指针之间数据元素的个数
7.关系运算
比较指针变量所存的地址值的大小关系
#include<stdio.h> int main(){
int s=,a[],*p,*q;
for(p=a+,q=a;p>=q;p--){
scanf("%d",p);
}
q=a+;
p=a;
while(p<q){
s+=*p;
++p;
}
printf("%d\n",s);
return ;
}
8.强制类型转换运算
(类型说明符*)指针表达式
功能:将"指针表达式"的值转换成"类型说明符"类型的指针
float q,*i=&q;
int *p;
p=(int *)i+;//是将i的值转换成与p同类型的指针。如果i的值是无符号十进制数,
//则(int *)i+1指在整数基础上加上一个float型长度值
9.空指针
在没有对指针变量赋值之前,指针变量存储的地址值是不确定的,它存储的地址值可能是操作系统程序在内存中占据的地址空间中的一个地址,也可能是某一常驻内存的系统应用程序所占据的一个地址,还可能是内存中还没有分配使用的一块空间中的地址。因此,没有对指针变量赋初值而直接使用指针变量p进行scanf("%s",p);和*p表达式;形式的赋值运算可能会产生不可预料的后果,甚至会导致系统无法正常运行。
为了避免上述问题,通常会给指针变量付一个初值0.并把它成为空指针变量。
p='\0';p=0;和p=NULL;三个语句等价。'\0'的ASCII码值为0;NULL是在studio.h文件中定义的常数,其值为0。
(3)指针变量与一维数组
1.指针变量与一维数组之间的联系与区别
相同点:指针变量与一维数组不仅都可以用来处理内存中连续存放的一系列数据,而且采用统一的地址计算方法访问内存。因此任何使用下标变量完成的操作都可以使用指针变量来实现。数组名代表数组的首地址,其值为数组第一个元素的地址,一个一维数组名就是一个指向该数组第一个元素的指针。
不同点:指针变量是地址变量,可以改变其本身的值;而除了作为形参的数组名外,其它数组名是地址常量,地址值不能改变,不可以给除了作为形参的数组名之外的其他数组名赋值;用数组存取内存中的数据是通过其每个元素来实现的,而用指针变量存取内存中的数据是通过连续地改变指针的指向来实现的。
若有定义int a[5],*p=a,i;则有以下等价形式:
p[i]、*(p+i)、*(a+i)、a[i]四个表达式等价,都表示数组元素a[i];
&p[i]、p+i、a+i与&a[i]四个表达式等价,都表示数组元素a[i]的地址。
#include<stdio.h> int main(){
char s1[],s2[],*p1=s1,*p2=s2;
scanf("%s,",s1);
for(;*p1!='\0';p1++,p2++){
*p2=*p1;
}
printf("%s\0",s2);
return ;
}
上述代码会出现这种问题:
原因应该是s2的数组的结尾没有默认为'\0',导致后续的继续输出的乱码。
解决方法:
1).先将s2数组所有元素初始为'\0'。
#include<stdio.h> int main(){
char s1[],s2[],*p1=s1,*p2=s2;
for(int k=;k<;k++){
s2[k]='\0';
}
scanf("%s,",s1);
for(;*p1!='\0';p1++,p2++){
*p2=*p1;
}
printf("%s\0",s2);
return ;
}
2).将s2与s1相同的位置加上'\0'。
#include<stdio.h> int main(){
char s1[],s2[],*p1=s1,*p2=s2;
scanf("%s,",s1);
for(;*p1!='\0';p1++,p2++){
*p2=*p1;
}
*p2='\0';
printf("%s\0",s2);
return ;
}
gets(s)函数与scanf("%s:",&s)/* scanf("%s",s) */相似,但不完全相同,使用scanf("%s",&s);函数输入字符串时存在一个问题,就是如果输入了空格会认为字符串结束,空格后的字符将作为下一个输入项处理,但gets()函数将接收输入的整个字符串直到遇到换行为止。
2.字符串指针与字符串
下面介绍指针的方法定义和引用字符串
有两种方法将一个字符型指针变量指向一个字符串:
1)用赋初值的方式:
char *p="C Language";它是将存放字符串常量的存储区(或称无名数组)的首地址赋给指针变量p,使p指向了字符串中第一个字符c所在的存储单元,并将字符串的字符依次存入首地址开始的连续的存储单元中,系统在最后一个字符e的后面加上'\0'.
2)用赋值运算的方式
char *p;p="C Language";这与赋初值的结果完全相同。
可以改变指针变量p中的地址而使p指向另外的字符串,另外的字符串的长度不受限制,一旦p指向另外的字符串,并且没有另外的指针指向"C language",则此字符串将失踪,再也无法找到。
无论用赋初值还是用赋值运算的方式,利用字符型指针变量指向字符串常量,系统都是把字符串常量存储在只读存储区,不允许对字符串进行修改。
注意:在数组赋初值时,语句char s[11]="C language";不能等价于程序段char s[11];s[]="C Language";即数组可以在定义时整体赋初值。不可以利用赋值语句对数组整体赋值。
用指针变量打印一个字符串:
#include<stdio.h> int main(){
char *p="C language";
char *q="I am a Student!";
for(;*p!='\0';){
putchar(*++p);
}
printf("\n");
int i=;
while(q[i]){
printf("%c",p[i++]);
}
return ;
}
求输入的字符串的长度:
#include<stdio.h> int main(){
char s[],*p=s;
printf("Please input a string:\n");
gets(s);
while(*++p);
printf("Length of the string is %d\n",p-s);
return ;
}
用字符之指针指向格式字符串:
#include<stdio.h> int main(){
char *p,a='A',b='B';
p="A的ASCII码为%d,B的ASCII码为%d。\n";
printf(p,a,b);
return ;
}
(4)指针与函数
1.指针作为函数参数
函数可以有指针类型的参数。定义函数的指针类型参数与定义指针类型变量的方法类似。
#include<stdio.h>
//函数dis1和dis2达到相同的效果,即传入一个指向字符串的指针,输出这个字符串
void dis1(char *a){
printf("%s\n",a);
}
void dis2(char *a){
while(*a){
printf("%c",*a++);
}
printf("\n");
}
//dispaly1函数和display2函数具有相同的效果,利用字符数组名作为形参,这里字符数组名可看做字符型指针变量。
void display1(char *t){
char s[]="display1定义的字符串";
printf("%s\n",t);
t=s;
printf("%s\n",t);
}
void display2(char t[]){
char s[]="display2定义的字符串";
printf("%s\n",t);
t=s;
printf("%s\n",t);
}
int main(){
//1.用两种方法输出一个字符串
char *p="I am MenAngel";
dis1(p);
dis2(p); //2.用字符指针作形参和字符数组名作为形参
char *q="主函数中定义的字符串!";
display1(q);
printf("\n");
printf("%s\n",q);
printf("\n");
display2(q);
return ;
}
由本例的执行结果可以看出,在display函数中的t与主函数中的p都是指针变量,分别存在内存的不同区域,变量p与t存储的信息可以不同,变量p(实参)存储的值传递给变量t(形参)之后,变量t存储的值得变化对变量p存储的值没有影响。
指针作为函数的参数,在调用时传递的时地址,传递地址的方式有四种:
1)形参和实参都是数组名;
2)形参和实参都用指针变量;
3)形参用数组名,实参用指针变量;
4)形参用指针变量,实参用数组名。
由于c编译系统将形参数组名当作指针变量来处理,因此在子函数体内可以将形参数组名作为指针变量使用,可以在子函数体内给形参数组名赋值。
实例:将字符串中的字符按照逆序输出:
#include<stdio.h>
#include<string.h>
#define M 80
void rever(char *q){
int length,k;
char *p;
length=strlen(q);
printf("The length of this string is %d。\n",length);
for(p=q+length-;q<p;q++,p--){
k=*q;
*q=*p;
*p=k;
}
}
int main(){
char str[M];
printf("Enter a string which is less than 80 characters:\n");
scanf("%s",str);
rever(str);
printf("revers string is :%s\n",str);
return ;
}
这里传入的是形参,但是通过形参改变了原来实参的指向的数据内容,与前一个例子不同的是形参可能会改变实参指向的内容,但形参的指向改变,实参的指向并不会同时发生改变。
2.返回指针的函数
返回指针函数定义的一般形式:
类型说明符 *函数名(){}
实例:
1)将给定字符串的第一个字母变成大写字母,其他字母变成小写字母
#include<stdio.h>
#include<string.h> char *str(char *s){
int i=;
if(*s>='a'&&*s<'z'){
*s=*s-;
}
while(*(s+i)!='\0'){
if(*(s+i)>='A'&&*(s+i)<='Z'){
*(s+i)=*(s+i)+;
}
i++;
}
return s;
}
int main(){
char str1[];
printf("original string is:\n");
gets(str1);
printf("%s\n",str1);
printf("correct string is:%s\n",str(str1));
return ;
}
2)在给定的字符串中寻找一个特定的字符x,若找到x,则返回x在s中第一次出现的地址,并把s中该字符和该字符之前的字符按照逆序输出:
#include<stdio.h>
#include<string.h> int main(){
char *str(char *,char);
char s[];
char *p,x;
gets(s);
x=getchar();
p=str(s,x);
if(*p){
printf("%c",*p);
while(p-s){
p--;
printf("%c",*p);
}
}else{
printf("char %c not found",x);
}
return ;
} char *str(char *s,char x){
int c=;
while(x!=s[c]&&s[c]!='\0'){
c++;
}
return (&s[c]);
}
3.函数的指针和指向函数的指针变量
函数的名字有值,其值等于该函数存储的首地址,即等于该函数的入口地址,在编译时分配给函数的这个入口地址就称为函数的指针。
指向函数的指针变量定义的一般形式:
类型说明符(*标识符)(形式参数表);例如 int (*p)(int a,int b);其中p即为指向函数的指针变量。
*定义指向函数的指针变量时,形式参数表只写出各个形式参数的类型即可,也可以与函数原型的写法相同,还可以将形式参数表省略不写。
*指向函数的指针变量允许的操作:
1)将函数名或者指向函数的指针变量的值赋给指向同一类型函数的指针变量;
2)函数名或者指向函数的指针变量作为函数的参数。
3)可以利用指向函数的指针变量调用函数,调用函数是:
(*变量名)(实参列表)
其调用结果是使程序的执行流程转移到指针变量所指向函数的函数体。函数的地址值赋给指向函数的指针变量以后,指针变量就指向了该函数。
实例:
1)求多项式x^4+x-1当x=1.5,2.5,3.5,4.5时的值。
#include<stdio.h>
#include<math.h> double f(double z){
double d;
d=pow(z,4.0)+z-;
return d;
} int main(){
int i;
double r,x,f(double),(*y)(double);
y=f;
for(i=;i<=;i++){
x=i+0.5;
r=(*y)(x);//可用r=y(x)代替r=(*y)(x);
printf("x=%f,y=%f\n",i+0.5,r);
}
return ;
}
2)当x=15度,30度,45度时,求函数y=2sinx-cos2x的值:
#include<stdio.h>
#include<math.h> double calculate(double (*p1)(double),double (*p2)(double),double q){
return (*(*p1)(q)-(*p2)(*q));
} int main(){
double x;
x=3.141592653/;
printf("x=15,y=%10.6f\n",calculate(sin,cos,*x));
printf("x=30,y=%10.6f\n",calculate(sin,cos,*x));
printf("x=45,y=%10.6f\n",calculate(sin,cos,*x));
return ;
}
将函数作为函数的参数,实现灵活的函数的使用。与C#中的委托相似。
c的详细学习(7)指针学习(一)的更多相关文章
- [转]C语言指针学习经验总结浅谈
指针是C语言的难点和重点,但指针也是C语言的灵魂 . 这篇C语言指针学习经验总结主要是我入职以来学习C指针过程中的点滴记录.文档里面就不重复书上说得很清楚的概念性东西,只把一些说得不清楚或理解起来比较 ...
- Qt 智能指针学习(7种指针)
Qt 智能指针学习 转载自:http://blog.csdn.net/dbzhang800/article/details/6403285 从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ ...
- (转) 学习C++ -> 指针初步
学习C++ -> 指针初步 一.指针 1. 什么是指针? 我们知道, 计算机的内存是由一个个独立的存储单元组成, 并且系统会对每一个存储单元分配一个唯一的号码, 称为这个存储 ...
- C++学习之指针的常见错误
C++学习之指针的常见错误 我们在编程的过程中,有时候在使用指针的时候,删除一个指针以后一定要将这个指针设置为空指针,这是因为删除这个指针只是删除这个指针指向的地址,这个指针还真是的存在程 ...
- C++中的智能指针、轻量级指针、强弱指针学习笔记
一.智能指针学习总结 1.一个非const引用无法指向一个临时变量,但是const引用是可以的! 2.C++中的delete和C中的free()类似,delete NULL不会报"doubl ...
- c指针学习小结(参考别人总结的经验)
指针学习与总结一.1.int *p :p与*先结合,说明p是一个指针,然后与int结合说明指向的是一个int型的.2.int p[3] :p与[]结合说明p是一个数组,然后与int结合,说明数组里的元 ...
- C语言指针学习总结
上学的时候学习C语言,最烦的就是里面指针,可是指针也恰恰是C语言的灵魂. 最近在重温数据结构的内容,因为大多数据结构的教材都是用C语言描述的,而数据结构中也大量的用到了指针的内容,所以我就在这篇笔记中 ...
- 《挑战30天C++入门极限》c++中指针学习的两个绝好例子
c/c++中指针学习的两个绝好例子 对于众多人提出的c/c++中指针难学的问题做个总结: 指针学习不好关键是概念不清造成的,说的简单点就是书没有认真看,指针的学习犹如人在学习饶口令不多看多学多 ...
- 计算机网络:这是一份全面 & 详细 的TCP协议学习指南
原文链接:blog.csdn.net 用这个媒体播放器组件,实时互动时也可共同观看本地视频juejin.im 前言 计算机网络基础 该是程序猿需掌握的知识,但往往会被忽略 今天,我将详细讲解计算机网络 ...
- Asp.net MVC4高级编程学习笔记-视图学习第一课20171009
首先解释下:本文只是对Asp.net MVC4高级编程这本书学习记录的学习笔记,书本内容感觉挺简单的,但学习容易忘记,因此在边看的同时边作下了笔记,可能其它朋友看的话没有情境和逻辑顺序还请谅解! 一. ...
随机推荐
- const readonly
静态常量(compile-time constants)静态常量是指编译器在编译时候会对常量进行解析,并将常量的值替换成初始化的那个值. 动态常量(runtime constants)而动态常量的值则 ...
- Theme.AppCompat.Light无法找到问题
使用adt开发新建一个Android app.选择支持的SDK版本号假设小于11(Android3.0)就会报例如以下错误. error: Error retrieving parent for it ...
- Sqlserver建立Oracle的鏈接服務器
--建立数据库链接服务器 EXEC sp_addlinkedserver @server =N'TestOracle', --要创建的链接服务器别名 @srvproduct=N'Oracle', -- ...
- [译]GLUT教程 - 位图和正交投影视图
Lighthouse3d.com >> GLUT Tutorial >> Fonts >> Bitmap Fonts and Orthogonal Projecti ...
- 【PHP】富文本HTML过滤器:HTMLPurifier使用教程(防止XSS)
在编程开发时安全问题是及其重要的,对于用户提交的数据要进行过滤,XSS就是需要重视的一点,先说一下什么是XSS,简单来说就是用户提交数据(例如发 表评论,发表日志)时往Web页面里插入恶意javasc ...
- 蒙特卡洛方法计算圆周率的三种实现-MPI openmp pthread
蒙特卡洛方法实现计算圆周率的方法比较简单,其思想是假设我们向一个正方形的标靶上随机投掷飞镖,靶心在正中央,标靶的长和宽都是2 英尺.同时假设有一个圆与标靶内切.圆的半径是1英尺,面积是π平方英尺.如果 ...
- 【一键激活win8.1系统】
下载激活工具地址: 链接:https://pan.baidu.com/s/1AUaQQRcqfCYWK94KapYjjA 密码:i4sk 下载后,右键单击“以管理员身份运行”Microsoft Too ...
- php 生成8位数唯一的激活码
/** *生成激活码 */ function showGenerationActivationCode(){ #渠道类型id $channel_id=$_POST['channel']; #根据渠道i ...
- ORA-24408: could not generate unique server group name
一台新虚拟机,CentOS 6.5系统,用lnmp一键安装包安装好Nginx + PHP环境,再安装Oracle客户端,准备搭建PHP连接Oracle,访问oracle.php,测试连接Oracle的 ...
- GUN C中的错误报告
在C语言中,很多库函数在调用失败时都会返回特定的值.比如返回-1,空指针,EOF等.但是这些值仅仅表示的调用失败,并未给出详细的错误信息.如果想查看详细的错误内容,就要去查看errno的错误代码,er ...