指针和字符串

指针和字符串

字符串声明

声明字符串有三种方式,字面量字符数组字符指针

字符串字面量是用双引号引起来的字符序列,常用来进行初始化,它们位于字符串字面量池

字符数组

char header[32] = "last week"; 

字符指针

const char* str = "last week";

字符串字面量池

定义字面量时通常会将其分配在字面量池中,这个内存区域保存了组成字符串的字符序列

多次用到同一个字面量时,字面量池中通常只有一份副本。这样会减少应用程序占用的内存。(通常认为字面量是不会变得,所以只有一份副本)

可以关闭字面量池,这样字面量就可以生成多份副本,每个副本都有自己的地址


字符串字面量一般分配在只读内存中,所以是不变的。

const char* str = "last week";

我们希望将字符串字面量看成常量,加入const避免修改字面量

(有些编译器可以通过指针修改,但像是visiual studio就不行,必须声明const类型的才可以正常赋值)

字符串初始化

初始化字符串采用的方法取决于变量是被声明为字符数组还是字符指针

字符串所用的内存要么是数组要么是指针指向的一块内存

初始化char数组

char header[] = "Media Player"; 
printf("%s", header);
Media Player

字面量“Media Player”的长度为12个字符,则表示字面量需要13个字节

初始化将字符复制到数组中,以NUL结尾(ASCII字符中的’\0’被称为NUL)

初始化char指针

char* header = (char*)malloc(strlen("Media Player") + 1);
strcpy(header, "Media Player");

注意:

  • 不要用sizeof操作符,sizeof操作符会返回数组和指针的长度而不是字符串的长度
  • strlen+1是加上NUL
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);

按照字典序作比较

  • s1 < s2 返回负数
  • s1 = s2 返回0
  • s1 > 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;
}

不要返回局部的字符串地址

下一次函数调用会覆写这块内存区域,解引用指针得到的内容可能已经改变

函数指针和字符串

实现自定义的sort,可以根据函数来判断排序的依据

//比较字符串字典序
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值