• 「C」 数组、字符串、指针


    一、数组

    (一)数组

      概念:用来存储一组数据的构造数据类型

      特点:只能存放一种类型的数据,如全部是int型或者全部是char型,数组里的数据成为元素。 

    (二)数组的定义

      格式: 类型 数组名[元素个数];

      举例:存储5个人的年龄

      int agrs[5];  // 在内存中开辟4x5=20个字节的存储空间

      可以在定义数组的同时对数组进行初始化:

      int ages[5] = {17,18,19,20,21};

      遍历数组:

      for(int i = 0;i<5;i++)

      {

        printf(“ages[%d]=%d ”,i,ages[i]);

      }

      注意:

      (1)数组的初始化

        ①. int ages[5] = {17,18,19,20,21}; // 一般写法

        ②. int ages[5] = {17,18}; // 只对前两个元素赋值

        ③. int ages[5] = {[3]=10,[4]=11}; // 对指定的元素赋值,这里为第三个和第四个

        ④. int ages[] = {11,12,13}. // 正确,右边的元素确定,则个数可以省略这里为3个。

        ⑤. int ages[]; // 错误,编译器无法知道应该分配多少的存储空间

        ⑥. int ages[5];ages = {17,18,19,20,21}; // 错误,只能在定义数组时这样进行初始化

        ⑦. int ages[‘A’] = {1,2,3}; // 正确,相当于是ages[65]

        ⑧. int count = 5;int ages[count]; // 如果不进行初始化,则这种写法正确,编译器不会报错为其分配20个字节的存储空间,ages[0]=1;ages[1]=2;可以像这样对数组的元素进行赋值,但是2,3,4等元素的值时不确定的。

        ⑨. 而int count=5;int ages[count]={1,2,3,4,5}; // 这种写法是错误的,在定义数组时对数组进行初始化,元素的个数必须为常量或者不写,不能是一个变量

      (2)计算数组元素

        当没有表明数组元素个数时,如何对其进行遍历(要求使用数组元素个数)?

        可以使用sizeof运算符来计算数组元素的个数

        int count=sizeof(ages)/sizeof(int); // 数组的总长度除以单个的长度等于元素个数

    (三)数组内存存储细节

      假设有数组如下:

      int x[] = {1,2};

      char ca[5] = {‘a’,‘A’,‘B’,‘c’,‘D’};

      数组名即代表数组的地址,数组的地址==数组名(ca)==数组的首元素的地址&ca[0]

      在内存中,内存从大到小进行寻址,为数组分配了存储空间后,数组的元素自然的从上往下排列存储,整个数组的地址为首元素的地址。

      模拟该数组的内存存储细节如下:

       

      注意:字符在内存中是以对应Ascii值的二进制形式存储的,而非上表的形式。

      在这个例子中,数组x的地址为它的首元素的地址0x08,数组ca的地址为0x03。 

    (四)数组-传址调用

      void change(int array[])  // 数组可以作为函数的形参,可以省略数组元素的个数

      {

        array[0] = 100;

      }

      void change2(int a)  //基本类型作为函数的形参

      {

        a = 200;

      }

      int main()

      {

        int ages[5] = {1,2,3,4,5};

        change2(ages[0]);

        change(ages);

        return 0;

      }

      array数组与ages数组的地址一致,若以数组作为函数的参数,这种传递方式是传址调用,传递的是整个数组的地址,修改形参数组元素的值,就是修改实参的值。

    当你把一个数组当做参数来传递时,它会看做是一个指针,在该函数体内使用sizeof运算符来计算数组的长度,得出的数值永远为8,而非数组的实际长度,因为任何类型的指针都占8个字节的存储空间。

      提示:数组作为一个函数的参数时,如果函数体涉及到数组遍历等操作,通常把数组的实际元素个数也作为参数传递给函数。

      如 void maxofarray(int array[],sizeof(ages)/sizeof(int)){....} 

    (五)二维数组

      int ages[50]; // 数组能够存放50个int类型的数据

      int ages1[3][10]; // 数组能够存放3个数组,每个数组存放10个数值,共3x10=30个述职数值。

      一个二维数组a,a包括两个一维数组a[0]和a[1],每个一维数组都包括三个元素。

      使用场合:五子棋,俄罗斯方块等,

      假设:

      char Y[3][2]={

      {‘A’,‘B’},

      {‘c,‘D’},

      {‘E,‘f’}

      };

      内存情况:

       

    二、字符串

    (一)字符串基础

      注意:字符串一定以结尾。

      printf(“yang ”);

      其中yang为字符串常量,“yang”=‘y’+‘a’+‘n’+‘g’+‘’。字符串由很多的字符组成,通常使用字符数组来存储字符串,如char name[10] = “yang”;也可以以printf(name);的形式输出,即通过数组来访问字符串,但会有警告。因为默认情况下,printf函数只接受字符串常量作为参数(对变量并未写明)。

      字符串的三种写法:

    1. char name[8] = “yang”;//数组占用了8个字节的存储空间,但是只含有5个字符。
    2. char name[8] = {‘y’+‘a’+‘n’+‘g’+‘o’};
    3. char name[8] = {‘y’+‘a’+‘n’+‘g’+‘0’};

      这三种写法在内存中的表现都是一样的。

       

      char name[] = {‘y’+‘a’};前面不写个数,不是一个字符串,只能说是一个普通的字符数组。

      char name[] = “yang”;

      name[1] = ‘o’;把字符串的第二个元素值由a改成o。

    (二)字符串使用注意点

      (1)分析代码,了解的作用。

        char name[]=“yang”;

        char name2[]={‘o’+‘k’};

        printf(“name2=%s”,name2);

        %s:根据右边的参数,打印字符串(遇到为止)

        上面代码的打印结果为:okyang

        下面是内存情况分析:

         

        问1:char name[]=“yng”;则打印结果为什么?(oky)

        问2:此时打印name的值,使用%s是多少?Yng还是y?

      (2)strlen函数

        strlen函数计算字符串的长度(字符数)但不包括,是字符数不是字数。比如一个汉字占三个字符。

        strlen(“haha”);//长度为4

        strlen(“哈haha”);//长度为7而不是5

      设

        char name[]=“itcast”;

        strlen(name);值为2,因为strlen从字符串的地址开始计算,直到遇到为止。

      假设

        char name[]=“itcast”;

        char name2[]={‘o’+‘k’};

        int size=strlen(name);

        此时size的值为8。

      (3)练习,编写一个函数char_contains(char str[],char c),如果字符串中包含字符c,则返回1,否则返回0。

        int char_contains(char str[],char c)

        {

          //遍历整个字符串

          for(int i=0,i<strlen(str);i++)

          {

            if(str[i]==c)

            return 1;

          }

          return 0;

        }

        //调用语句

        int result=char_contains(“yang”,‘a’);

        //使用while循环

        ①. while(i<strlen(str))

        ②. while(str[i]!=‘’)

        ③. while(str[i])

        ④. int i=-1;while(str[i++])

    (三)字符串数组

      二维字符数组,存储两个字符串数组,每个的长度为1,下面是两种写法但存储情况是一样的。

      char name[2][10]={“jack”,“rose”};

      char name2[2][10]={

      {‘j’+‘a’+‘c’+‘k’+‘’},

      {‘r’+‘o’+‘s’+‘e’+‘’}

      }

      把rose输出:printf(“%s”,name2[1]);

      输出k:printf(“%c”,name2[0][3]);

    三、指针

      前导程序

     

    复制代码
     1 #include<stdio.h>
     2 
     3 void change(int *n);
     4 
     5 int main()
     6 
     7 {
     8 
     9     int a = 90;
    10 
    11     change(&a);
    12 
    13     printf("a = %d
    ",a);
    14 
    15     return 0;
    16 
    17 }
    18 
    19 void change(int *n)
    20 
    21 {
    22 
    23     *n = 10;
    24 
    25 } 
    复制代码

     

    (一)基本知识点

      int a = 10;

      int *p; // 定义一个int类型的指针

      p = &a; // 指针变量p指向了变量a

      *p = 20; // 使用指针不通过变量直接修改变量a的值为20

      *p表示访问指针变量p指向的存储空间

      指针一个作用:能够根据一个地址值,访问(取值 | 赋值)对应的存储空间

      指针变量p前面的int,表示指针的类型

        ①. int *p;

        ②. *p = 10;

      两个*的区别:前一个起标识作用,表明定义的p是一个指针,后者的*表示通过访问p指向的地址空间

    (二)指针使用注意

      ①. int *p;

        double d = 10.0;

        p = &d; // 不建议此种做法

      ②. int *p;

        p = 200; // 指针变量只能存储地址

      ③. int *p;

        printf(“%d ”,*p); // 指针变量未经初始化,不要拿来间接访问其他的存储空间

      ④. int *p = &a;但是不能写成 int *p;*p=&a;这种写法没有任何的意义,可以认为*是和类型符一起使用的。

      ⑤. *是指针运算符,访问指针指向的空间

     

    复制代码
     1 #include <stdio.h>
     2 
     3 int main()
     4 {
     5     /* 不建议写法,int *p只能指向int 类型的数据
     6     int *p ;
     7     
     8     double a = 10.0;
     9     
    10     p = &a ;
    11      */
    12     
    13     /* 指针变量只能存储地址
    14      int *p;
    15      p = 200;
    16      */
    17     
    18     /* 指针变量未经过初始化,不要间接拿来访问其他存储空间
    19     int *p;
    20     
    21     printf("%d
    ",*p );
    22      */
    23     
    24     int a = 10;
    25     //定义变量时的*仅仅是一个象征,没有其他特殊含义
    26     int *p= &a;
    27     
    28     //不正确的写法
    29     //*p = &a;
    30     
    31     //这个时候* 的作用: 访问指向变量p的存储空间
    32     *p = 20;
    33     
    34     char c = 'A';
    35     char *cp = &c;
    36     *cp = 'D';
    37     printf("%c
    ",*cp);
    38     
    39     return 0;
    40 }
    复制代码

     

    (三)指向指针的指针

      int a = 10;

      int *p = &a; // 指向int型的指针

      int **p1 = &p; // 指向指针的指针

      int ***p2 = &p1; // 三级指针

       

      *p2相当于访问p1;

      **p2相当于访问p;

      ***p2相当于访问a;

      *p1相当于访问p;

      一颗星一条线。

    (四)指针练习

      编写一个函数,计算a和b的和与差(一个函数返回两个值)

      提示:指针的作用之一:实现让函数拥有多个返回值

     

    复制代码
     1 #include<stdio.h>
     2 
     3 int SumAndMinus(int n1,int n2,int *n3)
     4 
     5 {
     6 
     7    *n3 = n1 - n2;
     8 
     9    return n1 + n2;
    10 
    11 }
    12 
    13 int main()
    14 
    15 {
    16 
    17    int a = 10;
    18 
    19    int b = 11;
    20 
    21    int sum;
    22 
    23    int minus;
    24 
    25    sum = SumAndMinus(a,b,&minus);
    26 
    27    printf("和 = %d,差 = %d
    ",sum,minus);
    28 
    29 }
    复制代码

     

    (五)有关指针的疑问

      注意:任何类型的指针都占据8个字节的存储空间,那么为什么还要为指针加上类型呢?

      对下面一段代码进行内存分析,可以证明指针类型不正确带来的严重后果。

      int i = 2;

      char c = 1;

      int *p = &c; // 本应该是char类型的,写成了int类型

      printf(“c的值是%d ”,*p); // 打印结果为513,而非1

      printf(“c的值是%d ”,c); // 值为1

      下面是上述代码的结果的内存分析:

       

      指针p访问的本应该是1个字节空间的数据,此时因为指针的类型是int型的,因此程序自然的从指向的地址0x0a开始读取了4个字节的数据,访问的数据从1变成了513。

      提示:明确了指针的数据类型,指针才能够正确的访问应该访问的空间数据。

    (六)指针和数组

      int ages[5] = {10,9,8,7,6};

      遍历数组

      for(int i = 0;i < 5;i++)

        printf(“%d ”,ages[i]);

      使用指针遍历数组

      int *p;

      p = ages;//也可以写成p = &ages[0];,指针变量p指向了数组的首元素

      元素的地址:

        第一个元素的地址p    &ages[0]

        第二个元素的地址p+1    &ages[1]

        第三个元素的地址p+2    &ages[2]

      元素的值

        *p     ages[0]

        *(p+1) ages[1]

        *(p+2) ages[2]

      把指针当做数组来用:

      for(int i = 0;i < 5;i++)

        printf(“%d ”,*(p+i));

      (1)数组元素的三种访问形式:

        ①. 数组名[下标]

        ②. 指针变量名[下标]

        ③. *(p+1)

      (2)指针变量的+1究竟是加多少?这取决于指针的类型,如果是char类型则加1个字节,如果是int类型的,则加4个字节。

      (3)利用指针来接收一个数组,指针变量指向了数组的首元素。

        void change(int array[])等价于void change(int *array)。

        前者存储的虽然是数组元素的首地址,但是在传递时就已经变成指针了。

        示例:

          void change(int *array)

          {

            //printf(“%d ”,array[2]);

            printf(“%d ”,*(array+2));

          }

          int main()

          {

            int ages[5] = {1,2,3,4,5};

            change(ages);

          }

        调用的结果为:数组的第三个元素3

        若改给change(&ages[2]);则调用的结果为5,因为此时array指向的是ages[2],把ages[2]当做了array的首元素 

    (七)指针和字符串

      (1)基础知识

        下面两行代码有着本质的区别:

          ①. char name[] = “it”;

          ②. char *name2 = “it”;//指针变量name2指向了字符串的首字符i

     

          char name[0] = ‘y’;//改变第一个元素的值

          printf(“%s ”,name);//打印结果为yt

     

           *name2 = ‘y’;

          printf(“%s ”,name2);//此时程序崩溃

          这是因为,两者一个是字符串变量,一个是字符串常量。c语言的数组元素存放于栈,里面的元素可以随便修改,称为字符串变量。而字符串常量存放于常量区,会缓存起来是不可更改的。

          char *name1 = “it”;

          char *name2 = “it”;

          printf(“%p %p”,name1,name2); // 地址是一样的,说明name1和name2指向的是同一个字符串。

          掌握字符串定义的两种方式:

            ①. 利用数组

              特点:字符串里边的字符是可以修改的,适用于内容需要经常修改时。

            ②. 利用指针

              特点:其实是一个常量字符串,里面的字符不能修改,适用于字符串的内容不需要修改,且这个字符串经常被使用时。

      (2)指针数组

        整型数组:这个数组中存放的都是整型数组

        指针数组:这个数组中存放的都是指针

          int ages[5];

          char *name[5] = {“jack”,“rose”,“yang”}; // 字符串数组的常见写法

        对应于:

          char name2[3][10] = {“jack”,“rose”,“yang”};

        保存字符串数组的两种方式:

          ①. 指针数组(字符串数组)

          ②. 二维字符数组(字符串数组)

        如何输入字符串?(使用数组——因其可变)

        int main()

        {

          char name[20];

          printf(“请输入姓名: ”);

          scanf(“%s”,name);

          printf(“%s”,name);

        }

    (八)返回指针的函数

      程序示例:

     

    复制代码
     1 #include<stdio.h>
     2 
     3 char *test();
     4 
     5 int main()
     6 
     7 {
     8 
     9     char *name = test();
    10 
    11     printf(“name = %s
    ”,name);
    12 
    13     return 0;
    14 
    15 }
    16 
    17 char *test() // 返回指针的函数
    18 
    19 {
    20 
    21     return “rose”;
    22 
    23 }
    复制代码

     

    (九)指向函数的指针

      数组名即数组的地址,函数名即函数的地址。

      假设有函数:

      void test ()

      {

        printf(“调用了test函数 ”);

      }

      void (*p)();  // void指针变量指向的函数没有返回值,()表示p指向的函数没有形参

      p = test; // 有指针p,把指针p指向函数

      有三种方式可以操纵函数:

        ①. 直接调用test();

        ②. 利用指针变量简介调用  (*p)();

        ③. 简化使用p()

      练习:

      假设有函数声明为 int sum(int a,int b)

      则相对应的指向该函数的指针应该定义为:int (*p)(int ,int);

      把指针变量p指向函数:p = sum;

      调用该函数的三种方式:

      (1)int c = p(10,12);

      (2)int c = sum(10,12);

      (3)int c = (*p)(10,12);

     

      假设函数声明为:double haha(double a,char *b,int c);

      则定义一个指向haha函数的指针应该为:double (*p)(double,char *,int) = haha;

    如果一件事情你觉得难的完不成,你可以把它分为若干步,并不断寻找合适的方法。最后你发现你会是个超人。不要给自己找麻烦,但遇到麻烦绝不怕,更不要退缩。 电工查找电路不通点的最快方法是:分段诊断排除,快速定位。你有什么启示吗? 求知若饥,虚心若愚。 当你对一个事情掌控不足的时候,你需要做的就是“梳理”,并制定相应的规章制度,并使资源各司其职。
  • 相关阅读:
    使用CSS3实现超炫的Loading(加载)动画效果
    三种简洁的经典高效的DIV+CSS制作的Tab导航简析
    Span和Div的区别
    [总结]Jquery api 快速参考
    25个可遇不可求的jQuery插件
    基于单个 div 的 CSS 绘图
    一款基于jQuery的图片场景标注提示弹窗特效
    HTML5手机开发——滚动和惯性缓动
    发布一个高效的JavaScript分析、压缩工具 JavaScript Analyser
    CSS框架BluePrint
  • 原文地址:https://www.cnblogs.com/wvqusrtg/p/4499346.html
Copyright © 2020-2023  润新知