了解声明和初始化字符串的不同方法,研究C程序中字面量池的使用及其影响。此外,我们还会了解比较、复制和拼接字符串等常见字符串操作。
字符串通常以字符指针的形式传递给函数和从函数返回。我们可以用字符指针传递字符串,也可以用字符常量的指针传递字符串,后者可以避免字符串被函数修改。
我们也可以从函数返回字符串,从而满足某个请求。可以将这个字符串从外面传给函数并由函数修改,也可以在函数内部分配,还可以返回静态分配的字符串。
指针是保存字符串地址的。我们可以使用地址索引和接引数据的。
字符串是以ASCII字符NUL结尾的字符序列。ASCII字符NUL表示为 \0。字符串通常存储在数组或者从堆上分配的内存中。不过,并非所有的字符数组都是字符串,字符数组可能没有NUL字符。字符数组也用来表示布尔值等小的整数单元,以节省内存空间。C语言中有两种字符串
声明字符串的方式有三种:字面量、字符数组 和 字符指针。字符串字面量是用双引号引起来的字符序列,常用来进行初始化,它们位于字符串字面量池中。
声明例子如下:
//栈区字符串
char header[32];
//保存字符串的指针
char *header;
字符串字面量一般分配在只读内存中 (但最好显示声明字符串不可修改),所以是不可变的。字符串字面量在哪里使用,或者它是全局、静态或局部的都无关紧要,从这个角度讲,字符串字面量不存在作用域的概念。
const char*tabHeader="Media Player";
*tabHeader='L'; //会报错,不能休该
//第一种:直接字符字面量赋值
char header[]="Media Player";
//第二种:使用字符操作函数
char header[13];
strcpy(header,"Media Player");
//第三种:逐个赋值
header[0]='M';
header[1]='e';
header[12]='\0';
在使用malloc函数分配空间后,这个内存空间不会主动设置字符串结尾的标志 都是要手动设置的。
//使用malloc函数分配空间
char *header=(char*)malloc(strlen("Media Player")+1);
//第一种:直接字符字面量赋值
char*header="Media Player";
//第二种:使用字符拷贝函数和字面量赋值
strcpy(header,"Media Player");
//第三种:逐个赋值
*(header+0)='M';
*(header+1)='e';
.....
*(header+12)='\0';
其实就是从文件、用户输入等方面获取的字符串。初始化和前面两种差不多,只是来源不一样,上面是程序写死的,这个是开放的。
//从控制台输入 命令
#define StringLength 100
char*command = (char*)malloc(sizeof(char) * StringLength +1);;
printf("Enter a Command:");
scanf("%s",command);
下面是一个字符字面量、数组和指针的调用例子。
char*globalHeader="Chapter";
char globalArrayHeader[]="Chapter";
void displayHeader()
{
static char* staticHeader="Chapter";
char* localHeader="Chapter";
static char staticArrayHeader[]="Chapter";
char localArrayHeader [ ] = "Chapter" ;
char*heapHeader=(char*)malloc(strlen("Chapter")+1);
strcpy(heapHeader,"Chapter");
}
下面几个函数都定义于头文件
下面情况要注意:
//定义
int strcmp( const char *s1, const char *s2);
原理:
相互比较的方法是被比较的字符串中首对不同字符(都转译成 unsigned char )的值间的差的符号。
返回值:
实例代码:
#include
#include
void demo(const char* lhs, const char* rhs)
{
int rc = strcmp(lhs, rhs);
const char *rel = rc < 0 ? "precedes" : rc > 0 ? "follows" : "equals";
printf("[%s] %s [%s]\n", lhs, rel, rhs);
}
int main(void)
{
const char* string = "Hello World!";
demo(string, "Hello!");
demo(string, "Hello");
demo(string, "Hello there");
demo("Hello, everybody!" + 12, "Hello, somebody!" + 11);
}
输出:
[Hello World!] precedes [Hello!]
[Hello World!] follows [Hello]
[Hello World!] precedes [Hello there]
[body!] equals [body!]
char* strcpy(char*s1,const char*s2);
复制 s2 所指向的空终止字节字符串,包含空终止符,到首元素为 s1 所指的字符数组。
注意要点:
char *strcat(char*s1,const char*s2);
此函数把第二个字符串拼接到第一个的结尾,第二个字符串是以常量char指针的形式传递的。
注意要点:
size_t stringLength(char*string)
{
size_t length=0;
while(*(string++))
{
length++;
return length;
}
}
char simpleArray[]="simple string";
char*simplePtr=(char*)malloc(strlen("simple string")+1);
strcpy(simplePtr,"simple string");
//调用的方式
printf("%d\n",stringLength(simplePtr));
printf("%d\n",stringLength(simpleArray));
printf("%d\n",stringLength(&simpleArray));
printf("%d\n",stringLength(&simpleArray[0]));
传递字符常量就是为了安全;防止程序非法修改字符串
就像字符串函数 strcat;
char *strcat(char*s1,const char*s2);
char* strcpy(char*s1,const char*s2);
这个s2就是源字符串;s1就是目的字符串
标准库里面为了安全就是 使用 const 来防止源字符串被修改
有些情况下我们想让函数返回一个由该函数初始化的字符串。假设我们想传递一个
部件的信息,比如名字和数量,然后让函数返回表示这个信息的格式化字符串。通
过把格式化处理放在函数内部,我们可以在程序的不同部分重用这个函数。
不过,我们得决定是给函数传递一个空缓冲区让它填充并返回,还是让函数动态分
配缓冲区并返回。
要传递缓冲区,需要一些条件:
main函数通常是应用程序第一个执行的函数。对基于命令行的程序来说,通过为其
传递信息来打开某种行为的开关或控制某种行为很常见。可以用这些参数来指定要
处理的文件或是配置应用程序的输出。比如说,Linux的 ls 命令会基于接收到的参
数列出当前目录下的文件。
C用传统的argc和argv参数支持命令行参数。第一个参数argc,是一个指定传递
的参数数量的整数。系统至少会传递一个参数,这个参数是可执行文件的名字。第二
个参数argv,通常被看做字符串指针的一维数组,每个指针引用一个命令行参数。
下面的main函数只是简单地列出了它的参数,每行一个。在这个版本中,argv被
声明为一个char指针的指针。
int main(int argc, char** argv)
{
for(int i=0;i<argc;i++)
{
printf("argv[%d]%s\n",i,argv[i]);
}
}
程序可以用下面的命令执行:
process.exe-f names.txt limit=12 -verbose
输出如下:
argv[0] c:/process.exe
argv[1] -f
argv[2] names.txt
argv[3] limit=12
argv[4] -verbose
函数返回字符串时,它返回的实际是字符串的地址。这里应该关注的主要问题是如
何返回合法的地址,要做到这一点,可以返回以下三种对象之一的引用:
这个很好理解,举例下面的写法,就是返回的字面量
char* returnALiteral(int code)
{
switch(code)
{
case 100:
return"Boston Processing Center";
case 200:
return"Denver Processing Center";
case 300:
return "Atlanta Processing Center";
case 400:
return"San Jose Processing Center";
}
}
如果需要从函数返回字符串,我们可以在堆上分配字符串的内存然后返回其地址。
如下代码
char*blanks(int number)
{
char*spaces=(char*)malloc(number+1);
int i;
for(i=0;i<number;i++)
{
spaces[i] =' ';
}
spaces[number]='\0';
return spaces;
}
//调用
char*tmp=blanks(5);
程序内存示意图:
释放返回的内存是函数调用者的责任,如果不再需要内存但没有将其释放会造成内存泄漏。下面是一个内存泄漏的例子,printf函数中使用了字符串,但是接着它的地址就丢失了,因为我们没有保存:
printf("[%s]\n",blanks(5));
一个更安全的方法如下所示:
//这里的tmp就保存这个地址,可能后面还需要操作
char*tmp=blanks(5);
printf("[%s]\n",tmp);
//其他操作.........
free(tmp);
注意要点: