(1)指针与二维数组

一个数组的名字代表该数组的的首地址,是地址常量(作为形式参数的数组名除外),这一规定对二维数组或更高维数组同样适用。

在c语言中定义的任何一个二维数组实际上都可以看做是一个一维数组,该一维数组中的每一个成员又是一个一维数组。

若有定义int d[3][4],i,j;且0<=i<=2,0<=j<=3;

1.数组元素d[i][j]的表示方法为:

d[i][j]

*(d[i]+j)

*(*(d+i)+j)

(*(d+i))[j]

*(&d[0][0]+4*i*j)

*(d[0]+4*i+j)

2.数组元素d[i][j]地址的表示方法为:

&d[i][j]

d[i]+j

*(d+i)+j

&((*(d+i))[j])

&d[0][0]+4*i*j

d[0]+4*i*j

例如:设数组d的首地址是无符号十进制整数40000,则各表示形式、含义与地址的关系如下所示。其中地址值增减变化随程序运行环境的不同而不同

二维数组d各表示形式、含义与地址的关系

  1. d 二维数组名,数组首地址,第0行首地址
  2. d[],*(d+),*d 0行第0列元素d[][]的地址
  3. d+ 1行首地址
  4. d[],*(d+) 1行第0列元素d[][]的地址
  5. d[]+,*d[+]+,&d[][] 1行第2列元素d[][]地址

二维数组名d是指向行的,例如d+2表示二维数组d第2行的首地址;一维数组名d[0],d[1],d[2]是指向各自对应的列元素的,例如d[0]+2表示元素d[0]+2表示元素d[0][2]的地址。*d与*(d+1)都不代表任何数组元素。

例子:将a矩阵与b矩阵相加,所得之和存入c矩阵中。a,b,c矩阵都是3*4的矩阵

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<time.h>
  4.  
  5. int i,j;
  6. void maxtrix(int *x,int *y,int *z){
  7. for(i=;i<;i++){
  8. for(j=;j<;j++){
  9. *(z+i*+j)=*(x+i*+j)+*(y+i*+j);
  10. }
  11. }
  12. }
  13. int main(){
  14. srand((int)time(NULL));
  15. int (*p)[][],a[][],b[][],c[][];
  16. printf("The value of a:\n");
  17. for(i=;i<;i++){
  18. for(j=;j<;j++){
  19. *(a[i]+j)=(int)rand%;
  20. }
  21. }
  22. printf("the value of b:\n");
  23. for(i=;i<;i++){
  24. for(j=;j<;j++){
  25. *(*(b+i)+j)=(int)rand%;
  26. }
  27. maxtrix(*a,b[],&c[][]);
  28. }
  29. printf("The value of c:");
  30. printf("%d",a[][]);
  31. return ;
  32. }

这个程序运行时会出现错误,以后再讨论。

3.指针数组

如果一个数组的元素都是指针变量,则称这个数组是指针数组。

指针数组定义的一般形式:

类型说明符 *数组名[]......

4.指针与字符串数组

字符数组中每个元素都是一个字符,而字符串数组中指的是数组中的每个成员都是存放字符串的数组。

1)直接给字符串数组赋初值

char b[4][8]={"Turbo C","FORTRAN","BASIC","Foxpro"};

char   b[][8]={"Turbo C","FORTRAN","BASIC","Foxpro"};

2)用给字符型指针数组赋初值的方式构成字符串数组

char *f[4]={"Turbo C","FORTRAN","BASIC","Foxpro"};

char *f[]={"Turbo C","FORTRAN","BASIC","Foxpro"};

实例:利用数字月份查找其英文月份名:

  1. #include<stdio.h>
  2.  
  3. char *month_name(int n){
  4. char *name[]={
  5. "Illegal month",
  6. "January","February","March",
  7. "April","May","June","July",
  8. "August","September","October",
  9. "November","December"};
  10. return (n<|n>)?name[]:name[n];
  11. }
  12.  
  13. int main(){
  14. int n;
  15. scanf("%d",&n);
  16. printf("%d month name is %s\n",n,month_name(n));
  17. return ;
  18. }

5.指向数组的指针变量

指向数组的指针变量定义的一般形式:

类型说明符 (*变量名)[正整型常量表达式];

功能:定义一个名为"变量名"的指针变量,该指针变量所指向的是一个具有"正整型常量表达式"个元素的一维数组,该数组的每个元素都是"类型说明符"类型的。

例如:int (*p)[10],定义p是一个指针变量,它所指向的对象是一个具有10个元素的数组int型数组。

这种形式定义的指针一般用于指向二维的数组。

例如:int d[3][2],(*p)[2];p=d;

p=d;//p指向第0行,即指向整个数组d[0][0],d[0][1],d[0][2]......;

p++;//p指向第1行,即指向整个数组d[1][0],d[1][1],d[1][2]......;

(2) 二级指针

如果一个变量的值是其他变量的地址,而这些其他变量的值不再是内存地址,则这个变量是一级指针变量;

如果一个变量的值是一级指针的地址,则称这个变量是二级指针变量。

定义方法:

类型说明符   **标识符

int b,*m,**p;

m=&b;

p=&m;

实例1:二级指针输出整型数据:

  1. #include<stdio.h>
  2.  
  3. void swap(int **m,int **n){
  4. int *i;
  5. i=*m;
  6. *m=*n;
  7. *n=i;
  8. }
  9. int main(){
  10. int a,b,*pa,*pb;
  11. pa=&a;
  12. pb=&b;
  13. scanf("a=%d,b=%d",pa,pb);
  14. swap(&pa,&pb);
  15. printf("pa=%d,pb=%d\n",*pa,*pb);
  16. printf("a=%d,b=%d",a,b);
  17. return ;
  18. }

这里输入时要注意一个问题,就是输入时,要满足scanf语句中的格式,必须进行格式的完全匹配。

实例2:二级指针输出字符串型数据

  1. #include<stdio.h>
  2.  
  3. int main(){
  4. char **p,*address[]={
  5. "China","Japan","English",""
  6. };
  7. p=address;
  8. for(;**p!='\0';){
  9. printf("%s\n",*p++);
  10. }
  11. return ;
  12. }

实例3:指针数组(区别于指向数组的指针)

  1. #include<stdio.h>
  2.  
  3. int main(){
  4. int x[]={
  5. ,,,,
  6. };
  7. int *y[];
  8. int **a,b,i;
  9. for(i=;i<;i++){
  10. y[i]=&x[i];
  11. }
  12. a=y;
  13. for(b=;b>=;b--){
  14. printf("%3d",**a);
  15. a++;
  16. }
  17. printf("\n");
  18. return ;
  19. }

本程序还可以写成下面的形式:

  1. #include<stdio.h>
  2. void s(int **a){
  3. int b;
  4. for(b=;b<;b++){
  5. printf("%3d",**a);
  6. a++;
  7. }
  8. printf("\n");
  9. }
  10. int main(){
  11. int x[]={
  12. ,,,,
  13. };
  14. int *y[],i;
  15. for(i=;i<;i++){
  16. y[i]=&x[i];
  17. }
  18. s(y);
  19. return ;
  20. }

本例用二级指针变量输出了数组各元素的值。指针数组名作为函数实参时,形参得到的是实参指针数组的地址,这是一个二级指针。因此,形参可以写成int **a形式,也可以写成int *a[]的形式,两种方法等价。

(3)内存空间的动态分配

1.指向void类型的指针

void 类型是一种抽象的数据类型,如果用指针的void类型说明符定义一个指针变量,则该指针变量并没有被确定存储具体哪一种数据类型变量的地址。

void类型的指针和其他类型的指针可以相互赋值,且不必强制类型转换。

指向任何类型的指针都可以转换为指向void类型,如果将结果再转换为初始指针类型,则可以恢复初始指针类型并不会丢失信息。

  1. #include<stdio.h>
  2.  
  3. int main(){
  4. float a[]={
  5. ,,,
  6. },*p1,*p3;
  7. //测试void型指针,进行连续的类型转换
  8. void *p2=a;
  9. p1=(float *)p2;
  10. p2=(void *)p1;
  11.  
  12. p3=&a[];
  13. p2=p3;
  14. p3=(float *)p2;
  15. printf("a=%p,p1=%p,p2=%p,p3=%p,*p3=%f",a,p1,p2,p3,*p3);
  16. printf("\n");
  17. return ;
  18. }

将void型指针赋值给其他类型时,仍然需要强制类型转换。如上述14行所示。

从运行的结果可以看出,指向void的类型的指针起到了通用指针的作用。

2.常用内存管理函数

在此之前编写的程序,在程序运行之前所需要的变量数或者数组的大小就已经确定了。

C语言提供了一些内存管理函数,这些内存管理函数可以在程序运行期间分配内存空间,即可动态的分配内存空间,也可以将已经分配的内存空间释放。常用的内存管理函数有calloc,malloc,free函数。

1)calloc函数的功能是向系统申请分配连续的内存空间,如果申请获得成功,则把所分配内存区域的首地址作为函数返回值返回,该函数返回的是void类型的指针;如果申请获得成功,则函数返回空指针。原型:void *calloc(unsigned n,unsigned size);

int *p;

p=calloc(20,sizeof(int));

该程序段的功能是申请一块连续的能保存20个int型数据的内存区域,并使p指向该块内存区域。

2)malloc的功能与calloc函数类似,malloc函数用于向系统申请分配一块连续的size个字节的内存区域。

函数的原型:void *malloc(unsigned size);

3)free函数的原型是void free(void *p);其功能是释放p所指向的由calloc函数或者malloc函数已经申请成功的内存区域,free没有返回值。

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3.  
  4. int main(){
  5. int i,n,sum=,*p;
  6. printf("Please input a interger number imply that how many numbers you need to input next:\n");
  7. //scanf输入数据以空格结尾
  8. scanf("%d",&n);
  9. p=(int *)malloc(n*sizeof(int));//也可以改成p=malloc(n*size0f(int));//或者p=(int *)calloc(n,sizeof(int));
  10. printf("please input %d number:\n",n);
  11. for(i=;i<n;i++){
  12. scanf("%d",&p[i]);
  13. sum+=p[i];
  14. }
  15. free(p);
  16. printf("sum=%d\n",sum);
  17. return ;
  18. }

malloc和calloc函数用于创建动态数组,还与后续的结构体有关。

(4)main函数的参数

在此前写的main函数总是main(),实际上main()函数也可以有参数。

1.命令行参数

在操作系统提示符状态下为了执行某个操作系统命令或某个可执行文件,而键入的一行字符称为命令行。

命令行的一般形式为:命令名[参数1][参数2]......[参数n]

命令名与参数之间以及各参数之间用空格或者Tab键隔开,若参数本身含有空格,则给参数要用""包含起来。

例子:C>copy f1.c  f2.c

2.指针数组作为main函数的形参

在操作系统下运行一个c程序,实际上就是操作系统调用该函数的主函数,主函数再调用其他函数。

主函数main的形参一般有两个参数,例如:

main(int argc,char *argv[]){};

第一个参数是int型,习惯上记作argc,表示命令行中参数的个数(包括命令名在内),在运行c程序时由系统自动计算出参数的个数,第二个参数是指向字符型的指针数组,习惯上记作argv,用来存放命令行中的各个参数。(作为形参,char *argv[]等于char **argv);名字是可以自定的。

实例:假设以下程序存放在xx.c文件中,编译之后已经生成一个xx.c文件:

  1. #include<stdio.h>
  2.  
  3. int main(int argc,char *argv[]){
  4. int i=;
  5. printf("argc=%d",argc);
  6. printf("argv= ");
  7. while(i<argc){
  8. printf("%s ",argv[i]);
  9. i++;
  10. }
  11. return ;
  12. }

将该文本内容保存到Test.c文件中,为了方便我把它放在了桌面C:\Users\JMSun\Desktop,在Cfree中打开之后点击运行,将自动进行编译在桌面产生obj文件和.exe文件:

在Dos窗口中执行下述命令:

cd c:\JMSun\Desktop命令用于进入指定目录的文件夹下,进入之后则可以运用dos下的命令行直接调用函数。

而之所以在dos下能有很多直接用的程序也是这个道理,不过那些可以用的程序一般是配置过系统环境变量的。

本例命令行共有四项,其中Test是执行程序的命令,也是程序的名称,其与三项是命令行参数,argc中存放的是命令行中包括命令在内的字符串的个数。argv中存放的是命令行各项的首地址。ANSI要求argv[argc]的值必须是一个空指针。

(5)指针的编程练习

1.将直角坐标系的一个坐标点(x,y),转换为极坐标系中的坐标值(p,角度):

  1. #include<stdio.h>
  2. #include<math.h>
  3.  
  4. //用变量的地址值当形式参数,改变对应的地址所在单元的值。
  5. double change(double x,double y,double *p){
  6. double m;
  7. m=sqrt(x*x+y*y);
  8. *p=atan(y/x);
  9. return m;
  10. }
  11. int main(){
  12. double a,b,c,q;
  13. printf("please input x,y:\n");
  14. scanf("%lf%lf",&a,&b);
  15. c=change(a,b,&q);
  16. printf("c=%f,1=%f",c,q);
  17. return ;
  18. }

2.将10个国家的名字按照字母由小到大的顺序排列:

  1. #include<stdio.h>
  2. #include<string.h>
  3. //用字符数组指针指向字符数组,作为形参
  4. void sort(char *name[],int n){
  5. char *temp;
  6. int i,j,k;
  7. for(i=;i<n-;i++){
  8. k=i;
  9. for(j=i+;j<n;j++){
  10. if(strcmp(name[k],name[j])>)
  11. k=j;
  12. }
  13. if(k!=i){
  14. temp=name[i];
  15. name[i]=name[k];
  16. name[k]=temp;
  17. }
  18. }
  19. }
  20. int main(){
  21. int n=,i;
  22. char *name[]={
  23. "China","America","France","Britain","Canada",
  24. "Australia","Switzerland","Japan","Italy","Germany"
  25. };
  26. sort(name,n);
  27. for(i=;i<n;i++){
  28. printf("%s\n",name[i]);
  29. }
  30. return ;
  31. }

3.编写程序,删除字符串的所有尾部空格:

  1. #include<stdio.h>
  2. #include<string.h>
  3.  
  4. int main(){
  5. char *p;
  6. char str[];
  7. for(int k=;k<;k++){//将字符数组初始化为'\0'不是必须的。
  8. str[k]='\0';
  9. }
  10. p=str;
  11. printf("Please input a string:\n");
  12. gets(p);
  13. printf("打印一边字符串:");
  14. puts(p);
  15. //此语句用于将指针移到字符串末尾的'\0'处。
  16. while((*p)!='\0'){p++;}
  17. //上述语句等价于while(p++!='\0');p--;
  18. printf("字符串的长度为%d\n",p-str);
  19. do
  20. p--;
  21. while(*p==' ');//此语句用于将指针位置移动到'\0'前面第一个不为空格的字符上。
  22. *++p='\0';
  23. printf("%s",str);//puts(str)等同于printf("%s\n",str);
  24. return ;
  25. }

在做这个程序时,犯得错误有“指针前面忘了加*符,导致内存访问错误”,“printf和puts函数的功能的区别有点模糊”。

4.编写函数output和input,其功能分别于gets和puts相同,函数中分别用getchar()和putchar()读入和输出字符:

  1. #include<stdio.h>
  2. #include<string.h>
  3. void input1(char *s)
  4. {
  5. int i=;
  6. char c;
  7. while((c=getchar())!='\n')
  8. s[i++]=c;
  9. s[i]=;
  10. }
  11. void input2(char *a){
  12. char c;
  13. int i=;
  14. c=getchar();
  15. while(c!='\n'){
  16. a[i++]=c;
  17. c=getchar();
  18. }
  19. }
  20. void output(char a[]){
  21. while(*a!='\0'){
  22. putchar(*a++);
  23. }
  24. }
  25. int main(){
  26. char *p;
  27. char str[];
  28. output("please input a string:\n");
  29. input1(p);//同于input1;
  30. output(p);
  31. }

在本例中我出错的地方是getchar()函数用出错了,char c=getchar();我用成char c;getchar(c);

5.统计一个字符串中所有单词的个数:

  1. #include<stdio.h>
  2. #include<string.h>
  3.  
  4. int count(char *q){
  5. char *p=q;
  6. int flag=;//用于返回的单词个数。
  7. //将指针指到字符数组的最后的'\0'的位置
  8. while(*p++!='\0');
  9. p--;
  10.  
  11. for(p--;q<=p;p--){
  12. if(*p!=' '&&*(p-)!=' '|*p==' '&&*(p-)==' '){
  13. continue;
  14. }else if(*p!=' '&&*(p-)==' '){
  15. flag++;
  16. }else if(*p!=' '&&p==q){
  17. flag++;
  18. }
  19. }
  20. return flag;
  21. }
  22.  
  23. int main(){
  24. char str[],*p;
  25. puts("Please input a string containing many space to divide them: ");
  26. p=str;
  27. gets(p);
  28. printf("单词的个数为:%d",count(p));
  29. }

不知道应该写什么,自己怎么搞定的好像都不太清晰。醉了!

6.编写一个小学生做加减乘除的程序。例如在操作系统下,键入cal 15 * 15<回车>则在屏幕上显示15*15=225:

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4.  
  5. int main(int argv,char *argc[]){
  6. double a,b;
  7. double result;
  8. a=atof(argc[]);
  9. b=atof(argc[]);
  10. if(strlen(argc[])==){
  11. if(argc[][]=='+'){
  12. result=a+b;
  13. }else if(argc[][]=='-'){
  14. result=a-b;
  15. }
  16. else if(argc[][]=='*'){
  17. result=a*b;
  18. }
  19. else if(argc[][]=='/'){
  20. result=a/b;
  21. }
  22. printf("%lf %s %lf=%lf",a,argc[],b,result);
  23. }else
  24. {
  25. printf("输入错误!\n");
  26. }
  27. return ;
  28. }

这里采用了c语言中用于将字符串转化为数的函数,float atof(char *p ); 类似的还有int atoi(char *p);这两个函数都包含在"stdlib"头文件里。

  在C语言中将字符串转化成整型有两种方法。
  1 用atoi函数。
  atoi的功能就是将字符串转为整型并返回。其声明为
  int atoi(char *str);
  比如atoi("1234");会返回整型1234。

  2 用sscanf。
  sscanf与标准格式化输入函数scanf类似,不过源并非是标准输入,而是字符串。
  用sscanf可以处理更复杂的字符串。
  比如字符串char * str = "a=1, b=2";
  定义int a,b;后
  可以用
  sscanf(str,"a=%d, b=%d",&a,&b);
  来将a,b值提取,计算后,a=1, b=2。

c的详细学习(8)指针学习(二)的更多相关文章

  1. [转]C语言指针学习经验总结浅谈

    指针是C语言的难点和重点,但指针也是C语言的灵魂 . 这篇C语言指针学习经验总结主要是我入职以来学习C指针过程中的点滴记录.文档里面就不重复书上说得很清楚的概念性东西,只把一些说得不清楚或理解起来比较 ...

  2. 【Unity Shaders】学习笔记——SurfaceShader(二)两个结构体和CG类型

    [Unity Shaders]学习笔记——SurfaceShader(二)两个结构体和CG类型 转载请注明出处:http://www.cnblogs.com/-867259206/p/5596698. ...

  3. (转) 学习C++ -> 指针初步

    学习C++ -> 指针初步 一.指针    1. 什么是指针?        我们知道, 计算机的内存是由一个个独立的存储单元组成, 并且系统会对每一个存储单元分配一个唯一的号码, 称为这个存储 ...

  4. 一起学习造轮子(二):从零开始写一个Redux

    本文是一起学习造轮子系列的第二篇,本篇我们将从零开始写一个小巧完整的Redux,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Promises/A+,Red ...

  5. C++中的智能指针、轻量级指针、强弱指针学习笔记

    一.智能指针学习总结 1.一个非const引用无法指向一个临时变量,但是const引用是可以的! 2.C++中的delete和C中的free()类似,delete NULL不会报"doubl ...

  6. WCF入门教程(四)通过Host代码方式来承载服务 一个WCF使用TCP协议进行通协的例子 jquery ajax调用WCF,采用System.ServiceModel.WebHttpBinding System.ServiceModel.WSHttpBinding协议 学习WCF笔记之二 无废话WCF入门教程一[什么是WCF]

    WCF入门教程(四)通过Host代码方式来承载服务 Posted on 2014-05-15 13:03 停留的风 阅读(7681) 评论(0) 编辑 收藏 WCF入门教程(四)通过Host代码方式来 ...

  7. 【转】 Pro Android学习笔记(九二):AsyncTask(1):AsyncTask类

    文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://blog.csdn.net/flowingflying/ 在Handler的学习系列中,学习了如何h ...

  8. c指针学习小结(参考别人总结的经验)

    指针学习与总结一.1.int *p :p与*先结合,说明p是一个指针,然后与int结合说明指向的是一个int型的.2.int p[3] :p与[]结合说明p是一个数组,然后与int结合,说明数组里的元 ...

  9. 我的MYSQL学习心得(十二) 触发器

    我的MYSQL学习心得(十二) 触发器 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数 ...

随机推荐

  1. APP开发关于缓存

    1 http://www.cnblogs.com/qianxudetianxia/archive/2012/02/20/2112128.html 1.1 http://blog.csdn.net/ln ...

  2. 2.JAVA编程思想——一切都是对象

    一切都是对象 欢迎转载.转载请标明出处:http://blog.csdn.net/notbaron/article/details/51040221 虽然以C++为基础,但 Java 是一种更纯粹的面 ...

  3. 搭建Squid反向代理服务器

    好吧,更新个文章,有段时间没写技术博文了.今天就说说squid反向代理这个服务,当然,这是在Linux下配置完成的.说自己没偏见似乎不可能 了.大概是相对喜欢Linux而已.但我从不否认Windows ...

  4. springboot整合docker部署(两种构建Docker镜像方式)

    项目结构 package hello; import org.springframework.boot.SpringApplication; import org.springframework.bo ...

  5. reveal end of document

     window - Preferences - Run/Debug - Console 将 Console buffer size (characters)设置大一点

  6. C语言include预处理命令与多文件编译

    #include预处理命令几乎使我们在第一次接触C的时候就会碰到的预处理命令,可我现在还不怎么清楚,这次争取一次搞懂. 一.#include预处理指令的基本使用 预处理指令可以将别处的源代码内容插入到 ...

  7. C语言中的一个*和[]优先级问题

    最近写着玩了这么一段代码 int Init(int **T, int v1, int v2, int v3) { *sizeof(int)))) exit(-); *T[]=v1, *T[]=v2, ...

  8. EJB是什么?(节选)

    近期的项目中使用了EJB.当时就仅仅知道怎么用,没有深入的去理解.当完毕这个项目之后.再回想项目中使用的技术.框架以及工具的时候,突然感觉对EJB这个概念非常是模糊,于是上网搜一些资料.可是,非常多的 ...

  9. centos7.2 开发 部署 .net core

    1.centos7.2 安装 nginx官方文档:http://nginx.org/en/linux_packages.html#mainline 创建nginx.repo 文件 Pre-Built ...

  10. 【ubantu】Ubuntu的一些常用快捷键

    Ubuntu操作基本快捷键* 打开主菜单 = Alt + F1* 运行 = Alt + F2* 显示桌面 = Ctrl + Alt + d* 最小化当前窗口 = Alt + F9* 最大化当前窗口 = ...