码迷,mamicode.com
首页 > 编程语言 > 详细

C语言关于数组与指针内容小结

时间:2016-05-12 23:48:28      阅读:441      评论:0      收藏:0      [点我收藏+]

标签:

数组的基本概念

什么是数组:数组就是:数组是相同类型的元素的一个集合       类型说明符 数组名 [常量表达式];
其中,类型说明符是任一种基本数据类型或构造数据类型。数组名是用户定义的数组标识符。方括号中的常量表达式表示数据元素的个数,也称为数组的长度。例如:
int a[10]; /* 说明整型数组a,有10个元素 */
float b[10], c[20]; /* 说明实型数组b,有10个元素,实型数组c,有20个元素 */
char ch[20]; /* 说明字符数组ch,有20个元素 */
数组应注意以下五点:
数组的类型实际上是指数组元素的取值类型。对于同一个数组,其所有元素的数据类型都是相同的。
数组名的书写规则应符合标识符的书写规定。
数组名不能与其它变量名相同。
方括号中常量表达式表示数组元素的个数,如a[5]表示数组a有5个元素。但是其下标从0开始计算。因此5个元素分别为a[0], a[1], a[2], a[3], a[4]。
不能在方括号中用变量来表示元素的个数,但是可以是符号常数或常量表达式

数组的应用

1、一维数组的引用

数组元素是组成数组的基本单元。数组元素也是一种变量, 其标识方法为数组名后跟一个下标。下标表示了元素在数组中的顺序号。数组元素的一般形式为:
    数组名[下标]
其中下标只能为整型常量或整型表达式。如为小数时,C编译将自动取整。例如:
    a[5]
    a[i+j]
    a[i++]
都是合法的数组元素。

数组元素通常也称为下标变量。必须先定义数组,才能使用下标变量。在C语言中只能逐个地使用下标变量,而不能一次引用整个数组。例如,输出有10个元素的数组必须使用循环语句逐个输出各下标变量:
for(i=0; i<10; i++)
printf("%d",a[i]);
而不能用一个语句输出整个数组。因此,下面的写法是错误的:
printf("%d",a);

2、一维数组的初始化:

给数组赋值的方法除了用赋值语句对数组元素逐个赋值外, 还可采用初始化赋值和动态赋值的方法。

数组初始化赋值是指在数组定义时给数组元素赋予初值。数组初始化是在编译阶段进行的。这样将减少运行时间,提高效率。初始化赋值的一般形式为:
    类型说明符 数组名[常量表达式] = { 值, 值……值 };
其中在{ }中的各数据值即为各元素的初值,各值之间用逗号间隔。例如:
  1. int a[10]={ 0,1,2,3,4,5,6,7,8,9 };
相当于
a[0]=0; a[1]=1 ... a[9]=9;

C语言对数组的初始化赋值还有以下几点规定:
1) 可以只给部分元素赋初值。当{ }中值的个数少于元素个数时,只 给前面部分元素赋值。例如:
int a[10]={0,1,2,3,4};
表示只给a[0]~a[4]5个元素赋值,而后5个元素自动赋0值。
2) 只能给元素逐个赋值,不能给数组整体赋值。例如给十个元素全部赋1值,只能写为:
int a[10]={1,1,1,1,1,1,1,1,1,1};
而不能写为:
int a[10]=1;
3) 如给全部元素赋值,则在数组说明中,可以不给出数组元素的个数。例如:
int a[5]={1,2,3,4,5};
可写为:
int a[]={1,2,3,4,5};

多维数组(二维数组)

这里主要讨论二维数组:
二维数组定义的一般形式如下:
   类型标识符  数组名【常量表达式1】【常量表达式2】;
例如:
   int a[2][3];
   float b[3][10];
二维数组的初始化有两种:
(1)分行初始化,如:
     static int a[2][3]={{1,2,3,},{4,5,6}};
 (2)统一初始化,如:
     static int a[2][3]={1,2,3,4,5,6};

指针的基本概念

在计算机中,所有的数据都是存放在内存中的,一般把内存中的一个字节称为一个内存单元,不同的数据类型所占用的内存单元数不一样,如int占用4个字 节,char占用1个字节。为了正确地访问这些内存单元,必须为每个内存单元编上号。每个内存单元的编号是唯一的,根据编号可以准确地找到该内存单元。

内存单元的编号叫做地址(Address),也称为指针(Pointer)。 

内存单元的指针和内存单元的内容是两个不同的概念。 可以用一个通俗的例子来说明它们之间的关系。我们用银行卡到ATM机取款时,系统会根据我们的卡号去查找账户信息,包括存取款记录、余额等,信息正确、余 额足够的情况下才允许我们取款。在这里,卡号就是账户信息的指针, 存取款记录、余额等就是账户信息的内容。对于一个内存单元来说,单元的地址(编号)即为指针,其中存放的数据才是该单元的内容。

在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。

设有字符变量c,其内容为 ‘K‘(ASCII码为十进制数 75),c占用了0X11A号内存单元(地址通常用十六进数表示)。设有指针变量p,内容为 0X11A,这种情况我们称为p指向变量c,或说p是指向变量c的指针。


 
严格地说,一个指针是一个地址,是一个常量。而一个指针变量却可以被赋予不同的指针值,是变量。但常把指针变量简称为指针。为了避免混淆,本教程约定:“指针”是指地址,是常量,“指针变量”是指取值为地址的变量。定义指针的目的是为了通过指针去访问内存单元。

既然指针变量的值是一个地址,那么这个地址不仅可以是变量的地址,也可以是其它数据结构的地址。在一个指针变量中存放一个数组或一个函数的首地址有何意义呢?

因为数组或函数都是连续存放的。通过访问指针变量取得了数组或函数的首地址,也就找到了该数组或函数。这样一来,凡是出现数组,函数的地方都可以用一个指 针变量来表示,只要该指针变量中赋予数组或函数的首地址即可。这样做,将会使程序的概念十分清楚,程序本身也精练、高效。

在C语言中,一种数据类型或数据结构往往都占有一组连续的内存单元。用“地址”这个概念并不能很好地描述一种数据类型或数据结构,而“指针”虽然实际上也 是一个地址,但它却是一个数据结构的首地址,它是“指向”一个数据结构的,因而概念更为清楚,表示更为明确。 这也是引入“指针”概念的一个重要原因。

一级指针的应用

所谓一级指针可以说是储存变量地址的指针变量, 如 int a = 1; int *p = &a;

(1) & 取地址运算符
在以前我们所讲的scanf语句里面出现过&地址符,如
int a; 定义了一个整型变量,系统为这个整型变量分配一个内存地址。
scanf(“%d”,&a); 把读入的数据放在这个内存地址中。
printf(“a=%d”,a); 把这个变量的值(内存地址的值)输出。
而如果改用指针,程序可以改为这样:
int a,*p=a;
scanf(“%d”,p);
printf(“a=%d”,a);
(2) * 取(指针)内容运算符
我们也可以通过一定的操作把指针中保存的地址所保存的内容提取出来。
如:int a,*p=a;
scanf(“%d”,p);
printf(“a=%d”,*p);
注意第一行 int *p;中的*号仅仅是为了说明p变量是一个指针变量。
第三行的*p是指把指针变量p所保存的内存单元地址中所保存的那个值输出。
如:输入2,输出a=2
需要注意的是,我们定义好一个指针变量后,原则上会跟普通变量相联系,如:
#include "stdio.h"
void main()
{
   int *p;
   int a=5;
   printf("%d %d\n",p,a);
   printf("%d %d",*p,&a);
}   


需要注意的是,虽然定义了p指针变量,但是并没有把p=&a这样的赋值,所以只能输出
输出: 1245068 5
       4394640 1245048
第一行第一个数系统分配给p的一个地址(整数),第二个是a变量的值;
第二行是p指针中保存那个内存中地址的值,此处也是系统随机给的值;后一个是系统分给a变量的地址。
#include "stdio.h"
void main()
{
   int *p;
   int a=5;
   p=&a;
   printf("%d %d\n",p,a);
   printf("%d %d",*p,&a);
} 


输出: 1245048 5
       5 1245048
第一行第一个数是指针变量保存的那个内存地址,即系统分配给a变量的内存地址,第二个数是a变量的值。
第二行第一个数是p变量所保存的那个内存地址的值,即a的值。后一个数输出的是系统分配给a变量的内存地址。
即此时:p==&a  *p==a都是成立的。

 高级指针(二级指针)的应用

所谓二级指针可以说是储存指针变量地址的指针变量, 如 int *q = &p;
关于二级指针的应用,用一个例子来说明就行了,是一个C语言查找函数,函数原型: void find1(char array[], char search, char *pa)
这个函数参数中的数组array是以\0值为结束的字符串,要求在字符串array中查找与参数search给出的字符相同的字符。如果找到,通过第三个参数(pa)返回array字符串中首先碰到的字符的地址。如果没有找到,则为pa为NULL。

依题意,实现代码如下。
    void find1(char [] array, char search, char *pa)  
    {  
        for (int i = 0; *(array + i) != '\0'; ++i)  
        {  
            if (*(array + i) == search)  
            {          
                pa == array + i;    // pa得到某元素的地址  
                break;   
            }  
        }  
    }  

这个函数实现能实现所要求的功能吗?

下面调用这个函数试试

    int main(int argc, char *argv[])  
    {  
        char str[] = "abcdefdfefde";    // 待查找的字符串  
        char a = 'd';    // 设置要查找的字符  
        char *p = NULL;    // 先置为空  
        find1(str, a, p);    // 调用函数以实现查找操作  
        if (NULL == p)  
        {  
            cout << "Not found!" << endl;  
        }  
        else  
        {  
            cout << "found it!" << endl;  
        }          
           
        return 0;  
    }  


运行结果是Not found!        Why?!

分析:  先看函数定义处:

void find1(char [] array, char search, char *pa) 

再看调用处:

find1(str, a, p);

仔细考虑此时形参结合所发生的事:array得到了数组名为str, search得到了a的值, pa得到了p的值(是NULL,而非p自身的地址)!但实参p并未得到形参pa传回的值(某元素的地址)。可见尽管使用了指针,也并没实现传址,当实参形 参都是指针时,它们也仅仅是传值——传了别人的地址,没有传回来。此时我们就可以用到二级指针

修正:
    void find2(char [] array, char search, char **ppa)  
    {  
        for (int i = 0; *(array + i) != '0'; ++i)  
        {  
            if (*(array + i) == search)  
            {          
                *ppa = array + i;  
                break;  
            }  
        }  
      
        if ('\0' == *(array + i))  
        {  
            *ppa = NULL:  
        }  
    }  



主函数的调用处改如下:
                       find2(str, a, &p);    // 调用函数以实现操作 
ppa是指向指针p的指针。
对*ppa的修改就是对指针p的修改。
注意: 不要将指针的用法照搬到指向指针的指针头上!
如:
char *pa 和 const char *pa是类型相容的;
但char **pa 和 const char **pa则类型不相容!
似乎我们找到了二级指针的应用场合,那就是用来存放指针变量地址的指针变量

 数组与指针的关系

1 一维数组与指针:

我们知道,定义一个数组时,系统将分配一个连续的内存地址,如:
int a[5]则会分配一段连续的空间分别用来表示a[0] a[1] a[2] a[3] a[4] a[5]的地址。
#include "stdio.h"
void main()
{
   int a[5],i=0;
   for(i=0;i<5;i++)
   printf("%d ",&a[i]);
   printf("\n%d",a);
} 

输出:
1245036 1245040 1245044 1245048 1245052
1245036
可以看出,for循环一次输出a数组中各项的地址,是连续的。本编译环境支持一个int变量占用四个字节。
最后输出的是数组名,因为数组名表示的是这个数组的首地址即a==&a[0]是成立的。            
此时如果用指针来表示这个地址,直接保存这个数组的首地址就可以了。如
#include"stdio.h"
void main()
{
   int a[5]={11,12,13,14,15},*p,i;
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   p=a;//或写成p=&a[0];
   printf("%d\n",*p);
   printf("%d\n",*(p+2));
   p=p+1;
   printf("%d\n",*p);
} 


2 二维数组与指针
我相信指针和数组之间的暧昧缠绵让很多C初学者很头痛吧,特别是多维数组,那真的是要了亲命,这里我给大家好好分析一下指针和多维数组之间的关系。
大家都知道一维数组名即是一个指针常量,它代表数组第一个元素的地址,我们知道一维数组的长度,那么可以通过数组名输出一维数组的所有元素:
    #include <stdio.h>

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


但是在多维数组中,数组名却是第一个数组的地址,怎么理解呢?比如说定义一个二维数组:
    int a[2][5] = {1, 2, 3, 4, 5,
                   6, 7, 8, 9, 10};

那么数组名a就是二维数组的第一行一维数组的地址,而不要错误的认为它代表的是第一行第一个元素的地址噢,那我们应该怎么正确的申明一个指向整形数组的指针呢?
int (*p)[5] = a;
它使 p 指向二维数组的第一行的一维数组(注意是指向的第一行一维数组)。

    #include <stdio.h>

    int main(void)
    {
        int i;
        int a[2][5] = {1, 2, 3, 4, 5,
                       6, 7, 8, 9, 10};
        int (*p)[5] = a;
        for( i = 0; i < 5; i++ )
            printf( "%d\n", *(*p + i) );
        return 0;
    }

上面的程序也是依次输出了1~5,这里 p 是指向二维数组的第一行地址的指针,那么 *p 即是代表第一行数组,那么也就是第一行数组的地址,我们可以再通过 *(*p + i) 输出第一行数组的各个元素。有点乱?呵呵,再仔细想想,或者直接说 a 是指向的第一行,那么 *a 就代表第一行的那个一维数组,但 *a 仍然是一个指针常量,而 **a 才是这个一维数组的第一个元素的值。


如果我们要定义一个指针可以逐个访问元素呢?下面两种方法都是正确的声明和赋值:
int *p = &a[0][0];
int *p = a[0];
第一种方法,就干脆把数组的第一行第一个元素取地址给指针变量p,这可能是最直观的方法。
第二种方法,其实原理一样,前面刚说了 a 是指向的第一行,第一行是一个一维数组,那么a[0]就是这个一维数组的第一个元素的地址。特别注意这里的 a[0] 不是一个值,而是一个地址。
    #include <stdio.h>

    int main(void)
    {
        int i;
        int a[2][5] = {1, 2, 3, 4, 5,
                       6, 7, 8, 9, 10};
        int *p = a[0];
        for( i = 0; i < 10; i++ )
            printf( "%d\n", *(p + i) );
        return 0;
    }

上面的代码就输出了二维数组中的所有元素。

    //附:这里的a[0]却输出了hello
    int main(void)
    {
        char a[2][10] = {"hello", "hi"};
        printf( "%s\n", a[0] );
        //printf( "%s\n", *a );
        //printf( "%s\n", *a + 1 ); 输出 "ello"
        //printf( "%s\n", *(a + 1) ); 输出 "hi"
        return 0;
    }

而这又是为什么呢?这里的 a[0] 仍然是一个地址,它指向的是一个字符串常量,%s 是可以打印地址的,这里跟 %d 输出值是不一样的,a[0] 指向了字符串的第一个字母的地址,一直输出到 NULL 为止。

C语言关于数组与指针内容小结

标签:

原文地址:http://blog.csdn.net/loving_forever_/article/details/51345530

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!