字符串常量

简介: 字符串常量

1.内存分布

一个编译的C程序占用的内存分为以下几个部分:

1、栈区(stack)—也称自动类型存储区,由编译器自动分配释放,存放函数的参数值,局部变量的值等,例如函数调用结束后自动释放。

2、堆区(heap)—也称动态分配内存区,由程序员分配释放,从分配到程序结束为止,若不释放,程序结束时可能由OS回收,比如malloc分配的内存,free释放的内存。

3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放。

4、文字常量区—常量字符串放在这里,程序结束后由系统释放

5、程序代码区—编译后的程序代码放在这里。

来看一个具体的C程序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Y0D7Pnk-1672642934009)(D:\Typora图片\1240.png)]

下图所示为可执行代码存储时结构和运行时结构的对照图。一个正在运行着的C编译程序占用的内存分为代码区、初始化数据区、未初始化数据区、堆区和栈区5个部分。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hn6T26Mc-1672642934010)(D:\Typora图片\20210106164806527.png)]

(1) 代码区

存放 CPU 执行的机器指令。通常代码区是可共享的(即另外的执行程序可以调用它),使其可共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。

代码区通常是只读的,使其只读的原因是防止程序意外地修改了它的指令。另外,代码区还规划了局部变量的相关信息。

总结:你所写的所有代码都会放入到代码区中,代码区的特点是共享和只读。

(2) 全局区

全局区中主要存放的数据有:全局变量、静态变量、常量(如字符串常量)

全局区的叫法有很多:全局区、静态区、数据区、全局静态区、静态全局区

这部分可以细分为data区和bss区

2.1 data区

data区里主要存放的是已经初始化的全局变量、静态变量和常量

2.2 bss区

bss区主要存放的是未初始化的全局变量、静态变量,这些未初始化的数据在程序执行前会自动被系统初始化为0或者NULL

2.3 常量区

常量区是全局区中划分的一个小区域,里面存放的是常量,如const修饰的全局变量、字符串常量等

在VS下运行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D1GOTLNM-1672642934010)(D:\Typora图片\ef2cb597dda704b22ef804ec7bdc5fd9.jpeg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FfFm5OTE-1672642934011)(D:\Typora图片\20b6c057019a2ce10f01a9b22e96e6d5.jpeg)]

总结:全局区存放的是全局变量、静态变量和常量

在程序运行后由产生了两个区域,栈区和堆区。

(3) 栈区(stack)

栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。

vs运行效果如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DieR7Zs0-1672642934012)(D:\Typora图片\2ebcc5b554ce9a53f6cb9376f97c0cef.jpeg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yJRTWvjY-1672642934013)(D:\Typora图片\aa957b05773cef5905a08a8f93dcc002.jpeg)]

(4) 堆区(heap)

堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

vs运行效果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q4hIAiGg-1672642934013)(D:\Typora图片\382928f8c803df0141546416c216a1af.jpeg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HLvwCwVM-1672642934014)(D:\Typora图片\a631d388aebc9ee52535612afeb8364a.jpeg)]

当我们把几个案例放在一起执行,就可以看到内存将每个区域划分的很有条理。每个区域互不干涉,区域中的数据地址也是非常接近的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zrYpnDbc-1672642934014)(D:\Typora图片\899fd3f84bce419046447e27cb2e517b.jpeg)]


2.字符串常量的存储

首先,毫无疑问,即使是常量(字符串常量)也是要占据空间的。

一般来说,基本类型(整型、字符型等)常量会在编译阶段被编译成立即数,占的是代码段的内存。(代码段是只读的,而且不允程序员获取代码段的地址,所以在c++中,尽量不为const分配数据段的内存,但是一旦取cosnt的地址,就不得不分配了,但是读const的时候,依然是从代码段读取那个立即数)。当然,占代码段的内存一般不在我们常说的“占内存”范围中。代码段不是寄存器。

而字符串常量或基本类型的常量数组占用的是数据段内存。

程序中但凡出现“XXX”形式,这都是代表字符串常量,是要事先存储在程序的只读数据区的。

void test(){
  char name[32] = "hello,world"; // "hello,workd" 存储在程序的只读数据区, name 存储在test函数的栈区。
}

字符串常量的存储:字符串常量使用一对双引号括起来的字符序列,与基本类型常量的存储相似,字符串常量在内存中的存放位置由系统自动安排。

由于字符串常量是一串字符,通常被看作一个特殊的一位字符数组,与数组的存储类似,字符串常量中的所有字符在内存中连续存放。所以,系统在存储一个字符串常量时先给定一个起始地址,从该地址指定的存储单元开始,连续存放该字符串中的字符。

显然,该起始地址代表了存放字符串常量首字母的存储单元的地址,被称为字符串常量的值,也就是说,字符串常量实质上是一个指向该字符串首字符的指针常量。

例如字符串"Hello"的值是一个地址,从它指定的存储单元开始连续存放该字符串的6个字符('H' 'e' 'l' 'l' 'o' '\n')

字符数组与字符指针

如果定义一个字符指针接收字符串常量的值,该指针就指向字符串的首字符,例如:

char sa[]="array ";
char *sp="point ";
printf("%s", sa); //数组名sa作为printf的输出参数
printf("%s", sp); //字符指针sp作为printf的输出参数
printf("%s \n", "string"); //字符串常量作为printf的输出参数

输出:array point string

调用printf()函数,以%s的格式输出字符串时,作为输出参数,数组名sa指针sp字符串"string"的值都是地址,从该地址所指定的单元开始连续输出其中的内容(字符),直到遇到'\0'为止。

由此可见,输出字符串时,输出参数给出的起始位置(地址)'\0'用来控制结束。因此,字符串中其它字符的地址也能作为输出参数。例如:

printf("%s", sa+2); //数组元素sa[2]的资质作为输出参数
printf("%s", sp+3); //sp+3作为起始数组
printf("%s \n", "string" +1 ); //t作为起始输出

输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xSW2SrUd-1672642934015)(D:\Typora图片\image-20230101221854877.png)]


字符数组与字符指针都可以处理字符串,但两者之间有重要区别,如:

char sa[]="This is a string";
char* sp="This is a string";

字符数组sa在内存中占用了一块连续的单元,有确定的地址,每个数组元素放字符串的一个字符,字符串就存放在数组中。

字符指针sp只占用一个可以存放地址的内存单元,而不是将字符串放到字符指针变量中去

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hIzvXXVY-1672642934015)(D:\Typora图片\watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBATGl1ZXMyMzM=,size_20,color_FFFFFF,t_70,g_se,x_16.png)]

如果要改变数组sa所代表的字符串,只能改变数组元素的内容。

如果要改变指针sp所代表的字符串,通常直接改变指针的值,让它指向新的字符串。因为sp是指针变量,它的值可以改变,转向指向其它单元。


当定义字符指针后,如果没有赋值,指针的值是不确定的,引用未赋值的指针可能出现难以预料的结果:

char* s;
scanf("%s",s);

没有对指针s赋值,却对s指向的单元赋值,如果该单元已经分配给其他变量,其值就改变了。

所以以下写法str有确定的存储单元,这才是正确的:

char* s,str[20];
s=str;
scanf("%s",s);

为了避免引用未赋值的指针造成的危害,在定义指针时,可先将它的初值置为空,如:char* s=NULL;

一般在使用指针类型后,为避免出现内存泄漏,都需要手动释放内存,如:

char*s =new char[128];
delete []s;
s = NULL;

但如果是像 const char* str 这种指针,则不需要手动释放内存。

这是因为 const char* str 定义的是一个指向常量的指针。

如果str是局部变量,则字符串会随着变量所在的函数的退出而自动释放;如果str是全局变量,则程序退出时才自动释放。

#include<stdio.h>
int main(void){
   //字符串指针
   const char* s = "Hello World!";
   printf("%s\n", s);
   //字符串数组
   char ch[] = "Hello World!";
   printf("%s\n", ch);
   //整型变量和指针
   int* i, a = 233333;
   i = &a;
   printf("%d\n", *i);
   //字符型变量和指针
   char* cha, b = 'a';
   cha = &b;
   printf("%c\n", *cha);
   return 0;
}

输出结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YfSMcio9-1672642934015)(D:\Typora图片\image-20230102092609563.png)]

在处理整型和字符型等时,printf()函数输出时指针变量前需要带上 *

而处理字符串时,输出时指针变量前则不需要* 号。

3.字符串常量内存释放问题

#include <iostream>
using namespace std;
char *str1()
{
    char *str="hello world";
    return str;
}
char *str2()
{
    char str[]="hello world"; //str[]在栈上,子函数结束后自动释放内存,返回的其实是首地址
    return str;   //拷贝后,返回和”hello world“无关,返回存在str[]中的内容
}
char *str3()
{
    static char str[]="hello world";
    return str;
}
int main()
{
    char *str=NULL;
    str=str1();
    cout<<"指针指向内存内容:"<<str<<endl;  //输出hello world
    str=str2();
    cout<<"栈内容:"<<str<<endl;    //输出乱码
    str=str3();
    cout<<"静态存储区内容:"<<str<<endl;   //输出hello world
    return 0;
}

字符串常量存放在静态存储区

str1返回指针指向内存首地址,由于字符串常量存在静态区,所以内容不变,还是“hello world”

str2将静态存储区的内容拷贝一份到栈中,由于栈在str2结束时释放栈内存,所以输出为乱码

str3返回存在静态存储区的内容,“hello world”

可以返回一个局部变量的值,也可以返回一个局部静态指针的地址,但不应该返回一个局部自动指针的地址

int get()
{
   int a=152;
   return a;    //可以正确返回
}
int *get()
{
   int a=152;
   return & a;    //无意义
}

4.字符串常量生命周期

字符串char s="hello"; 与char s[]="hello";,看似都是将hello字符串的地址赋值给指针 p。

但是前面一个表达式是字符串常量的地址赋值给指针,该指针指向的字符串中的字符是不允许被更改的。

而后面一个表达式是将该字符串的每一个字符赋值给数组,该指针指向的数组的首地址,而数组成员是变量,因此可以允许被更改赋值。

下面讨论关于字符串常量在内存中存在生命周期的的问题。

问题如下:

假如

char *s0="hello";
s0="world";

就是说如果开始s0指针指向“hello”这个字符串这个常量的地址,但是当s0指针指向了“world”符串的时候,那么“hello”这个字符串在内存中的是否会被释放掉,还是会一直存在内存中,直到程序结束。

我们不妨先试着写几行代码,通过运行结果来进行分析:

案例 一:

代码:

#include<stdio.h>
int main()
{
    char *s0,*s1,*s2;
    s0="hello";
    s1="hello";
    s2="hello";
    printf("%p\n",s0);
    printf("%p\n",s1);
    printf("%p\n",s2);
    return 0;
}

运行结果:

三个指针变量的指向的地址都是相同的,如图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TMPzvVIt-1672642934016)(D:\Typora图片\image-20230101215956565.png)]

分析:

通过运行结果我们可以看出,赋给不同字符指针的相同的字符串,所有的指针都指向了相同的地址。

但是案例一由于三个指针变量都在同一个函数,因此不能看出其生命周期,但是可以为下面的案例解释做好铺垫。

案例二:

代码:

#include<stdio.h>
char *s0="hello";
void a(){
    char *s1="hello";
    printf("%p\n",s1);
    s1="world";
}
void b(){
    char *s2="hello";
    printf("%p\n",s2);
    s2="world";
}
int main(){
    char *s3=(char *)0;
    printf("%p\n",s0);
    a();
    b();
    s3="hello";
    printf("%p\n",s3);
    return 0;
}

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ut1JAIfd-1672642934016)(D:\Typora图片\image-20230101220205924.png)]

四个字符指针变量指向的地址还是一样的。

分析:

这就说明问题了,在函数a(),b()中,局部指针变量s1,s2都指向”hello“字符串,但是在退出之前有改变指针指向其他字符串,但是两个字符串指针打印出来的地址还是一样的,说明在使用了字符串常量后,该字符串常量并没有在随着函数的结束而消失,而是依旧存在于内存中,因此当其他函数中使用一样的字符串常量时,指向的依旧是跟还是一样的地址。

但是也许有人会问到,因为有在全局字符指针变量 char *s0 ="hello";指向了该字符串常量,会像全局变量一样,在真个程序运行都不会释放,因此其他函数调使用该字符串常量时才指向了同一地址。确实确实有这种可能,那么我们就可以看下面案例三。

案例三:

#include<stdio.h>
void a(){
    char *s1="hello";
    printf("%0x\n",s1);
    s1="world";
}
void b(){
    char *s2="hello";
    printf("%0x\n",s2);
    s2="world";
}
int main(){
    char *s3=(char *)0;
    a();
    b();
    s3="hello";
    printf("%0x\n",s3);
    return 0;
}

三个字符串指针变量指向的地址还是一样的,如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IV7a3245-1672642934017)(D:\Typora图片\image-20230101220730477.png)]

分析:

当我们去掉全局指针变量后,其结果依旧是s1,s2,s3 三个指针变量指向的地址依旧是没有变。

在运行完a(),b()两个函数之后,我们再将”hello”赋值给空指针s3指针,其指向地址与s1,s2都一样的。

因此我们可以得出结论:==一旦有字符串常量在运行期间创建,就会在内存中一直保持到程序结束==,当使用相同的字符串常量的时候,不会再创建字符串常量,而是指向之前的那个。因此字符串常量是贯穿整个程序的生命周期的。


附:既然说到这里了,那就再多说一点。

案例:当将 char* s0 改为char s0[ ]时。

代码:

#include<stdio.h>
char s0[]="hello";
void a(){
    char *s1="hello";
    printf("%0x\n",s1);
    s1="world";
}
void b(){
    char *s2="hello";
    printf("%0x\n",s2);
    s2="world";
}
int main(){
    char *s3=(char *)0;
    printf("%0x\n",s0);
    a();
    b();
    s3="hello";
    printf("%0x\n",s3);
    return 0;
}

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U8NPnT08-1672642934017)(D:\Typora图片\image-20230101221124047.png)]

s0与s1 、s2、 s3指向的地址不同。

分析:

造成这样的结果的原因是因为char s0[]="hello",而s0指向的是该数组的首地址,而不是字符串的首地址。

s1、 s2、 s3都是指向字符串的首地址,因此不同。

#include <stdio.h>

int main(int argc, char *argv[])
{
    printf("Hello C-Free!\n");
    
    //定义一个数组变量,用字符串常量初始化其值 。 
    char a[] = "123";          
    //定义一个字符指针,再定义一个字符串常量,指针指向的常量首地址 
    const char* b = "321";    
    const char* c = "321";    
    //打印变量在内存里的地址,栈区【高地址->低地址】 
    printf("%d,%d,%d\n",&a, &b, &c);//6356772,6356768,6356764
    //打印指针的首地址,b和c相同因为都指向同一常量区 
    printf("%d,%d,%d\n",a, b, c);    //6356772,4206607,4206607
    //打印内容 
    printf("%s,%s,%s\n",a, b, c);    //123,321,321

    //从b内存拷贝3个字节给a变量,正常 
    memcpy(a, b, 3); 
    //从a内存拷贝3个字节给a常量指针,编译失败或运行失败,常量不可修改 
    memcpy(b, a, 3); 

    printf("%s,%s,%s\n",a, b, c);    //123,321,321
    return 0;
}

5.字符串常量定义

定义:用双引号(“”)括起来的0个或者多个字符组成的序列。

存储:每个字符串尾自动加一个 ‘\0’ 作为字符串结束标志。

字符串常量在内存的常量存储区是按顺序存储的,如:

char* a = "123";
char* b = "456";
char* c = "456";

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eYbwxDPW-1672642934018)(D:\Typora图片\990943-20210713165252108-1379805600.png)]

定义a时,存储一个“123\0”;

定义b时,判断存储区是否有"456\0",发现没有则在后面追加"456\0";

定义c时,判断存储区是否有"456\0",发现有,则不再存储,此时b和c两个指针存储的都是"456\0"这片内存地址

既然是常量,那么不可被修改,所以memcpy(b, a, 3);是错误的。

6.字符数组

char a[] = "123";

首先声明一个字符数组a,大小没有确定,但是将一个字符串常量“123\0”赋值给了a,故a的length就是4个字节。【注意“123\0”并没有存储在常量区】

注意a归根结底是一个数组,数组是一个变量,不是指针,虽然可把a当做一个指针,因为它指向数组的首地址,但归根结底不是指针。

正因为a是变量,所以a能够修改其存储的值。

比如:

int main(){
    char a[]="123";
    a[0]='a';
    printf("%s\n",a);
}

输出结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sRbtmf46-1672642934018)(D:\Typora图片\image-20230101193318240.png)]

7.字符指针

const char* b = "321";

既然名字是字符指针,那么它一定是个指针,指针存储地址。

故解读这句就是:

首先声明一个字符指针b,然后定义一个字符串常量"321\0"。

字符串常量存储在常量区,b存储在栈区,b存储的值是字符串常量"321\0"的地址。

这里用到了const,在C语言里不加const也行,C++里不加会有个警告,但不影响编译。但是建议加上const,能够让程序员一眼就知道此指针指向的是常量,也就是最终内容无法修改。

虽然最终指向不能修改,但是指针自身的取值,是可以修改的,即可修改指针指向的地方。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Txr2Qw9f-1672642934018)(D:\Typora图片\990943-20210713173550898-1076816549.png)]

8.内存图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oy3LXGsc-1672642934019)(D:\Typora图片\990943-20210713174838404-1399769579.png)]

所有存储类型代码区、常量区、静态区(全局区)、堆区、栈区,只有栈是从高地址往低地址存储,其他都是低地址往高地址存储。

9.补充

来分析几段代码。

代码:test0.c

#include <stdio.h>
#include <stdlib.h>
char* toStr() 
{
    char *s = "hello word!";
    return s;
}
int main(void) 
{
    printf("%s\n", toStr());
}

运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SmvEyuWA-1672642934019)(D:\Typora图片\image-20230101223619968.png)]

运行没有任何问题,因为 “hello world” 是一个字符串常量,存放在文字常量区,把该字符串常量存放的地址赋值给了指针 s,toStr 函数退出时,该该字符串常量所在内存不会被回收,故能够通过指针顺利无误的访问。

代码:test1.c

#include <stdio.h>
#include <stdlib.h>
char* toStr()
{
    char s[] = "hello word!";
    return s;
}
int main(void)
{
    printf("%s\n", toStr());
}

编译运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5gGkLUoq-1672642934019)(D:\Typora图片\20210108200224964.png)]

调用 toStr 函数,定义了一个局部变量( char [] 型数组),该局部变量存放在栈中,当 toStr 函数退出时,栈要清空,局部变量的内存也被清空了,所以这时的函数返回的是一个已被释放的内存地址,出现段错误。

如果函数的返回值非要是一个局部变量的地址,那么该局部变量一定要申明为static类型

代码:test2.c

#include <stdio.h>
char *returnStr()
{
    static char p[]="hello world!";
    return p;
}
int main()
{
    char *str=NULL;
    str=returnStr();
    printf("%s\n", str);                                                                          
    return 0;
}

运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GveRoy2j-1672642934020)(D:\Typora图片\image-20230101223914993.png)]

综合性代码:

#include <stdio.h>

//返回的是局部变量的地址,该地址位于动态数据区,栈里                                                                             
char *s1()
{
    char p[] = "Hello world!";

    printf("in s1 p=%p\n", p);
    printf("in s1: string's address: %p\n", &("Hello world!"));

    return p;
}

//返回的是字符串常量的地址,该地址位于静态数据区
char *s2()
{
    char *q = "Hello world!";

    printf("in s2 q=%p\n", q);
    printf("in s2: string's address: %p\n", &("Hello world!"));

    return q;
}

//返回的是静态局部变量的地址,该地址位于静态数据区
char *s3()
{
    static char r[] = "Hello world!";

    printf("in s3 r=%p\n", r);
    printf("in s3: string's address: %p\n", &("Hello world!"));
    return r;
}
int main()
{
    char *t1, *t2, *t3;

    t1 = s1();
    t2 = s2();
    t3 = s3();

    printf("\nin main:\n");
    printf("p = %p\nq = %p\nr = %p\n", t1, t2, t3);

//  printf("%s\n", t1);        /* 运行会出现段错误,先屏蔽 */
    printf("%s\n", t2);
    printf("%s\n", t3);
                                                                                                                                                   
    return 0;
}

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fSsNT4Yu-1672642934020)(D:\Typora图片\image-20230101223952538.png)]

根据运行结果我们就很清楚了,这里不再赘述。


:bookmark: 部分参考文章:

(37条消息) 关于字符串常量在内存中的生命周期_w_16822的博客-CSDN博客

(39条消息) 【C语言入门】笔记十 (指针中)_Liues233的博客-CSDN博客_假设字符数组sa为一个英文字符串,设计程序,当用户用键盘输入字符串sa后,将

(40条消息) C语言—内存的管理和释放_^^不加糖^^的博客-CSDN博客_c语言释放内存

请添加图片描述

相关文章
|
7月前
|
存储 C++
c++字符和不常见常量
c++字符和不常见常量
60 0
0 和 1 的字面量
Julia 中,数据类型涵盖整数和浮点数,字面量表示固定值。`zero(x)` 和 `one(x)` 函数返回x类型对应的0和1,减少类型转换成本。例如: ```markdown julia&gt; zero(Float32) # Float32的0 0.0f0 julia&gt; one(Int32) # Int32的1 1 ```
|
3月前
|
JavaScript 前端开发
字面量
【9月更文挑战第04天】
46 2
|
4月前
|
C++
【学习笔记】【C/C++】 c++字面值常量
【学习笔记】【C/C++】 c++字面值常量
46 1
|
7月前
|
存储 Java 编译器
字符串常量和字符串字面值
字符串常量和字符串字面值
145 1
|
7月前
|
存储 C语言
字符指针变量与字符数组的比较
字符指针变量与字符数组的比较
57 3
|
7月前
|
存储 Java 编译器
字符串常量和字符串字面值:区别与联系
字符串常量和字符串字面值:区别与联系
|
7月前
|
C语言
C语言常量与变量、字符串与转义字符
C语言常量与变量、字符串与转义字符
55 0
|
7月前
|
存储 编译器 程序员
C++中的宏定义和字符串字面值的区别
C++中的宏定义和字符串字面值的区别
|
7月前
|
存储 程序员 C++
使用字符指针变量和字符数组的比较
使用字符指针变量和字符数组的比较
77 1