C&C++ 字符串与指针

本篇博客是对educoder相关实训知识的汇总


前言

        指针是 C 语言中的一个重要概念,也是 C 语言的一个重要特色。在 C 语言中,指针被广泛使用,它和数组、字符串、函数间数据的传递等有着密不可分的联系。可以说,没有掌握指针就没有掌握 C 语言的精华。

        指针可以对内存中各种类型的数据进行快速、直接的处理,也可以为函数间的数据传递提供简洁、便利的方法。正确熟练地使用指针有利于编写高效的程序,但指针使用不当也容易导致严重错误。指针很灵活,也很危险。


一、定义指针变量

        指针变量(简称指针)就是存放地址的变量。其声明形式与一般变量声明相比只是在变量名前多一个星号*,接下来看两个例子。

1

int *p;

        该例中声明了变量 p 为指向整型值的指针(即变量 p 中可以存放某个整型变量的地址)。这里的*在声明语句中,是指针说明符,表示声明的变量是指针变量。

2

float *xPtr, *yPtr, f;

        该例子中声明了两个指向浮点型值的指针 xPtr 和 yPtr 以及一个浮点型变量 f 。


 二、指针的性质

        ① 指针可以赋值为 NULL 或某个地址。具有值 NULL 的指针不指向任何地址;

        ② 指针是具有特定属性的地址。光有地址只是知道数据存储在内存的某个位置,但怎么访问该位置的数据(即访问多少位,以什么方式访问)还需要指针类型来明确;

        例如:使用 int 类型的指针访问其所指向的数据时,会一次性读取32位( int 类型的数据是32位),并使用整型数据的格式访问该数据。所以指针不仅仅是一个地址,还必须有特定的属性(类型)。

        ③ 数组名可以看成是一个特殊的地址,首先数组名是地址(数组的首地址),其次数组名有属性(数组元素的类型),所以可以把数组名赋值给同类型的指针变量。

例如:

char s[10] = "China";
char *sptr = s;

第二条语句赋值后 sptr 拥有和 s 同样的值,即数组的首地址,也就是存储字符‘C’的单元的地址。

        ④ 要访问指针所指向的单元可以使用间接引用运算符*(不同于前面声明语句中的*,这里的*在表达式中,是运算符),*也被称为复引用运算符,它返回其操作数(指针)所指向的对象;

例如:

char s[10] = "China";
char *sptr = s;
cout << *sptr;

将输出指针 sptr 所指的单元中存储的字符(因为 sptr 是 char 类型的指针),也就是输出字符‘C’。

        ⑤ 可以通过指针的复引用修改指针所指向的单元

例如:

char s[10] = "China";
char *sptr = s;
*sptr = 'c';

上述代码会将该存储单元中大写字符的‘C’修改为小写字符的‘c’。

请注意前面代码的输出语句和下面的代码语句的区别:

char s[10] = "China";
char *sptr = s;
cout << sptr;

        这条语句将输出字符串“China”。之前学习字符数组时应该知道,语句cout << s;会输出数组 s 中存储的整个字符串,实际上 C++ 在使用 cout 输出 char 类型指针时,不是输出字符指针的值(地址),而是输出从该地址开始的字符串(逐个输出一个个字符,直到碰到 '\0' 为止)。所以cout << sptr;cout << s;的作用一样,都是输出字符串“China”。

        ⑥ 访问一个字符串一般也是使用该字符串的首字符的地址

        ⑦ 指针也可以参与算术运算。指针加上或减去一个整数 n,其运算结果是指向指针当前指向变量的后方或前方的第 n 个变量。

例如:

之前 sptr 指向字符‘C’的存储单元,执行语句sptr++;后 sptr 则指向字符‘h’的存储单元。如下语句:

while(*sptr != '\0') sptr++;

则可以使指针 sptr 指向该字符串后面的 '\0'。

如果要输出字符串中的部分内容,也可以通过修改指针实现,如:

char s[10] = "China"; char *sptr = s; sptr++; cout << sptr;

上述代码执行语句sptr++;后,指针 sptr 指向了字符 'h' 的存储单元,此时cout << sptr;输出的是 sptr 指向的字符串,即“hina”。

        ⑧ 同类型的两个指针可以参与各种关系运算,其结果可以反映两指针所指向的地址之间的位置前后关系。

例如:

int a[10]; int *p = a, *q = &a[1]; if(p > q) cout << "p>q" << endl; else cout << "p<=q" << endl;

上述代码中指针 p 中存放的是 a 的值,也就是 a[0] 的地址,q 中存放的是 a[1] 的地址,而数组元素是按序连续存储的,所以 q 的值要比 p 的值大,程序输出p<=q


 三、用指针传递参数

        C 和 C++ 函数调用的参数传递方式有两种:传值和传引用。

  • 传值只是值的传递,被调用函数则无法修改实在参数的值;
  • 传引用则是实参和形参共享实在参数的存储单元,所以被调用函数可以通过修改形参来修改实参的值。

        如果采用传值的方式传递指针值,可以实现类似于传引用的效果。

例如:

#include 
using namespace std;

// 函数inc:将p指向的整数值加
// 参数:p-int类型指针,指向要加的整数
void inc(int * p)
{
    (*p)++;     // *p 访问 p 指向的单元,++ 将该单元的数据加
    // 注意不能是 *p++, 因为 * 和 ++ 优先级相同,且右结合,这种写法修改的是 p 的值,而不是 *p 的值
}

int main()
{
    int a = 10;
    inc(&a);     // 调用 inc 函数,修改 a 的值(传递的是 a 的地址)
    cout << a << endl;     // 输出 a 的值
    return 0;
}

上述程序的输出为11,其中被调用函数 inc 只修改了 main 函数中的局部变量 a 的值,但并没有修改实参的值(实参是&a,即 a 的地址依然没变)。


四、字符串操作函数 

        C 和 C++ 提供了一系列操作字符串的函数,要使用这些函数只要在代码的头文件部分包含 string.h 即可。

常用的字符串处理函数见下表:

函数原型

函数功能

char * strcpy(char *dest,const char *src)

将字符串 src 复制到 dest

char * strcat(char *dest,const char *src)

将字符串 src 添加到 dest 末尾

char * strchr(const char *s,int c)

检索并返回字符 c 在字符串 s 中第一次出现的位置

int strcmp(const char *s1,const char *s2)

比较字符串 s1 与 s2 的大小,若 s1 串大于 s2 串则返回一个大于 0 的值;若 s1 串等于 s2 串则返回值为 0;若 s1 串小于 s2 串则返回一个小于 0 的值。

size_t strlen(const char *s)

返回字符串 s 的长度

char * strncat(char *dest,const char *src,size_t n)

将字符串 src 中最多 n 个字符复制到字符串 dest 中

int strncmp(const char *s1,const char *s2,size_t n)

比较字符串 s1 与 s2 中前 n 个字符

char * strncpy(char *dest,const char *src,zise_t n)

复制 src 中的前 n 个字符到 dest 中

char * strstr(const char *s1,const char *s2)

扫描字符串 s1,并返回第一次出现 s2 的位置

char * strtok(char *s1,const char *s2)

检索字符串 s1,该字符串 s1 是由字符串 s2 中定义的定界符所分隔

C&C++ 字符串与指针_第1张图片

strstr 函数

        在一个长串中查找子串可以使用strstr函数,该函数的函数原型为:

char* strstr(const char* s1, const char* s2);

该函数从 s1 所指字符串中第一个字符起,顺序向后查找出与 s2 所指字符串相同的子串,若查找成功则返回该子串首次出现的首地址,否则返回 NULL。

例如:

char *a="abcdeabcde";
char *b="bcd";
cout<

该程序输出结果为“bcdeabcde”,因为strstr(a, b)的返回值为“bcd”在“abcdeabcde”中第一次出现的首地址,所以用 cout 输出时,从该位置的字符开始,逐个输出直到 '\0',即字符串“bcdeabcde”。

        当然,查找子串时,也可以从长串的某个位置开始。

例如:

char *a="abcdeabcde";
char *b="bcd";
cout<

该程序的输出为“bcde”。因为a+4得到一个新地址,即 a 指向的字符串中第一个字符‘e’的地址,从该位置开始查找 b 指向的字符串‘bcd’,得到从字符‘e’开始的第一个“bcd”出现的地址,然后用 cout 输出该地址开始的字符串,即“bcde”。

        值得注意的是,在实际运用该函数过程中,总是出现错误,几经debug,最终明确了其const char* 的重要意义:在调用该函数时(尤其实在循环中调用),未结束对该函数的调用之前,不能改变其参数的值!包括下面的strlen函数在内,存在const限定的函数调用,都应该避免此情形。

strlen 函数

另外,下次从什么地方开始查找子串?应该是上次找到子串的开始位置加上子串的长度。其中,函数strlen可以计算字符串的长度,其函数原型为:

int strlen(const char *s);

函数 strlen 只有一个参数 s,它是一个字符指针,代表了一个字符串,函数计算 s 指向字符串的长度并返回。

例如:

char *a="x";
char *b="Hello world!";
cout<

substr函数

用于截取子字符串。

        形式 : s.substr(pos, len)
        返回值: string,包含s中从pos开始的len个字符的拷贝(pos的默认值是0,len的默认值是s.size() - pos,即不加参数会默认拷贝整个s)
        异常 :若pos的值超过了string的大小,则substr函数会抛出一个out_of_range异常;若pos+n的值超过了string的大小,则substr会调整n的值,只拷贝到string的末尾


 总结

        指针用的好可以让程序清晰高效,用的差晦涩冗杂,正如开篇语所言:指针很危险,也很灵活。而没有掌握指针,也就没有掌握 C 语言的精华。

substr函数部分借鉴于CSDN博主「哦啦哦啦!」的原创文章,于此处声明。

你可能感兴趣的:(C++程序设计,c++)