C语言:随笔6--指针1.2

1、多维数组与指针

用指针变量可以指向一维数组中的元素,也可以指向多维数组的元素。一个数组他的数组名就是这个数组的首地址,而指针变量里边存放的恰好就是一个地址。我们只要把这个指针变量里边存放的是一维数组的首地址,那么我们就说这个指针指向的就是这个数组。

下面是一个3行4列的数组,在内存中的存储其实他是线性排列存储的。

  

#include<stdio.h>
void main()
{
   int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11,12};
   printf("a:%d\n",a);
   printf("*a:%d\n",*a);
   printf("a[0]:%d\n",a[0]);
   printf("&a[0]:%d\n",&a[0]);
   printf("&a[0][0]:%d\n",&a[0][0]);
   printf("a+1:%d\n",a+1);
   printf("*(a+1):%d\n",*(a+1));
}

把二维数组a分解为一维数组a[0](我们这个a[0]又指向了四个元素,a[0]里面存放的是一个地址,这个地址有四个元素,那么也就是说它就是一个二维数组,),a[1](同理),a[2](同理)之后(说明这是一个a3x4的一个二维数组,概念就是当我这个一维数组这个元素里边存放的是另一个数组的地址的时候,那他就是一个二维数组,因为我这个元素a[0]还没完,里面还指向一个地址,这个地址是一个数组的地址,那就是二维数组了),设p为指向二维数组的指针变量。可定义为:

int (*p)[4];//它表示p是一个指针变量,它指向包含四个元素的一维数组。若指向第一个一维数组a[0],其值等于a,a[0],或者&a[0][0]等。
//而p+i则指向一维数组a[i];因为p指向的是a的地址。
//从前面的分析可以得出*(p+i)+j是二维数组i行j列的元素的地址,而*(*(p+i)+j)则是i行j列元素的值
//怎么理解呢?????????
//因为我们之前解释,如果一维数组里面存放的是另外一个数组的地址那它就是一个二维数组。
//那我们这里p+i也就是说一维数组第几个元素取出他的值(比如是a[i],*(p+i)是一个地址值,**(p+i)才是对应第i行的首个元素值)
也就是相应一个二维数组的一个元素的初始地址,再加上j就是这个二维数组的偏移地址,第几列,然后我们再把总共的取出他的值。

//二维数组指针变量说明的一般形式为:

类型说明符  (*指针变量名)[长度]

其中“类型说明符”为所指数组的数据类型。“*”表示其后的变量是指针类型。“长度”表示二维数组分解为多个一维数组时,一维数组的长度,也就是二维数组的列数。(//前面的不是应该声明为行数吗?那行数不用定义吗?)。行数不用定义,这个行数是多少。也就是说一维数组是多少我们的具体长度是看我们给他赋值,赋多少值它自动分配的。所以我们直接给出一个指针,指向它的行数的首地址就可以了。

例子:用指针输出二维数组元素变量的值

#include<stdio.h>
void main()
{
    int a[3][4]={};
    int (*p)[4];//定义一个二维数组的指针,指向二维数组//切记(*p)中的括号一定要带
    p=a;//将二维数组的地址指向了这个指针的地址
    int i,j;//定为i和j来定义行和列
    for(i=0;i<3;i++)
    {
       for(j=0;j<4;j++)
       {
          printf("%2d",*(*(p+i)+j));
       }
       printf("\n");
    }
}

例子:通过输入指定行数和列数打印出二维数组对应任一行任一列元素的值。

#include<stdio.h>
void main()
{
   int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
   int (*p)[4],i,j;
   p=a;
   printf("i=");
   scanf("%d",&i);
   while(i>2||i<0)
   {
      printf("i=");
      scanf("%d",&i);
   }
   printf("j=");
   scanf("%d",&j);
   while(j>3||i<0)
  {
      printf("j=");
      scanf("%d",&j);
  }
  printf("a[%d,%d]=%d\n",i,j,*(*(p+i)+j));//经过两次取值
}

2、字符串与指针

(1)用字符数组存放一个字符串,然后输出该字符串。

例子:定义一个字符数组,对他初始化,然后输出该字符串。(通过字符数组的形式来定义字符串)

#include<stdio.h>
void main()
{
   char string[]="I Love Fishc.com!";//不定义长度,让编译器自行计算
   printf("%s\n",string);//s就是字符串的形式打印,给他一个地址,给他字符串的首地址他就会依次打印直到出现'\0'字符他就停止打印。
}

下面展示了元素依次存放,空格也占用一个元素。在最后还要补充一个'\0',\0这个字符相当于ASCII中的整形0。

(2)用字符指针指向一个字符串。

例子:可以不定义字符数组,而定义一个字符指针。用字符指针指向字符串中的字符。

#include<stdio.h>
void main()
{
    char *string="I Love Fishc.com!";//这里定义的是一个字符指针,不再是一个字符串
    printf("%s\n",string);
}

(3)对字符串中字符的存取,可以用下标方法,也可以用指针方法。

例子:将字符串a复制为字符串b

//下标法举例
#include<stdio.h>
void main()
{
    char a[]="A is a good man!",b[40];
    int i;
    for(i=0;*(a+i)!='\0';i++)//只要a数组的这个元素不为0,0就是字符串结束的标志
    {
        *(b+i)=*(a+i);//不为0的话就依次copy过去
    }
    *(b+i)='\0';//然后最后还应该把他补上0因为我们这个是字符串,如果没有补上的话,他就不知道字符串的结尾是哪里,她就会把内存中相关的我们定义的40个字符嘛,这些随机的数据他都会把它当成字符串给他显示出来直到没有。
    printf("String a is:%s\n",a);
    printf("String b is:");//2也可以直接打印b
    for(i=0;b[i]!='\0';i++)
    {
       printf("%c",b[i]);//1使用了数组的索引方式打印的
    }
    printf("\n\n");
}

用指针的方法就是迭代循环里边的传递不同

//下标法举例
#include<stdio.h>
void main()
{
    char a[]="A is a good man!",*p1,*p2;//定义的两个指针分别指向数组a和数组b,那么我取出p1的值也就相当于取出a的值
    int i;
    p1=a;
    p2=b;
    for(;*p1!='\0';p1++,p2++)//++就是递增1,往后边元素,指向后边元素
    {
        *p2=*p1;//不为0的话就依次copy过去
    }
    *p2='\0';//然后最后还应该把他补上0因为我们这个是字符串,如果没有补上的话,他就不知道字符串的结尾是哪里,她就会把内存中相关的我们定义的40个字符嘛,这些随机的数据他都会把它当成字符串给他显示出来直到没有。
    printf("String a is:%s\n",a);
    printf("String b is:");//2也可以直接打印b
    for(i=0;b[i]!='\0';i++)
    {
       printf("%c",b[i]);//1使用了数组的索引方式打印的
    }
    printf("\n\n");
}

(4)字符指针作函数参数

用函数调用实现字符串的赋值

(1)用字符数组作参数。

(2)形参用字符指针变量。

//设一个函数process,在调用他的时候,每次实现不同的功能。
//输入a,b第一次调用process时找最大,第二次找最小,第三次求和。
#include<stdio.h>
void main()
{
   int max(int,int);
   int min(int,int);
   int add(int,int);
   void process(int,int,int(*fun)());
   int a,b;
   printf("Enter a and b:");
   scanf("%d %d",&a,&b);
   printf("max=");
   process(a,b,max);
   printf("min=");
   process(a,b,min);
   printf("add");
   process(a,b,add);
}
int max(int x,int y)
{
   int z;
   if(x>y)
   {
      z=x;
   }
   else
   {
      z=y;
    }
   return z;
}

(5)

(6)返回指针值的函数

一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。其概念与以前类似,只是带回的值的类型是指针类型而已。

这种带回指针值的函数,一般定义形式为:

类型名 *函数名(参数列表);
//例如:
int *a(int x,int y);//加上*号表示带回来的是指向整型的指针。如果不带回什么就void

例子:有若干个学生的成绩(每个学生有4门课程),要求在用户输入学生序号以后,能输出该学生的全部成绩,用指针函数来实现。

#include<stdio.h>
void main()
{//用二维数组每一行表示每一个同学,4个列表示4门课程
   double score[][4]={
   
   {},{},{},{}};
   double *search(double(*pointer)[4],int n);//定义了一个指针函数(他首先是一个函数,因为有一个括号,接着返回指针类型,这个指针还是指向double的指针)
   double *p;
   int i,m;
   printf("PLease enter the number of student:");
   scanf("%d",&m);
   
   printf("The scores of No.%d are:",m);
   p=search(score,m);//3返回值给p把他的地址给p,然后下边再打印出来。
   for(i=0;i<4;i++)//打印第m行的元素值;p代表第m 行的首地址
   {
       printf("%5.2f\t",*(p+i));
   }
   printf("\n\n\n")
}
double *search(double(*pointer)[4],int n)
{
   double *pt;
   pt=*(pointer+n);//1序号加上行数的索引,取出来的就是说某一个学生的一个归于那一行地址的一个索引,然后再返回
   return pt;//2返回的是二维数组第n行的首地址
}

对上例中的学生,找出其中有不及格课程的学生及其学生号。(对比一下,把各个成绩提取出来,如果小于60,Ok就把这个数组的指针给返回来,然后接收到指针把他对应的人给打印出来)

//代码

PS:指针函数和函数指针的区别(这两个概念都是简称)

指针函数:是指带指针的函数,即本质是一个函数。

函数指针:是指向函数的指针变量,因而函数指针本身首先是指针变量,只不过该指针变量指向函数。

(7)指针数组和指向指针的指针

指针数组(终究还是一个数组)的概念:

一个数组若其元素均为指针类型的数据,称为指针数组,也就说,指针数组中的每一个元素都相当于一个指针变量(也就是一堆指针变量排排站站成一队)。一维指针数组的定义形式为:

类型名 数组名[数组长度];
//例如:
int *name[4];//*表示他是一个指针

(8)总结

5、两个指针变量的比较

若两个指针指向同一个数组的元素,则可以进行比较,指向前面的元素的指针变量“小于”指向后面的元素的指针变量。(其实比较的是两个指针分别指向这个数组元素的序号)

6、void类型

void真正发挥作用:(1)对函数返回类型的限定;(2)对函数参数类型的限定。

void类型用于指针。ANSIC新标准增加了一种”void“指针类型,即不指定他是指向哪一种类型数据的指针变量。(他是空类型啥都不是,当成放一个地址,放一个地址指向的是什么变量呢?暂时还不知道。但是我们可以通过一个括号int即()int把他变成一个整型,或者()其他类型)

李例如void *p;表示指针变量p不指向一个确定的类型数据,他的作用仅仅是用来存放一个地址。

void指针他可以指向任何类型数据(因为他是空的),也就是说可以用任何类型的指针直接给void指针赋值,但是,如果需要将void指针的值赋给其他类型的指针,则需要进行强制类型转换。

7、用三个例子来谈谈const(指针)

#include<stdio.h>
void main()
{
    const char *str="Welcome to!\n\n";
   //这个语句的含义是:声明一个名为str的指针变量,他指向一个字符型常量,初始化str为指向字符串"Welcome to\n\n"
//再声明他为const就是常量(就是是一个变量常量化使他不能够被改变)
#if(0)//宏定义//如果改为1让他编译就会报错:L-value specifies const object.(因为常量对象是不能被赋值的,不能充当左值的)
    str[0]='w';//这条语句是错误的,但可以改变str指针的值。
#enfif
    str="I Love!\n\n";//合法//我们强制把他的地址改变成指向"I Love!\n\n"
    printf("\n\n%s",str);
}

将上边的修改一下

#include<stdio.h>
void main()
{
    char *const str="Welcome to!\n\n";//存放在常量的data区
   //常量指针是一个固定的指针,不可以改变它的值,但它所指的数据可以改变。
   str[0]='w';//windows禁止常量被重写(所以我这里把他重写为小写的w他就不给写)//但是编译是符合c语言的语法规范的
#if(0)
    str="I Love!\n\n";//非法
//从上边可以看到const接近str这个指针的时候,说明把这个指针定义为const变量了,他就不能被修改。
//如果说把const放在前面,那就是定义每一个字符是一个const常量不能被替换。
#enfif
    printf("\n\n%s",str);
}

再修改一下:

#include<stdio.h>
void main()
{
    const char *const str="Welcome to!\n\n";//存放在常量的data区
   //两边都是const变量
   str[0]='w';//非法
   str="I Love!\n\n";//非法

    printf("\n\n%s",str);
}

API函数:memcpy

//copyes characters between buffers
void *memcpy(void *dest,const void*src,size_t count)

猜你喜欢

转载自blog.csdn.net/m0_37957160/article/details/108578515