C语言-数组和指针-学习攻坚

文章目录

  • 前言
  • 一、指针是什么
    • 1.1 先从数据类型说起
    • 1.2 利用指针调用数据类型
  • 二、为什么要用指针
  • 三、数组类型
    • 3.1数组和指针
    • 3.2二维数组与数组指针
    • 3.3指针数组
  • 总结


前言

C语言是我第一个接触的编程语言,当时通过学习C语言的编程学习,我大概建立起来了计算机编程的思维。其中利用最基础的变量、字符等基础概念自然不在话下,搭配for循环、函数等也能编写一些程序。但是每每提到指针总会感觉:真不熟。不过最近有些感悟,特此写了本文,记录下自己对指针的认识!


一、指针是什么

我们经常听到一句话:指针里面保存的地址,地址指向的位置是变量的值(通过*操作)。在我看来指针也是一种数据类型,和整形那些都一样,只不过它存储的是不是一般的数,而是地址值。或许指针才是一名进阶程序员所使用的数据类型,而那些整型、浮点…

1.1 先从数据类型说起

路要一步步走,先从简单的说起,纠正一些误区。

char        //字符数据类型  short       //短整型
int         //整形          long        //长整型
long long   //更长的整形    float       //单精度浮点数
double      //双精度浮点数

1.2 利用指针调用数据类型

int main()
{
	char ch = 'w';
	char *p1=&ch;
    int number = 10;
    int *p2 = &number;
    printf("%c\n",ch);
    printf("%d\n",number);
    return 0;}

这里说一点指针指向数据的格式:int *p2 = &number;
int *指明了要声明的变量是个整型指针,p2是这个变量的名字
所以
**定义的时候赋值是int *p2 = &number;
**定义之后再赋值是int *p2 ;p2 = &number(*p2 = number);
注:&为取地址符,*为取地址指向的变量值

问:我直接调用这些数据类型不就行了,为啥还要用指针?

二、为什么要用指针

通过指针不仅可以对数据本身,还可以对存储数据的变量地址进行操作,注意这里的对其地址进行操作并不是改变变量的地址本身。举个例子:调用函数的时候,我们需要对函数输入参数,但是在函数里面在对这个参数进行赋值、操作的时候,在主函数内的参数是不改变的。但是如果输入的是地址,在函数里面改变地址指向值是可以影响主函数中的变量的。这就是形参,也解释了对其地址进行操作的特殊性。指针就是内存地址,指针变量是用来存放内存地址的变量。
指针的具体作用有很多,使用指针代码运行速度更快(我认为用指针才是一个C语言master必用的)。具体的一些方面如下:
1可以当成全局变量使用

#include 
#include 
int num = 0;
int *p1 = #
int func(int x){
    *p1=*p1+1;
    if(x <=2)
        return;
    return func(x- 3)+ func(x- 5);}
void main()
{
    func(10);
    printf("%d\n",*p1);}

2使用指针可以完成参数的传递,要不然形参是不能影响实参的值的。

#include 
#include 
int num = 0;
int *p1 = #
int func(int * x){
    *x=*x+1;//2.在这里为啥又用 *x,因为不能直接对地址进行操作,要对地址指向的值进行操作(瞎哔哔的)
    return 0;}
void main()
{
    func(p1);//1.在主函数进行掉用是func(p1);为啥不写*p1是因为函数定义int func(int * x)的int *代表的是数据类型,x才是名称
    printf("%d\n",*p1);}

如果直接将 int 型的变量输入进去的话,只会改变形参的量,而将指针传进去的话,在对指针进行操作(也就是对地址进行操作)是可以改变的实参传输。

指针的真相不止如此,接着向下看

三、数组类型

3.1数组和指针

在来看数组类型,数组类型的定义。

int ar[10] ;

前面在讲指针,至于为啥要在这里讲数组类型,是因为数组名也是代表了首个数组变量的地址。它与和指针很相似,之前还一直以为数组名就是指针,其实并不是。可以参考一下这篇文章链接: 数组名和指针的区别 和链接: [数组]使用与详解-C语言。虽然不是完全相同,但是数组名的用法和指针的用法还是有很多地方的操作是一样的。
在C中,两个表达式ar[i]和*(ar+i)的意义是等价的。而且不管ar是一个数组名还是一个指针变量,这两个表达式都可以工作。然而只有当ar是一个指针变量时,才可以使用ar++这样的表达式。

处理数组的函数实际上是使用指针做为参数的。但是在编写处理数组的函数时,数组符号和指针符号都是可以选用的。如果使用数组符号,则函数处理数组这一事实更加明显。同时,对于习惯于其他编程语言(如 FORTRAN、Pascal、Modula-2或BASIC)的程序员来说,使用数组也更为熟悉。也有一些程序员可能更习惯于使用指针,觉得指针使用起来更加自然。

使用数组符号做为参数

int sum(int ar[],int n);
int main(void)
{
	int marbles[10]={1,2,3,4,5,6,7,8,9,10};
	int answer;
	answer=sum(merbles,10);
}

使用指针做为参数

int sum(int *start,int *end);
int main(void)
{
	int marbles[10]={1,2,3,4,5,6,7,8,9,10};
	int answer;
	answer=sum(merbles,merbles+10);
}

以上也有一个问题,那就是数组会被当做实参传递,因此特殊的时候需要保护数组内容,该内容暂时不做详述。

指针符号(尤其是在对其使用增量运算符时)更接近于机器语言,而且某些编译器在编译时能够生成效率更高的代码。然而,很多程序员认为程序员的主要任务是保证程序的正确性和易读性,代码的优化应留给编译器去做。

3.2二维数组与数组指针

前面讲了数组名和指针可以在一定程度上相同使用。按照上边的写法,可以这样写:

int a[3]={0,1,2}; //[]表示a为数组,[x]中的数字x为索引,从零开始。
int * p=a;

然后再利用*p+1 就可以调用数组元素 (当然用数组名和指针是一样的) ,除了一维数组还有多维数组。

如何声明指向二维数组的指针变量per?指向int的指针可以胜任吗?不可以。这种指针只是和a[0]兼容,因为它们都指向一个单个int值。但是a是其首元素的地址,而二维数组首元素又是包含两个int值的数组。因此,per必须指向一个包含两个int值的数组,而不是指向一个单个int值。下面是正确的代码;

int (*pz)[2];//pz指向一个包含2个int值的数组

数组指针的例子

//错误情况
#include 
#include 
int main()
{
    int a[2][2]={1,2,3,4};
    int *p=a;
    printf("%d\n",*(*(p+1)+1));
    return 0;}
//正确情况

int main()
{
    int a[2][2]={1,2,3,4};
    int (*p)[2]=a;
    printf("%d\n",*(*(p+1)+1));
    //printf("%d\n",p[1][1]);
    return 0;}

上面a为二维数组,a[i]就是一维数组,可以把它看做是二维数组的一行。
a[0]和a[1]的地址差在这里就是两个字节。

3.3指针数组

上面的定义int (*p)[2];为什么要使用(),因为表达式中[]优先级高于 *,如果写成

int * p[2];

[ ]与p结合代表包含两个某种元素的数组。然后和*结合,表示p是两个指针组成的数组。最后用int来定义,表示p是有两个指向int值的指针构成的数组。这种声明会创建两个指向单个int值的指针。

总结

数组是由同一种数据类刑的元素系列构成的,数佣元素按顺序存储于内存中,通过使用整数索引(或偏移量)来访问。在C中,首元素的索引值为0。因出句众n个元素的数组的末元素索引为n1。程序员要能够正确地使用数组索引,因为编译哭和程序z行时都不检杳索引是否合法。
要声明一个简单的一维数组,可以采用下面的形式:

type name [size];

此处,type是数组内每个元素的数据类型,name是数组名,size是元素的个数。传统上,C要求size是一个常量整数表达式。而C99标准则允许使用非常量整数表达式,这种情况下,数组是变长数组。
C把数组名解释为该数组首元素的地址。也就是说,数组名和指向首元素的指针是等价的。通常,数组和指针是紧密联系的。如果ar是数组,那么表达式ari]和* ( ar+i)是等价的。
C不支持把整个数组作为函数参数进行传递,但是可以传递数组的地址。然后函数可以利用该地址来处理原始数组。如果函数功能不需要修改原始数组,那么在声明相应的形式参量时,需要加上关键字const.在被调函数中,您可以使用数组符号或指针符号。无论哪种形式,实际上使用的都是指针变量。
对指针加上一个整数或进行增量运算时,指针值的改变都是以所指向对象的字节大小为单位的。也就是说,如果pd指向数组内的一个8字节长的double数值,则对pd加1就相当于对它的值加上数值8。这样,该指针会指向数组的下一个元素。
二维数组表示数组的数组。例如:

double sales[5][12];

这个声明创建了包含5个元素的数组sales,每个元素包含12个double数。这些一维数组的第1个可以写作 sales[0],第2个可以写作为sales[1],等等。每个都是包含12个double数的数组。使用第二个索引可以访问这些数组中的每一个元素。例如,sales[2][5]是sales[2]的第6个元素,sales[2]是sales 的第3个元素。
传统的C向函数传递多维数组的方法是把数组名(也就是地址)传递给相应类型的指针参量。指针的声明需要指定各维的大小(除了最左面的不需要明确指出大小);第一个参量的维数大小通常做为第二个参数来传递。例如,要处理前面提到的数组 sales,函数原型和函数调用应该如下:

void display (double ar[][12],int rowrs );
...
display (sales,5);

变长数组则提供了另一种方法,两个维大小都可以做为参数被传递给函数。因此,函数原型和函数调用就可以如下这样写:

void display (int rows.int cols.double ar[rows] [cols]);
...
display (5,12, sales);

本例使用了int 数组和double 数组,对于其他类型的数组,结论也都适用。然而,字符串有很多特殊的规则。这是由它的终止null 字符决定的。有了这个终止字符,无须向函数传递字符串大小,函数就能够
检测字符串的结束。在第11章“字符串和字符串函数”中我们将详细介绍字符串的特性。

你可能感兴趣的:(C/C++,c语言,学习,c++)