声明字符串有三种方式,字面量
、字符数组
和字符指针
字符串字面量
是用双引号引起来的字符序列,常用来进行初始化,它们位于字符串字面量池
中
字符数组
char header[32] = "last week";
字符指针
const char* str = "last week";
定义字面量时通常会将其分配在字面量池中,这个内存区域保存了组成字符串的字符序列
多次用到同一个字面量时,字面量池中通常只有一份副本。这样会减少应用程序占用的内存。(通常认为字面量是不会变得,所以只有一份副本)
可以关闭字面量池,这样字面量就可以生成多份副本,每个副本都有自己的地址
字符串字面量一般分配在只读内存
中,所以是不变的。
const char* str = "last week";
我们希望将字符串字面量看成常量,加入const避免修改字面量
(有些编译器可以通过指针修改,但像是visiual studio就不行,必须声明const类型的才可以正常赋值)
初始化字符串采用的方法取决于变量是被声明为字符数组
还是字符指针
字符串所用的内存要么是数组要么是指针指向的一块内存
char header[] = "Media Player";
printf("%s", header);
Media Player
字面量“Media Player”的长度为12个字符,则表示字面量需要13个字节
初始化将字符复制到数组中,以NUL结尾(ASCII字符中的’\0’
被称为NUL)
char* header = (char*)malloc(strlen("Media Player") + 1);
strcpy(header, "Media Player");
注意:
sizeof
操作符,sizeof
操作符会返回数组和指针的长度而不是字符串的长度const char* globalHeader = "Chapter";//全局指针 指向字符串字面量池
char golbalArrayHeader[] = "Chapter";//全局数组 拷贝字符串字面量到全局数组
void displayHeader() {
static const char* staticHeader = "Chapter";//静态指针 在全局内存指向字符串字面量池
const char* localHeader = "Chapter";//局部指针 在程序栈内指向字符串字面量池
static char staticArrayHeader[] = "Chapter";//静态数组 拷贝字符串字面量到全局数组
char localArrayHeader[] = "Chapter";//局部数组 拷贝 字符串字面量到程序栈的数组
char* heapHeader = (char*)malloc(strlen("Chapter") + 1);//堆区动态分配内存 在堆区开辟空间并返回指针给heapHeader
strcpy(heapHeader, "Chapter");//往堆区拷贝字符串字面量
}
strcmp
函数原型
int strcmp(const char* s1, const char* s2);
按照字典序作比较
strcpy
函数原型
char* strcpy(char* s1, const char* s2);
int n = 2;
char name[32];
char* names[30];
size_t count = 0;
while (n--) {
printf("Enter a name\n");
gets_s(name,20);
names[count] = (char*)malloc(strlen(name) + 1);
strcpy(names[count], name);
count++;
}
for (int i = 0; i < 2; i++) {
printf("%s\n", names[i]);
}
定义了一个指针数组
,为每个指针按照输入字符的大小动态分配内存,利用strcpy
拷贝字符串的地址
strcat
函数原型
char* strcat(char* s1, const char* s2);
const char* error = "ERROR";
const char* errorMessage = "Not enough memory";
char* buffer = (char*)malloc(strlen(error) + strlen(errorMessage) + 1);//动态分配两个字符串都可以容纳的内存
strcpy(buffer, error);//先将buffer拷贝过去
strcat(buffer, errorMessage);//再将errorMessage拼接过来
buffer相当于缓冲区
,确保分配的内存够用
倘若不为拼接后的字符串分配独立的内存,就可能会覆写第一个字符串,比如下面一个没有用缓冲区的例子
char* error = "ERROR";
const char* errorMessage = "Not enough memory";
strcat(error, errorMessage);//再将errorMessage拼接过来
未分配缓冲区,直接连接
函数返回字符串时,返回的实际上是字符串的地址
,如何做到返回合法的地址,要做到这一点,可以返回以下三种对象的引用
const 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 Jost Processing Center";
}
}
char* blanks() {
char* spaces = (char*)malloc(strlen("last week") + 1);
strcpy(spaces, "Last week");
return spaces;
}
int main() {
char* temp = blanks();
printf("%s\n", temp);
free(temp);//记得释放堆区内存
return 0;
}
下一次函数调用会覆写
这块内存区域,解引用指针得到的内容可能已经改变
实现自定义的sor
t,可以根据函数来判断排序的依据
//比较字符串字典序
int compare(const char* s1, const char* s2) {
return strcmp(s1, s2);
}
//转换字符串为小写
char* stringToLower(const char* string) {
char* tmp = (char*)malloc(strlen(string) + 1);
char* start = tmp;//记录tmp起点,方便返回初始地址
while(*string != 0) {
*tmp++ = tolower(*string++);
}
*tmp = 0;//最后一个字符为NUL
return start;
}
int compareIgnoreCase(const char* s1, const char* s2) {
char* t1 = stringToLower(s1);//转换小写
char* t2 = stringToLower(s2);//转换小写
int result = strcmp(t1, t2);
free(t1);//释放在stringToLower函数中开辟的堆区内存
free(t2);//释放在stringToLower函数中开辟的堆区内存
return result;
}
//声明函数指针类型
typedef int (fptrOperation)(const char*, const char*);
void sort(const char* array[], int size, fptrOperation operation) {
int swap = 1;
while (swap) {
swap = 0;
for (int i = 0; i < size - 1; i++) {
if (operation(array[i], array[i + 1]) > 0) {
swap = 1;
const char* tmp = array[i];//每个元素都是指针,交换的是字符串地址
array[i] = array[i + 1];
array[i + 1] = tmp;
}
}
}
}
void displayNames(const char* names[], int size) {
for (int i = 0; i < size; i++) {
printf("%s ", names[i]);
}
printf("\n");
}
int main() {
const char* names[] = {
"bob","Ted", "Carol","Alice","alice" };
sort(names, 5, compare);
sort(names, 5, compareIgnoreCase);
displayNames(names, 5);
return 0;
}