字符串与指针

本文介绍了C语言中字符串的两种表示方法:字符数组和字符指针,详细解释了字符串常量的存储方式及其与字符数组的区别。此外,还探讨了字符串指针作为函数参数的应用,以及如何通过指针实现字符串的复制。

字符串与指针

1、  字符串的表示形式

C语言中,我们可以用两种方式访问字符串

(1)       用字符数组存放一个字符串,然后输出该字符串。

main(){

   char string[]="I love China!";

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

}

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

main(){

   char *string="I love China!";

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

}

    在这里,我们没有定义字符数组,而是在程序中定义了一个字符指针变量string,用字符串常量"I love China!",对它进行初始化。C语言对字符串常量是按字符数组处理的,在内存中开辟了一个字符数组用来才存放该字符串常量。对字符指针变量初始化,实际上是把字符串第1个元素的地址(即存放字符串的字符数组的首元素地址)赋给string有人认为string是一个字符串变量,以为在定义时把"I love China!"这几个字符赋给该字符串变量,这是不对的!!

      实际上,char *string="I love China!"; 等价于:

Char  *string

String = “I love China!”;

    可以看到,string被定义为一个指针变量,指向字符型数据,请注意它只是指向了一个字符变量或其他字符类型数据,不能同时指向多个字符数据,更不是把“I love China!”这些字符存放到string中(指针变量只能存放地址),也不是把字符串赋给*string只是把“I love China!”的第一个字符的地址赋给指针变量string

在输出时,要用:printf(“%s\n”, string);,其中“%s”是输出字符串时所用的格式符,在输出项中给出字符指针变量名,则系统先输出它所指向的一个字符数据,然后自动是string1,使之指向下一个字符,然后再输出一个字符……如此知道遇到字符串结束标志“\0”为止。

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

Int i[10];

………

Printf”%d\n”, i);

是不行的,只能逐个输出!!!!!!显然%s可以对一个字符串进行整体的输入和输出。

 

2、  字符串指针作函数参数

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

 

注意:对于实参和形参的用法,十分灵活,我们可以慢慢去熟悉它,这里列出一个表格便于大家记忆:

实参

形参

数组名

数组名

数组名

字符指针变量

字符指针变量

字符指针变量

字符指针变量

数组名

 


字符'/0'和'0'的区别

字符’0’对应的ASCII码为十六进制30;而’/0’即为ASCII码中的0,其对应字符空字符NUL。

     char c = '/0';çèchar c = 0 // NUL

     char c = '0';çèchar c = 48;

最典型如memset函数:void *memset( void *buffer, int ch, size_t count );

将一段长为count字节的内存块初始化为ASCII码值0,字符为NUL:

memset(pBuffer, '/0', sizeof(pBuffer) );çèmemset(pBuffer, 0, sizeof(pBuffer) );

 

字符串

字符串常量是双引号括起的任意字符序列。

例如:"Hello World","Fantasy","Please enter your full name:",……

C语言没有专门定义字符串数据类型(如其他语言中的string) ,所谓的字符串,只是对字符数组的一种特殊应用而已,它用以'/0'结尾的字符数组来表示一个逻辑意义上的字符串。

在字符串常量中,显然不能直接写双引号,因为这将被认为是字符串的结束。转义序列在字符串常量中要包含双引号,需要用“/"”表示。如:"Hello /"Accp/""

与字符数组不同的是:在存完字符串常量的所有字符之后,还要另存一个空字符'/0'作为结束的标志,空字符是ASCII码值为0的字符,C语言中用'/0'标识字符串的结束,所以也称为结束符。如果在程序里写了字符串:char hello[]="HELLO"或{"HELLO"};虽然只有5个字符,在内存中却需要占用6个字节存储,其中'/0'表示空字符。存储情况如:

H

E

L

L

O

/0

5005

5006

5007   

5008

5009

500A

而字符数组char hello[]={'H','E','L','L','O'};的长度和大小则为5.

根据字符串存储形式的规定,只要在数组里顺序存入所需字符,随后存一个空字符,这个字符数组里的数据就有了字符串的表现形式,这个数组也就可以当作字符串使用了。分析char str[]="abc";因为定义的是一个字符数组,所以就相当于定义了一些空间来存放"abc",而又因为字符数组就是把字符一个一个地存放的,所以编译器把这个语句解析为char str[3] = {'a','b','c'};根据上面的总结,所以charstr[]="abc";的最终结果是char str[4] = {'a','b','c','/0'};即char str[] = {'a','b','c','/0'}<==> char str[]="abc"

值得注意的是只有在程序中对字符串进行处理时,才考虑字符串结束标志的问题。考虑字符数组char a[4]={'B','O','Y','/0'};a数组中存放的是字符串“BOY”。如果执行语句a[1]= '/0';后,a数组的内容为'B','/0','Y','/0'此时系统认为a数组中存放了一个字符串“B”,printf("a=%s/n",a);输出的结果是a=B; printf("length a=%d/n",strlen(a))输出的结果是length a=1,而printf("sizeof a=%d/n",sizeof(a));输出结果为4.不要认为数组中只有'B''/0'了,只不过按系统对字符串的处理方式来看就是这样的。

char s3[10]="HELLO";//s3是编译期大小已经固定的数组

int a=strlen(s3); //a=5;//strlen()在运行起确定

int b=sizeof(s3); //b=10;//sizeof()在编译期确定

 

sizeof(s)

strlen(s)

char *s="HELLO";

4

char s[]="HELLO";

6

5

char s[10]="HELLO";

10

5

注意printf("hello is %s/n",hello);和cout<<hello;以及头文件<string.h>中的库函数只能操作以'/0'结尾的字符串,故对char hello[]={'H','E','L','L','O'};操作无效。

以下为四种hello字符串的定义:

char hello1[]="HELLO";

char hello2[]={"HELLO"}; //等同于hello1

char hello3[]={'H','E','L','L','O','/0'};//等价于hello1

char *hello4="HELLO";//区别:hello1是字符数组,hello4是字符指针。

用strlen函数测试hello1, hello2, hello3, hello4的结果均为5,因为不计末尾结束符’/0’。常用hello1或hello4来定义字符串。 

 

字符串与指针

C语言中许多字符串的操作都由指向字符数组的指针及指针的运算来实现的。因为对字符串来说一般都是严格顺序存取,使用指针可以打破这种存取方式,使字符串的处理更加灵活。

以下代码利用指针简洁的实现了字符串拷贝函数:

    void strcpy(s,t)//copy t to s

        char *s,*t;

    {

        while(*s++=*t++);

}

解析:*s++=*t++,先赋值*s=*t,然后指针后移t++,s++,当遇到*t= '/0'时,则拷贝操作完成。

char *pChar;    

char *text = "hello";

pChar = new char[strlen(text) + 1]; // 注意这里考虑终止字符!

cout << strcpy(pChartext) << endl// 不能用pChar = text;

cout << (strcmp(pChartext) == 0) << endl// 不能用pChar == text;

 

字符串常量

当一个字符串常量出现于表达式中时,它的值是个指针常量。编译器把这些指定字符的一份拷贝存储在内存的某个位置,并存储一个指向第一个字符的指针

    "xyz"+1;//这个表达式计算指针值加上的值,返回一个指针,指向字符串中的第二个字符'y'

    *"xyz";//返回第一个字符'x'

    "xyz"[2];//返回第三个字符'z'

*("xyz"+4);//偏移量超过了这个字符串的范围,返回一个不可预测的字符

那什么使用会用到上述表达式呢?我们考虑把二进制转换为字符并把它们打印出来。

     remainder = value % 16;//value对求余保存到remainder

     if(remainder < 10)//0~9

         putchar(remainder + '0');

     else////10~15

         putchar(remainder - 10 + 'A');

下面的代码用一种不同的方法解决这个问题:

 putchar("0123456789ABCDEF"[value % 16]);

<think>嗯,用户现在问的是关于C语言中字符串指针的关系。之前他们问过字符串是否是数组,现在可能想更深入理解字符串在内存中的表示,尤其是如何通过指针操作。我需要先回顾之前的回答,确保内容连贯。 首先,用户已经知道字符串本质是字符数组,以'\0'结尾。现在转向指针,得解释指针如何指向字符串,以及两者之间的关联和区别。可能的疑问点包括:指针如何存储字符串地址、字符串字面量的不可修改性、数组指针的不同之处。 需要分几个部分:内存中的存储形式、指针的定义和操作、数组指针的对比、常见错误。比如,用户可能混淆指针和数组的用法,或者试图修改字符串字面量导致错误。还要注意安全操作,如使用strdup或者动态分配来复制字符串。 另外,用户可能想知道为什么有时候用指针声明字符串,有时候用数组,以及它们在函数传递时的区别。需要举例子说明,比如char *p = "hello"和char arr[] = "hello"的不同内存区域。 还要提到标准库函数的使用,比如strcpy、strlen,这些函数如何处理指针参数。可能需要注意指针的运算,比如p++遍历字符串。 总结时,强调指针的灵活性和内存管理的注意事项,确保用户理解正确使用指针处理字符串的方法,避免常见错误。</think>好的!C语言中**字符串指针的关系**非常密切,但二者又有本质区别。下面从底层原理到实际应用进行系统讲解: --- ### 一、内存中的字符串存储形式 字符串在内存中有两种典型存在形式: 1. **字符串字面量(常量区)** ```c char *p = "Hello"; // "Hello"存储在只读数据段 ``` - 内容不可修改(尝试修改会导致未定义行为) - 生命周期程序相同 2. **字符数组(栈/堆)** ```c char arr[] = "World"; // 栈空间,可修改 char *heap_str = malloc(6); // 堆空间,需手动释放 strcpy(heap_str, "Heap"); ``` --- ### 二、指针操作字符串的核心原理 #### 1. 指针本质 - **指针变量存储的是字符串首字符的地址** - 示例: ```c char *str = "ABC"; // str指向'A'的地址 ``` $$ \begin{array}{|c|c|c|c|c|} \hline A & B & C & \backslash 0 & ... \\ \hline \uparrow \\ str \end{array} $$ #### 2. 通过指针访问字符串 ```c printf("%c", *str); // 输出A(解引用) printf("%s", str); // 输出ABC(自动遍历直到'\0') printf("%c", *(str+1)); // 输出B(指针运算) ``` #### 3. 指针遍历字符串的典型方式 ```c char *p = "Hello"; while(*p != '\0') { putchar(*p); p++; // 指针移动到下一个字符 } ``` --- ### 三、数组指针的关键区别 | 特性 | 字符数组 | 字符指针 | |---------------|---------------------------|---------------------------| | 内存位置 | 栈空间(局部变量) | 可能指向常量区/堆/栈 | | 可修改性 | 内容可修改 | 取决于指向的区域 | | 内存分配 | 编译时确定大小 | 动态分配需手动管理 | | sizeof结果 | 返回数组总字节数 | 返回指针本身大小(4/8字节)| | 赋值操作 | 不能直接整体赋值 | 可以改变指向目标 | 示例: ```c char arr[] = "Test"; char *ptr = "Test"; arr[0] = 't'; // 合法操作 ptr[0] = 't'; // 非法!试图修改常量区 ptr = arr; // 合法,改变指针指向 ``` --- ### 四、指针的典型应用场景 #### 1. 函数参数传递 ```c void print_str(const char *s) { // 通过指针高效传递 while(*s) { putchar(*s++); } } ``` #### 2. 动态字符串操作 ```c char *concat(const char *s1, const char *s2) { char *result = malloc(strlen(s1)+strlen(s2)+1); strcpy(result, s1); strcat(result, s2); return result; // 调用者需负责释放内存 } ``` #### 3. 字符串字面量的安全使用 ```c const char *messages[] = { // 指针数组存储多个字符串 "Error: File not found", "Warning: Memory low", "Info: Operation completed" }; ``` --- ### 五、常见错误规避方法 #### 1. 野指针问题 ```c char *dangerous; // 未初始化的指针 strcpy(dangerous, "test"); // 崩溃!指向随机地址 ``` **解决方法**:始终初始化指针,或使用`NULL`检查 #### 2. 修改常量字符串 ```c char *p = "CONSTANT"; *p = 'c'; // 段错误(核心已转储) ``` **正确做法**:声明为`const char*`类型 #### 3. 指针越界访问 ```c char *p = "Short"; printf("%c", p[10]); // 访问无效内存 ``` **防御手段**:使用`strlen`获取实际长度 --- ### 六、最佳实践建议 1. **优先使用`const`修饰只读指针**: ```c const char *readonly = "Important Data"; ``` 2. **安全复制字符串**: ```c char *safe_copy = strdup(original); // POSIX标准 // 记得后续使用free释放 ``` 3. **使用指针时的长度校验**: ```c if(strlen(src) >= dest_size) { // 处理缓冲区溢出风险 } ``` 4. **指针数组的混合使用**: ```c char buffer[100]; char *cursor = buffer; // 用指针操作数组 ``` --- ### 总结 **字符串指针的关系本质**: - 指针是操作字符串的"导航工具",通过地址访问内存中的字符序列 - 字符串的实际存储形式决定了指针操作的合法性 - 理解二者的区别联系是掌握C语言字符串处理的关键 掌握这些原理后,可以更安全高效地实现:字符串解析、动态内存管理、函数间数据传递等核心功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值