• 字符串的指针和指向字符串的指针变量


      引自:http://s319.dlut.edu.cn/educ/83.htm

    §8.4 字符串的指针和指向字符串的指针变量

    8.4.1 字符串的表现形式

    C程序中,可以用两种方法实现一个字符串。

    1. 用字符数组实现。

    [8.11]

    void main(void)

    {static char string [ ]="I Love China!";

    printf("%s\n",string);

    }

    运行时输出: I Love China!

    和前面介绍的数组属性一样,string是数组名,它代表字符数组的首地址,(见图8.17)。string[4]代表数组中序号为4的元素(v),实际上string[4]就是*(string+4)string+4是指向字符“v”指针。

    2. 用字符指针实现。

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

    [8.12]

    void main(void)

    {char *string="I Love China!";

    printf("%s\n",string);

    }

    在这里没有定义字符数组,但C语言对字符串常量是按字符数组处理的,实际上在内存开辟了一个字符数组用来存放字符串数组。在程序中定义了一个字符指针变量string。并把字符串首地址(即存放字符串的字符数组的首地址)赋给它(见图8.18)。有人认为string是一个字符串变量,以为定义时把"I Love China!"赋给该字符串变量,这是不确切的。定义string的部分:

    char *string="I Love China!";

    等价于下面两行:

    char *string;

    string="I Love China!";

    可以看到:string被定义为一个指针变量,它指向字符型数据,请注意只能指向一个字符变量或其它字符类型数据,不能同时指向多个字符数据,更不是把"I Love China!"这些字符存放到string中。只是把"I Love China!"的首地址赋给指针变量string(不是把字符串赋给*string)。因此不要认为上述定义行等价于:

    char *string;

    *string="I Love China!";

    在输出时,用

    printf("%s\n",string);

    %s表示输出一个字符串,给出字符指针变量名string,则系统先输出它所指向的一个字符数据,然后自动使string1,使之指向下一个字符,然后再输出一个字符,……,如此直到遇到字符串结束标志‘\0’为止。注意,在内存中,字符串的最后被自动加了一个‘\0’(如图8.18所示),因此在输出时能确定字符串的终止位置。

    通过字符数组名或字符指针变量可以输出一个字符串。而对一个数值型数组,是不能企图用数组名输出它的全部元素的。如:

    int i[10]

    :

    printf("%d\n",i);

    是不行的,只能逐个元素输出。显然,可以把字符串看作为一个整体来处理,可以对一个字符串进行整体的输入输出。

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

    [8.13]将字符串a复制得字符串b

    void main(void)

    {char a[ ]= "I am a boy. ",b[20];

    int i;

    for(i=0;*(a+i)!=‘\0’;i++)

    *(b+i)=*(a+i);

    *(b+i)=‘\0’;

    printf("string a is:%s\n",a);

    printf("string b is: ");

    for(i=0;b[i]!=‘\0’;i++)

    printf("%c",b[i]);

    printf("\n");

    }

    程序运行结果为:

    string a is:I am a boy.

    string b is:I am a boy.

    c817.jpg (28464 bytes)

    程序中ab都定义为字符数组,可以用地址方法表示数组元素。在for语句中,先检查a[i]是否为’\0’(今a[i]是以*(a+i)形式表示的)。如果不等于’\0’,表示字符串尚未处理完,就将a[i]的值赋给b[i],即复制一个字符。在for循环中将a串全部复制给了b串。最后还应将’\0’复制过去,故有:*(b+i)=‘\0’;

    此时的i的值是字符串有效字符的个数n1。第二个for循环中用下标法表示一个数组元素(即一个字符)。也可以设指针变量,用它的值的改变来指向字符串中的不同的字符。

    [8.14] 用指针变量来处理例8.13问题。

    void main(void)

    {char a[ ]= "I am a boy. ",b[20],*p1,*p2;

    int i;

    p1=a;p2=b;

    for(;*p1!=‘\0’;p1++,p2++)

    *p2=*p1;

    *p2=‘\0’;

    printf("string a is:%s\n",a);

    printf("string b is: ");

    for(i=0;b[i]!=‘\0’;i++)

    printf("%c",b[i]);

    printf("\n");

    }

    p1p2是指针变量,它指向字符型数据,先使p1p2的值分别为字符串ab的首地址。*p1最初的值为‘i’,赋值语句“*p2=*p1;”的作用是将字符’I’a串中第一个字符)赋给p2所指向的元素,直到 *p1的值为’\0’止。注意p1p2的值是不断在改变的,程序必须保证使p1p2同步移动。

    8.4.2 字符串指针作函数参数

    将一个字符串从一个函数传递到另一个函数,可以用地址传递的办法,即用字符数组名作参数或用指向字符串的指针作参数。在被调用的函数中可以改变字符串的内容,在主调函数中可以得到改变了的字符串。

    [8.15] 用函数调用实现字符串的复制。

    (1)用字符数组作参数

    void copy_string (char from[ ],char to[ ])

    {int i=0;

    while (from[i]!=‘\0’)

    {to[i]=from[i];i++;}

    to[i]=‘\0’;

    }

    void main(void)

    {char a[ ]= "I am a teacher. ";

    char b[ ]= "You are a student. ";

    printf("string_a=%s\n string_b=%s\n",a,b);

    copy_string(a,b);

    printf("\nstring_a=%s\n string_b=%s\n",a,b);

    }

    程序运行结果如下:

    string_a=I am a teacher.

    string_b=You are a student.

     

    string_a=I am a teacher.

    string_b=I am a teacher.

    ab是字符数组。初值如图8.19(a)所示。copy_string函数的作用是将from[i]赋给to[i],直到from[i]的值为’\0’为止。在调用copy_string函数时,将ab的首地址分别传递给形参数组formto。因此from[i]a[i]是同一个单元,to[i]b[i]是同一个单元。程序执行完以后,b数组的内容如图8.19(b)所示。可以看出,由于b数组原来的长度大于a数组,因此在a数组复制到b数组后,未能全部覆盖b数组原有内容。b数组最后三个元素仍保留原状。在输出b时由于按%s(字符串)输出,遇’\0’即告结束,因此第一个’\0’后的字符不输出。如果不采取%s格式输出而用%c逐个字符输出是可以输出后面这些字符的。

    main函数中也可以不定义字符数组,而用字符型指针变量。main函数可改写如下:

    void main(void)

    {char *a=“I am a teacher.”;

    char *b= “You are a student.”;

    printf(“string_a=%s\n

    string_b=%s\n”,a,b);

    copy_string(a,b);

    printf(“\nstring_a=%s\n

    string_b=%s\n”,a,b);

    }

    与上面程序运行结果相同。

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

    程序如下:

    void copy_string(char *from,char *to)

    {

    for(;*from!=‘\0’;from++,to++)

    *to=*from;

    *to=‘\0’;

    }

     

    void main(void)

    {char *a="I am a teacher. ";

    char *b= "You are a student. ";

    printf("string_a=%s\n

    string_b=%s\n",a,b);

    copy_string(a,b);

    printf("\nstring_a=%s\n string_b=%s\n",a,b);

    }

    c818.jpg (27303 bytes)

    形参formto是字符指针变量。它们相当于例8.19中的p1p2。算法也与例8.19完全相同。在调用copy_string时,将数组a的首地址传给from,把数组b的首地址传给to。在函数copy_string中的for循环中,每次将*from赋给*to,第1次就是将a数组中第1个字符赋给b数组的第1个字符。在执行from++to++以后,fromto就分别指向a[1]b[1]。再执行*to=*from,就将a[1]赋给b[1],……。最后将’\0’赋给*to,注意此时to指向哪个单元。

    (3)copy_string函数还可作简化

    1. copy_string函数改写为:

    void copy_string (char *from,char *to)

    {

    while ((*to=*from)!=‘\0’)

    {to++;from++;}

    }

    请与上面一个程序对比。在本程序中将“*to=*from;”的操作放在while语句的表达式中,把赋值运算和判断是否为’\0’的运算放在表达式中,先赋值后判断。在循环体中tofrom增值,指向下一个元素,……,直到*from的值为’\0’为止。

    2. copy_string函数的函数体还可改为:

    {

    while((*to++=*from++)!=‘\0’);

    }

    把上面程序的to++from++运算与*to=*from合并,它的执行过程是:先将*from赋给*to,然后使tofrom增值。显然这又简化了。

    3. 函数体还可写成:

    {

    while(*from!=‘\0’)

    *to++=*from++;

    *to=‘\0’;

    }

    *from不为’\0’时,使*from赋给*to,然后使tofrom增值。

    字符可以用其ASCII代码来代替。例如:”ch=‘a’”可以用”ch=97”代替,”while(ch!=‘a’)”可以用”while(ch!=97)”代替。因此,”while(*from!=‘\0’)”可以用”while(*from!=0)代替(’\0’ASCII代码为0)。而关系表达式”*from!=0”又可简化为”*from”,这是因为若*form的值不等于0,则表达式”*from”为真,同时”*from!=0”也为真。因此”while(*from!=0)””while(*from)”是等价的。所以函数体可简化为:

    {while(*from)

    *to++=*from++;

    *to=‘\0’;}

    4. 上面的while语句还可以进一步简化为下面的while语句:

    while(*to++=*from++)

    它与下面语句等价:

    while((*to++=*from++)!=‘\0’);

    *from赋给*to,如果赋值后的*to值等于’\0’,则循环终止(’\0’已赋给*to)。

    5. 函数体中while语句也可以改用for语句:

    for(;(*to++=*from++)!=0;);

    for(;*to++=*from;);

    6. 也可用指针变量,函数copy_string可写为:

    void copy_string( char from[ ],char to[ ])

    {char *p1,*p2;

    p1=from;p2=to;

    while((*p2++=*p1++)!=‘\0’);

    }

    以上各种用法,变化多端,使用十分灵活,初看起来不太习惯,含义不直观。初学者会有些困难,也容易出错。但对C熟练之后,以上形式的使用是比较多的,读者应逐渐熟悉它,掌握它。

    归纳起来,作为函数参数,有以下几种情况:

    1.

    2. 字符指针变量

    3. 字符指针变量 字符指针变量

    4. 字符指针变量

    8.4.3 字符指针变量与字符数组

    虽然用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点:

    1. 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串的首地址),决不是将字符串放到字符指针变量中。

    2. 赋初值的方式。对数组赋初值要用static存储类别,如

    static str[ ]={ "I love China! ");

    而对字符指针变量不必加static存储类型,如

    char *a="I love China! ";

    这是因为并没有对数组初始化,只是对指针变量初始化。

    3. 赋值方式。对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。

    char str[14];

    str="I love China! ";

    而对字符指针变量,可以采用下面方法赋值:

    char *a;

    a="I love China! ";

    但注意赋给a的不是字符,而是字符串的首地址。

    4. 赋初值时,对以下的变量定义和赋初值:

    char *a="I love China! ";

    等价于:

    char *a;

    a="I love China! ";

    而对数组初始化时:

    static char str[14]={ "I love China! "};

    不是等价于

    char str[14];

    str[ ]= "I love China! ";

    即数组可以在变量定义时整体赋初值,但不能在赋值语句中整体赋值。

    5. 在定义一个数组时,在编译时即已分配内存单元,有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个地址值,也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋以一个地址位,则它并未具体指向哪一个字符数据。如:

    char str[10];

    scanf("%s",str);

    是可以的,而常有人用下面的方法:

    char *a;

    scanf("%s",a);

    目的是输入一个字符串,虽然一般也能运行,但这种方法是危险的,不宜提倡。因为编译时虽然分配给指针变量a一个单元,a的地址(即&a)是已指定了,但a的值并未指定,在a单元中是一个不可预料的值。因此在scanf函数中要求将一个字符串输入到a的值(地址)开始的存储区(这是好的情况),也有可能指向已存放指令或数据的内存段,这就会破坏了程序,甚至会造成严重的后果。在程序规模小时,由于空白地带多,往往可以正常运行,而程序规模大时,出现上述“冲突”的可能性就大多了。应当这样:

    char *a,str[10];

    a=str;

    scanf("%s",a);

    先使a有确定值,也就是使a指向一个数组的开头,然后输入字符串到该地址开始的若干单元中。

    6. 指针变量的值是可以改变的,如:

    [8.16]

    void main(void)

    {char *a="I love China! ";

    a=a+7;

    printf("%s",a);

    }

    运行结果如下:

    China!

    指针变量a的值可以变化,输出字符串从a当时所指向的单元开始输出各个字符,直到遇’\0’为止。而数组名虽然代表地址,但它的值是不能改变的。下面是错的:

    char str[ ]={ "I love China! "};

    str=str+7;

    printf("%s",str);

    需要说明:若定义了一个指针变量,使它指向一个字符串后,可以用下标形式引用指针变量所指的字符串中的字符。如:

    [8.17]

    void main(void)

    {char *a="I LOVE CHINA. ";

    int i;

    printf("The sixth charcter is %c\n",a[5]);

    for(i=0;a[i]!=‘\0’;i++)

    printf("%c",a[i]);

    }

    运行结果如下:

    The sixth charcter is E

    I LOVE CHINA.

    程序中虽然并未定义数组a,但字符串在内存中是以字符数组形式存放的。a[5]*(a+5)执行,即从a当前所指向的元素下移5个元素的位置,取出其单元中的值。

    7. 用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。如:

    char *format;

    format="a=%d,b=%f\n";

    printf(format,a,b);

    它相当于

    printf("a=%d,b=%f\n",a,b);

    因此只要改变指针变量format所指向的字符串,就可以改变输入输出的格式。这种printf函数称为可变格式输出函数。

    也可以用字符数组。如:

    char format[ ]= "a=%d,b=%f\n";

    printf(format,a,b);

    但由于不能采取赋值语句对数组整体赋值的形式,如:

    char format[ ];

    format="a=%d,b=%f\n";

    因此用指针变量指向字符串的方式更为方便。

    y3= 1.29

  • 相关阅读:
    session
    php增删改查,分页
    sql题
    php简单的数据增删改查
    php简单登录注册验证
    js题
    jQHTML(获取内容和属性)
    jQ效果(动画)
    Codeforces Round #460 (Div. 2): D. Substring(DAG+DP+判环)
    POJ 2891 中国剩余定理(不互素)
  • 原文地址:https://www.cnblogs.com/inspurhaitian/p/1285027.html
Copyright © 2020-2023  润新知