char *s 和char s[]的区别

char *s 和 char s[] 的区别小结  
博客分类: C语言

c教育 .


最近的项目中有不少c的程序,在与项目新成员的交流中发现,普遍对于char *s1 和 char s2[] 认识有误区(认为无区别),导致有时出现“难以理解”的错误。一时也不能说得很明白,网上也搜了一下相关文章发现一些写的比较好的,综合了一下当教育资料备用。



char *s1 = "hello";
char s2[] = "hello";



【区别所在】

char *s1 的s1,而指针是指向一块内存区域,它指向的内存区域的大小可以随时改变,而且当指针指向常量字符串时,它的内容是不可以被修改的,否则在运行时会报错。
char s2[]的s2 是数组对应着一块内存区域,其地址和容量在生命期里不会改变,只有数组的内容可以改变



【内存模型】
       +-----+     +---+---+---+---+---+---+
   s1: |  *======> | h | e | l | l | o |\0 |
       +-----+     +---+---+---+---+---+---+
       +---+---+---+---+---+---+
   s2: | h | e | l | l | o |\0 |
       +---+---+---+---+---+---+



场景一)
char *s1 = "hello";
char s2[] = "hello";
s2=s1;  //编译ERROR
s1=s2;  //OK



分析:s2其地址和容量在生命期里不能改变



场景二)
char s2[] = "hello";
char *s1 = s2;  //编译器做了隐式的转换 实际为&s2
或
char *s1 = &s2;



分析:以上两个指针复值完全等价,由于编译器会做这个隐式转换也容易导致初学者误认为 char *s 与char s[]是一回事。
      另用第二种在一些编译器甚至会报警告信息。



场景三)
char *s1 = "hello";
char s2[] = "hello";
s1[0]='a';  //×运行ERROR( 这一句好像在一些的编译器不会出错,原因待查)
s2[0]='a';  //OK



分析:运行时会报错,原因在于企图改变s1的内容,由于s1指向的是常量字符串,其内容是不可修改的,因此在运行时不会通过。而s2指向的是变量区字符串,可以修改。



场景四)
让我们来给一个指针的指针赋值,在使用某些含char**参数的函数时会用到,场景二的增强版。
    char *s1="hello";
    char s2[]="hello";
    char *s3=s2;       //★注意这句必须要★
    char **s4=&s3;   //s2(char[])要用两步才能完成赋值
    char **s5=&s1;   //s1(char*) 只需一步
    printf("s4=[%s]\n",*s4);//打印结果:s4=[hello]
    printf("s5=[%s]\n",*s5);//打印结果:s5=[hello]



分析:这个例子应当说最能反映出char *与char []的差异,但是由于使用场合不多,新人尤其需要注意。



下面是一些char *s1 和 char s2[]相同的地方(同样编译器对char[]做了隐式变化):
1)作为形参完全相同
如:
   void function(char *s1);
   void function(char s1[]);



2)只读取不修改的时候
如:
    char *s1="hello";
    char s2[]="hello";
    printf("s1[1]=[%c]\n",s1[1]);   //s1[1]=[e] 
    printf("s2[1]=[%c]\n",s2[1]);   //s2[1]=[e] 
    printf("s1=[%s]\n",s1);         //s1=[hello]
    printf("s2=[%s]\n",s2);         //s2=[hello]

看一个简单例子:

char p;

这个变量p是个指针变量;就是说,p这个变量里能存储一个char类型的变量的首地址。

关于首地址,请看我在“数组、指针”帖子中的回复。

结论是,char
占用4B。

另外,楼主谈到的(char ),一定有上下文关系,请将有关程序完整的帖出来再问,否则,无法回答你的问题。

一般来说,(char
)是C语言中的“强制类型转换”的语法形式。

指针对于初学的程序员来说,是一个很难理解的问题。什么叫指针呢?一句话讲,就是指向内存地址的一个变量。这样一说,朋友们根本不懂是什么意思。我来上一张图,好帮助大家理解。

首先说一下格式。

定义格式: 类型 *指针名;

如: int *p;

这里的*p 是代表我们定义的变量是指针类型。

【C语言】指针的应用 - Kam - KamのSpace

【C语言】指针的应用 - Kam - KamのSpace

指针是比较重要也是比较难明白的。所以我们再怎么迷糊,都要记住:指针是用于指向地址的!

下面我将曹sir的文章搬过来,希望对各位有帮助。


一、 指针基础知识入门
1. 内存地址与指针
    在计算机中,所有的数据都是存放在存储器中的。 一般把存储器中的一个字节称为一个内存单元, 不同的数据类型所占用的内存单元数不等,如整型量占2个单元,字符量占1个单元等。为了正确地访问这些内存单元, 必须为每个内存单元编上号。 根据一个内存单元的编号即可准确地找到该内存单元。内存单元的编号也叫做地址。 既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。
    内存单元的指针和内存单元的内容是两个不同的概念。对于一个内存单元来说,单元的地址即为指针, 其中存放的数据才是该单元的内容。在C语言中, 允许用一个变量来存放指针,这种变量称为指针变量。因此, 一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。
    如我们在定义一个int a=5;时,内存将为a变量分配一个内存,假定分配了一个内存编号为2000的内存单元,那么表示的是在内存单元编号为2000的那个地方,存放着一个内容为5的变量值。只不过,我们平常用的时候,只关心5这个值,而不关心2000这个内存编号罢了,我们只需要知道定义一个变量,系统会给我们分配一定的内存空间,至于是哪个内存空间,就不需要计较罢了。而现在引入指针变量后,我们除了需要知道值,还可以去计较这个内存编号,用一个指针变量的保存这个内存地址。
2. 指针变量定义与赋值
对指针变量的类型说明包括三个内容:
(1)指针类型说明,即定义变量为一个指针变量; 
(2)指针变量名;
(3)变量值(指针)所指向的变量的数据类型。
  其一般形式为: 类型说明符 *变量名; 
  其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指针变量所指向的变量的数据类型。
如:int *p; 表示定义了一个指针类型的变量,这个指针变量的值应该是可以用来存放整数型变量的内存地址的。
main()
{
   int *p;
   int a=5;
   p=&a;
   printf("%d %d",p,a);
} 
程序说明:
先定义了一个指针型变量。
再定义了一个整型变量。
把整型变量的地址赋值给指针变量。
最后把这个地址和a变量的值输出。 
程序也可以这样写:
main()
{
   int a=5,*p=&a;
   printf("%d %d",p,a);
}
这样称为变量赋初值。
不允许把一个数赋予指针变量,故下面的赋值是错误的: int *p;p=1000; 被赋值的指针变量前不能再加“*”说明符,如写为*p=&a 也是错误的。
3. 指针变量运算
(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 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);
} 
本例通过C/C++试验系统编译。
输出:
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);
} 
本例通过C/C++试验系统编译。

输出:
11 12 13 14 15  //for循环输出整个数组的值
11  //输出a[0]的值
13  //输出a[2]的值
12  //输出a[1]的值

程序功能:完成数组的输入输出.   
本例通过C/C++试验系统编译。
#include"stdio.h"
void main()
{
   int a[3],i=0;
   //读入数据到数组中
   for(i=0;i<3;i++)
     scanf("%d",&a[i]);
   //输出数组数据
   for(i=0;i<3;i++)
     printf("%d ",a[i]);
} 
#include"stdio.h"
void main()
{
   int a[3],i=0;
   int *p=a;
   //读入数据到数组中
   for(i=0;i<3;i++) scanf("%d",p++);
   p=a;  
   //输出数组数据
   for(i=0;i<3;i++)
     printf("%d ",*p++);
} 
#include"stdio.h"
void main()
{
   int a[3],i=0;
   int *p=a;
   //读入数据到数组中
   for(i=0;i<3;i++,p++)
     scanf("%d",p);
   p=a;  
   //输出数组数据
   for(i=0;i<3;i++,p++)
     printf("%d ",*p);
}
注意p++的作用:当p目前保存的是&a[0],即p指向数组的第0号单元时,执行一次p++,可以使p指针指向数组的第1号单元,即此时p保存的是&a[1].
再分析一下下面一个程序的结果
#include"stdio.h"
void main()
{
   int a[3],i=0;
   int *p=a,*q;
   q=p;
   //读入数据到数组中
   for(i=0;i<3;i++,p++)
     scanf("%d",p);
   p=a;  
   //输出数组数据
   for(p;p-q<3;p++)
     printf("%d ",*p);
   printf("\n");
   while(--p>=q)
     printf("%d ",*p);
} 随时注意p,q指针的变化

本例通过C/C++试验系统编译。
输入:1 2 3
输出:1 2 3
      3 2 1 
  设有实数组a,指向a的指针变量为pa,可以得出有以下关系:int a[5],*pa=a; 则
pa,a,&a[0]均指向同一单元,它们是数组a的首地址,也是0 号元素a[0]的首地址。pa+1,a+1,&a[1]均指向1号元素a[1]。类推可知pa+i,a+i,&a[i]指向i号元素a[i]。
特别应该说明的是pa是变量,而a,&a[i]都是常量。即我们可以完成pa++或pa=pa+2,但是不能执行a=a+1这样的操作。

三、 函数与指针
函数中,我们强调了值传递和地址传递两种情况,考虑到指针是一个地址,如果实参使用的是指针类型,则属于地址传递的情况。如:
#include"stdio.h"
void change(int *m,int *n)
{
    int temp;
    while(*m!=0 && *n!=0)
    {
      if(*m>*n)
      {
       temp=*m;*m=*n;*n=temp;
      } 
      m++;n++;
    }
}
void main()
{
   int a[6]={10,13,5,122,51,0},*p=a,i;
   int b[6]={1,43,6,17,99,0};   
   int *q=b;
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   for(i=0;i<5;i++)
     printf("%d ",b[i]);
   printf("\n");
   change(p,q);
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   for(i=0;i<5;i++)
     printf("%d ",b[i]);     
} 

#i nclude "stdio.h"
void change(int *m,int *n)
{
    int temp;
    if(*m>*n)
    {
     temp=*m;*m=*n;*n=temp;
    } 
}
void main()
{
   int a[6]={10,13,5,122,51,0},*p=a,i;
   int b[6]={1,43,6,17,99,0};   
   int *q=b;
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   for(i=0;i<5;i++)
     printf("%d ",b[i]);
   printf("\n");
   for(i=0;i<6;i++)
    {
      change(p,q);
      p++;q++;
    }
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   for(i=0;i<5;i++)
     printf("%d ",b[i]);     
}

#include"stdio.h"
void change(int *m,int *n,int k)
{
    int temp,i=0;
    while(i<k)
    {
      if(*m>*n)
      {
       temp=*m;*m=*n;*n=temp;
      } 
      m++;n++;
      i++;
    }
}
void main()
{
   int a[6]={10,13,5,122,51,0},*p=a,i;
   int b[6]={1,43,6,17,99,0};   
   int *q=b;
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   for(i=0;i<5;i++)
     printf("%d ",b[i]);
   printf("\n");
   change(p,q,6);
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   for(i=0;i<5;i++)
     printf("%d ",b[i]);     
}

三个程序相同结果,输出
10 13 5 122 51
1 43 6 17 99
1 13 5 17 51
10 43 6 122 99

原文链接: https://www.cnblogs.com/qmfsun/p/4575233.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/217669

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月13日 上午9:53
下一篇 2023年2月13日 上午9:54

相关推荐